[Effective Java] 7. finalize 를 사용하지 말것

1 분 소요

C++의 경우 생성자와 소멸자를 쌍으로 만들지만 자바의 경우는 GC가 불필요한 메모리를 정리하기 때문에 소멸자가 필요하지 않다.

자바에서 소멸자인 finalize는 절대로 사용하면 안된다.

1. finalize의 단점

자바의 finalize의 경우 예측이 불가능하고, 위험하며 불필요하다.

1.1. 즉시 실행된다는 보장이 없다.

객체에 대한 모든 참조가 사라지고 난 이후 finalize가 언제 호출될지는 알 수가 없다.
이 호출 시점은 GC의 알고리즘에 좌우되고 이는 JVM에 따라서 다르다.

따라서 파일을 닫는것과 같은 행위를 finalize에서 하면 안된다. file descriptor는 시스템에서 유한한 자원인데 언제 호출될지 모르는 finalize에서 이를 반환하게 하면 자칫 다음 파일을 열때 파일 개수 제한에 걸려 오류가 날 수도 있다.

1.2. 실행 된다는 보장도 없다.

자바 명세에는 종료자가 즉시 실행되어야 한다는 내용도 없지만 반드시 실행되어야 한다는 내용도 없다. 따라서 JVM에 따라 아예 실행이 되지 않을 가능성도 있다.

1.3. 객체 상태를 망가트릴 수 있고, 예외를 던지지 않는다.

finalize 실행 도중 uncaught 예외가 발생할 경우 해당 예외는 무시되고 종료 과정은 그냥 중단된다.
무시되는 예외는 stack trace에 표시되지 않고 경고조차 뜨지 않는다.

1.4. 프로그램 성능이 심각하게 떨어진다.

테스트를 해보면 finalize를 사용하는 경우 객체의 생성과 소멸 시간이 엄청나게 늘어난다고 한다.
일단 finalize 메서드가 정의되어 있으면 GC에 의해 바로 메모리가 정리되지 않는다. 대신 Finalization Queue에 들어가고 이후 Finalizer에 의해 정리가 된다. finalize 메서드 수행 시간이 길다면 그만큼 객체가 오랫동안 메모리를 점유하게 되고, 메모리가 부족한 경우 OOME가 발생한다.

2. finalize의 실행

  1. System.gc(), System.runFinalization()
    이 메서드를 호출하는 방법이 있는데 이 역시 종료자의 호출 가능성을 높여주는 것 뿐 명시적으로 실행을 보장하는 것은 아니다.

  2. System.runFinalizersOnExit(), Runtime.runFinalizersOnExit()
    종료자의 실행을 보장하는 메서드이다. 하지만 심각한 결함이 있어 이미 명세에서 폐기되었다.

뒷부분 추가 정리할 부분 남았음.

댓글남기기