Post

try-with-resources 자원 해제 처리

리소스 처리와 try-catch-finally 구문, try-with-resources 구문의 차이, 그리고 리소스에 대한 예외 처리

예제에 사용된 소스 코드는 하단의 Repository 링크를 참고하면 된다

리소스

리소스 종류 및 파일 핸들러

java에서는 메모리, 파일, 네트워크 등 여러가지 리소스를 다룰 수 있다

리소스를 다루는데 가장 중요한 점은 사용하는 리소스에 대해서 맺고 끊음이 분명 해야 한다

파일 리소스에 접근하는 경우 OS에 설정에 따라서 하나의 프로세스에서 동시에 가질 수 있는 파일의 핸들, 즉 파일의 수가 정해져 있으며 그 이상의 파일을 열 수 없다

실행하는 java 어플리케이션이 실행 후 종료가 된다면 파일 핸들에 대한 오류가 발생하기 쉽지 않지만, 특정 주기로 종료 되지 않고 계속해서 실행되고 있다면 파일을 더 이상 열 수 없는 상황으로 서버의 가용성에 문제가 발생한다

파일 핸들의 수에 대한 설정 확인

1
2
3
4
5
ulimit -n
256

ulimit -u
2666

ulimit -n 명령어로 하나의 프로세스에서 가질 수 있는 파일 핸들의 수, ulimit -u 명령어로 하나의 사용자가 가질 수 있는 프로세스의 수를 구하여 하나의 사용자가 가질 수 있는 파일 핸들의 수를 구할 수 있다

리소스 처리

java에서는 리소스 다루는 class에서는 대부분 Closeable interface를 구현하고 있다

1
2
3
4
5
6
7
package java.io;

import java.io.IOException;

public interface Closeable extends AutoCloseable {
    public void close() throws IOException;
}

Closeable interface에서는 close 메서드를 정의하고 있으며, 파일 관련 리소스에서는 파일 핸들러를 반납하고, 네트워크 관련 리소스에서는 원격 서버에 대한 요청의 종료 처리를 한다

Closeable interface의 close 메서드에서는 IOException 예외를 처리하도록 한다

개발자는 리소스 처리의 마무리는 close 메서드의 예외 처리이다

예외 처리

java 어플리케이션에서 기본적인 예외 처리 구문으로는 try-catch-finally 구문이 있다

try 블록에서 비즈니스 로직을 처리하는 도중에 예외가 발생하는 경우에 catch 블록과 finally 블록에서 예외에 대한 대응과 예외 발생과 무관하게 반드시 실행이 필요한 작업을 처리 할 수 있다

java 1.7부터 try-with-resources구문을 사용해서 Closeable interface를 구현하고 있는 class에 대한 close 처리를 try-catch-finally 구문 보다 간편하게 할 수 있다

예제

Closeable 구현 class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.illdangag;

import java.io.Closeable;
import java.io.IOException;

public class ThrowClose implements Closeable {
    public void process(boolean isThrowException) throws Exception {
        System.out.println("ThrowClose.process() called");
        if (isThrowException) {
            throw new IOException("ThrowClose.process() throw exception");
        }
    }

    @Override
    public void close() throws IOException {
        System.out.println("ThrowClose.close() called");
        throw new IOException("ThrowClose.close() throw exception");
    }
}

테스트를 위해서 Closeable interface를 구현한 ThrowClose class를 작성하였다

try-catch-finally 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.illdangag;

import java.io.IOException;

public class TryCatchMain {
    public static void main(String[] args) {
        ThrowClose throwClose = new ThrowClose();

        try {
            throwClose.process(true);
        } catch (Exception exception) {
            System.out.println("process exception. message: " + exception.getMessage());
        } finally {
            try {
                throwClose.close();
            } catch (IOException exception){
                System.out.println("close exception. message: " + exception.getMessage());
            }
        }
    }
}
1
2
3
4
ThrowClose.process() called
process exception. message: ThrowClose.process() throw exception
ThrowClose.close() called
close exception. message: ThrowClose.close() throw exception

try-catch-finally 구문으로 예외를 처리하는 class이다

process method도 예외를 전파하고 close method로 예외를 전파하기 때문에 위와 같이 finally 블록에서도 try-catch-finally 구문이 중첩 되었다

try-with-resources 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.illdangag;

import java.io.IOException;

public class TryResourceMain {
    public static void main(String[] args) {
        try (ThrowClose throwClose = new ThrowClose()) {
            System.out.println("try block start");

            throwClose.process(true);

            System.out.println("try block end");
        } catch (Exception exception) {
            System.out.println("Exception exception");

            exception.printStackTrace();
        } finally {
            System.out.println("finally block");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
try block start
ThrowClose.process() called
ThrowClose.close() called
Exception exception
finally block

java.io.IOException: ThrowClose.process() throw exception
	at com.illdangag.ThrowClose.process(ThrowClose.java:10)
	at com.illdangag.TryResourceMain.main(TryResourceMain.java:8)
	Suppressed: java.io.IOException: ThrowClose.close() throw exception
		at com.illdangag.ThrowClose.close(ThrowClose.java:17)
		at com.illdangag.TryResourceMain.main(TryResourceMain.java:5)

try-with-resources 구문으로 예외를 처리하는 class이다

try-catch-finally 구문과는 다르게 명시적으로 close method를 호출하지 않아도 try 블록의 구문이 모두 실행 되거나 예외가 발생하는 경우 close method를 호출 한다

process method도 예외를 전파하고 close method로 예외를 전파 하지만 하나의 catch 블록으로 예외를 처리 할 수 있다

process method와 close method에서 모두 예외를 발생 시켰으며 process method에서 발생한 예외에 close method에서 발생한 예외가 중첩되어 전파된다

결론

Closeable interface를 구현한 class를 사용하는 경우에는 try-catch-finally 구문 보다 try-with-resources 구문을 사용을 추천한다

try-with-resources 구문에서는 여러개의 리소스를 처리 할 수 있으며 그에 따라 여러개의 close method를 개발자가 직접 처리하지 않게 된다

그러므로 리소스를 사용한 후에 필수적인 처리에 대해서 누락하지 않게 되며, 코드에 대한 가독성에도 도움이 된다

This post is licensed under CC BY 4.0 by the author.