Item 8. Finalizer와 Cleaner 사용은 피하라

서론

Java에서는 2가지의 객체 소멸자를 제공한다.

  • finalzier
  • cleaner

이 2가지 객체 소멸자는 JVM내의 GC가 작동할 때 실행되는 구문이다.
기본적으로 이 2가지 구문은 사용하지 말아야 한다.
현재 Java 9 버전 부터는 Object 클래스에 대한 finalizer를 Deprecated 처리하였다.
아래에 사용하지 말아햐 하는 이유를 하나씩 보도록 하겠다.

finalizer와 cleaner를 사용하지 말아야 하는 이유

실행을 보장할 수 없다.

finalizer에 특정 로직을 삽입하는 경우 실행을 보장 할 수 없다.
기본적으로 GC가 발생할 때 실행되는 로직이지만, Java Application이 죽는다던지의 이유로 finalizer 실행이 되지 않을 수 있다.
그렇기 때문에 제 때 실행되어야 하거나, 뭔가 상태를 수정하는 행위를 절대적으로 하면은 안된다.

느리다.

Effective Java 책의 예제에서는 AutoCloseable과 finalizer의 성능비교를 한 문단이 있다.
Autocloseable을 사용한 GC 수행시간은 12ns였지만, finalizer를 사용한 GC 수행 시간은 550ns가 걸렸다고 한다.
finalizer가 가비지 컬렉터의 효율을 떨어뜨리기 때문이다.

시스템 전체 장애를 유발 할 수 있다.

Java API 문서 상에서는 GC가 UnReachable 상태의 객체를 가비지 컬렉션 할 때 finalizer가 호출된다고 명시하고 있다.
하지만 finalizer가 실행되는 시점은 GC 발생 시 즉각적으로 이루어지는게 아니다.
finalizer queue에 삽입되어 순차적으로 finalizer를 실행하게 된다.
그렇기 때문에 finalizer메소드 실행이 느린 경우 객체 소멸이 느려지므로 Out of Memory와 같은 오류를 발생 시킬 가능성이 높아지게 된다.

finalizer 공격에 취약하다.

위에 적은 것 처럼 finalizer 메소드 실행시간이 오래 걸리게 만들면 시스템에 심각한 장애를 유발할 수 있다.
finalizer 메서드를 override해서 악의적으로 느리게 할 수 있기 떄문에 finalizer를 사용해야 하는 경우라면
메소드에 final 키워드를 붙여서 상속하지 못하도록 해야한다.

그럼 finalizer나 cleaner는 어디서 쓰나?

  • 개발자가 객체의 close를 호출하지 않는 경우 -> 자원 해제를 안하느니 느리더라도 하는게 낫다.
    (이 경우는 동의 못하겠다. 개발자가 close를 시켜야 한다.)
  • native peer와 연결된 객체
    • native peer는 자바 객체가 아니기 떄문에 가비지 컬렉터의 관리 대상이 아니다.
      그렇기 때문에 native peer가 회수 될때 finalizer 메서드를 실행해 자원을 해제 할 수 있다.

finalizer 기능이 필요한 경우에는 어떻게?

Autocloseable Interface를 사용하여 close를 호출시키도록 하자.
다음장의 try-catch-resource 구문에서 설명하도록 하겠다.

참고