본문 바로가기
Effective Java

[Effective Java] 아이템8 finalizer와 cleaner 사용을 피하라

by byeongoo 2021. 1. 21.

■ 객체 소멸자

자바에서는 두 가지 객체 소멸자를 제공한다. 오브젝트 클래스에 포함된 finalize 메서드와 Java 9에서 추가된 java.lang.ref 패키지에포함된 Cleanner 클래스가 있다.

 

두 가지 모두 JVM에서 Garbage Collector가 수행될 때 실행되는 구문이다. 그런데 finalize 메서드는 Java 9에서 Deprecated 되었다. 또한 새로 추가된 Cleaner의 경우도 사용을 권장하지 않는다.

 

■ 사용 지양 이유

  • 사용 했을 때 실행 시점을 보장할 수 없다. 
  • 실행 조차 안될 수 있다. 즉시 실행이 안되는 것을 감안하더라도 프로그램이 비정상 종료된다는 등의 이유로 실행조차 안될 수도 있다. 특정 시점 또는 반드시 실행되어야 한다는 것을 기대하고 사용해서는 안된다.
  • 역효과를 불러올 수 있다. Unreachable 상태의 객체를 가비지 컬렉션이 처리할 때 기본적으로 finalizer가 호출되지만 그렇다고 가비지 컬렉션이 즉시 수행되는 것은 아니다. finalizer queue에 삽입되어 순차적으로 수행된다. finalizer는 다른 쓰레드보다 우선순위가 낮다. 따라서 finalize 메서드 실행이 느린 경우 인스턴스 소멸이 느려지며 OutOfMemory와 같은 오류를 발생시킬 수 있다.
  • finalizer의 경우 동작 중 발생한 예외가 무시된다. 처리할 작업이 남았더라도 그 순간 종료된다.
  • 실행이 느리다. 필자의 컴퓨터로 AutoClosable을 구현한 객체를 만들고 클라이언트에서 인스턴스를 다 사용한 후 close를 호출했을 경우 자원을 반납하는데 12ns가 소요되는 반면에 finalizer를 사용한 가비지 컬렉션의 경우 수행시간이 550ns가 소요되었다. 즉, 가비지 컬렉터의 효율을 떨어뜨린다.
  • 보안에 취약하다. finalize 메서드의 실행 시간이 오래 걸리도록 만들면 전반적인 시스템 장애를 불러올 수 있다. 메서드를 재정의하여 악의적으로 실행을 방해할 수 있기 때문에 final 키워드를 붙여서 상속하지 못하도록 막아야 한다.

■ 사용 이유

  • Autoclosable을 구현하지 않았으며 자원의 소유자가 close 메서드를 호출하지 않은 것에 대비한 안전망 역할이다. cleaner나 finalizer가 즉시 호출되리라는 보장은 없지만 클라이언트가 하지 않은 자원 회수를 늦게라도 해주는 것이 아예 안하는 것보다 낫다. 자바 라이브러리에서는 FileInputStream, FileOutputStream, ThreadPoolExecutor가 대표적이다.
  • 가비지 컬렉터(Garbage Collector)가 회수하지 못하는 네이티브(native) 자원의 정리에 사용한다. 자바 객체가 아니므로 가비지 컬렉터가 관리하는 대상이 아니기 때문이다. finalizer를 명시적으로 호출함으로 자원을 회수할 수 있다.