본문 바로가기

JAVA

Java - try-with-resources를 통한 자원해제방법

최근에 FileReader, BufferedReader를 공부하면서, 버퍼의 자원해제 관련하여 새로 알게된 내용이 있어 남겨둔다

 

= FileReaderTests.java

package com.ys.test.lab;

import com.ys.test.lab.common.File;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class FileReaderTests {
    File file;
    @BeforeEach
    public void load() {
        this.file = new File();
    }

    @Test
    public void getMemberTestOne() {
        List<String> files = file.getFileReader("src/main/resources/contents/members.csv");
        String[] memberInfo = memberArr(files.get(0));
        assertEquals("xggames", memberInfo[0]); // 회원ID
        assertEquals("백엔드개발자", memberInfo[1]); // 회원의 직업
    }

    private String[] memberArr(String memberStr) {
        return memberStr.split(",");
    }
}

= File.java (AS-IS)

package com.ys.test.lab.common;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

// 파일을 읽어들이는 Class
public class File {
    public List<String> getFileReader(String fileUrl) {
        FileReader fileReader;
        BufferedReader bufferedReader;
        try {
            fileReader = new FileReader(fileUrl);
            bufferedReader = new BufferedReader(fileReader);
            
            List<String> returnList = bufferedReader.lines().collect(Collectors.toList());
            fileReader.close();
            bufferedReader.close();
            return returnList;
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
        return new ArrayList<>();
    }
}

FileReaderTests.java의 테스트케이스에서 File.java에 보면 FileReader와 BufferedReader를 사용하고, 자원을 해제처리하였다. 그런데 만약 중간에 Exception이 발생하게되면 close()를 하지못하는 상황이 생기고, 메모리 누수 및 특정 프로그램의 독점으로 인해 객체가 올바르게 동작하지 않을 수도 있다. 물론 CG가 결국 메모리를 회수해가긴 하지만, 그 시점이 언제인지도 불투명하므로 가급적이면 자원의 해제처리가 필요하다.

 

이러한 문제를 java7부터 지원하는 Try-with-resources를 사용하여 자동으로 자원해제처리할 수 있다.

= File.java (TO-BE)

package com.ys.test.lab.common;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class File {
    public List<String> getFileReader(String fileUrl) {
        try(
            FileReader fileReader = new FileReader(fileUrl);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
                ) {
            return bufferedReader.lines().collect(Collectors.toList());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
        return new ArrayList<>();
    }
}

=> try문에 괄호를 열어 버퍼 객체생성 코드를 넣어준다. 자원의 사용이 끝나면, 자동으로 닫아주게 된다.

덤으로 코드도 깔끔해진다.

 

try-with-resources 사용조건

try문에 입력할 리소스 객체는 java.lang.AutoCloseable 인터페이스를 구현하고 있어야 한다는 조건이 필요하다. AutoCloseable에는 close() 메소드가 정의되어 있는데, try-with-resources는 이 close() 메소드를 자동으로 호출한다.

 

(위의 FileReader와 BufferedReader는 상위에 Reader 추상화 클래스를 상속받아 구현하는데, Reader에서 Closeable을 implements하고, Closeable 인터페이스가 AutoCloseable을 상속받기때문에, close()가 호출되며, 자원해제처리가 된다)

더 알아보기 (Java Oracle 공식문서)

 

The try-with-resources Statement (The Java™ Tutorials > Essential Java Classes > Exceptions)

The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available. See Java Language Changes for a summary of updated

docs.oracle.com

참고자료

= https://ryan-han.com/post/java/try_with_resources/