Item 8. finalizer와 cleaner 사용을 피하라

생성일
2023/07/27 10:35
챕터
2장 - 객체 생성과 파괴

자바의 소멸자

자바에는 finalizer와 cleaner라는 두 가지 소멸자를 제공한다.
finalizer는 동작을 예측할 수 없고, 상황에 따라 위험할 수 있기 때문에 일반적으로 사용해서는 안된다.
cleaner는 finalizer보다는 덜 위험하지만, 여전히 예측할 수 없고, 느리고, 불필요하다.

finalizer와 cleaner의 문제점

finalizer와 cleaner는 메모리 수거 작업이 즉시 수행된다는 보장이 없다. 객체에 접근할 수 없게 된 이후, finalizer나 cleaner가 실행되기까지 얼마나 걸릴지 예측할 수 없다.
두 소멸자 모두 가비지 컬렉터의 알고리즘과 환경에 따라 동작이 다르기 때문에, 제때 실행되어야 하는 작업으로 finalizer와 cleaner를 수행하는 것은 불가능하다.
자바 언어 명세에는 finalizer와 cleaner의 수행 시점 뿐만아니라 수행 여부조차보장하지 않는다.
그렇기 때문에 상태를 영구적으로 수정하는 작업에서는 finalizer와 cleaner 모두 절대 의존해서는 안된다.
또한 두 소멸자는 심각한 성능 문제를 동반한다.
AutoCloseable 객체를 try-with-resources로 수거하면 12ns가 걸리는 작업을, finalizer를 호출하면 550ns가 걸린다.
finalizer를 사용한 클래스는 finalizer 공격에 노출되어 심각한 보안 문제를 발생시킬 수 있다.
finalizer 공격은 생성자나 직렬화 과정에서 예외가 발생하면, 생성되다 실패한 객체에서 악의적인 하위 클래스의 finalizer가 수행될 수 있게 하는 것이다.
객체 생성을 막고자한다면 생성자에서 예외를 던지면 충분하지만, finalizer를 사용하면 finalizer 공격을 받을 수 있다.
final이 아닌 클래스를 finalizer 공격으로 방어하려면, finalize 메서드를 final로 선언하고 아무 것도 동작하지 않게 만들어 두어야 한다.

소멸자 사용 대안

객체의 클래스에서 종료해야할 자원이 있다면 AutoCloseable을 구현하고 close 메서드를 호출하자.

소멸자의 올바른 사용처

cleaner와 finalizer를 제대로 사용되는 경우로는, 자원의 소유자가 close 메서드를 호출하지 않을 경우에 대비하여 안전망 역할로 사용하는 것이다.
또 다른 경우로는, 네이티브 메서드를 통해 기능을 위임한 네이티브 객체(네이티브 피어)를 사용할 때이다. 네이티브 피어는 자바 객체가 아니므로 가비지 컬렉터가 인식할 수 없다.