finalizer: 객체 소멸자 (Destructor) 이며, 오동작, 낮은 성능, 이식성 문제 등으로 자바 9부터는 deprecated API로 지정되었다.
cleaner: finalizer 의 대안, 덜 위험하지만 여전히 예측할 수 없는 동작을 하며, 일반적으로 불필요함
객체를 사용하지 않게 된 뒤 언제 호출될 지 알 수 없다. -> 가비지 컬렉터의 알고리즘에 따라 언제 소멸될 지 모르기때문에
접근할 수 없는 일부 객체에 딸려있는 종료 작업은 수행되지 않을 수도 있다. 즉, 수행 여부조차 보장되지 않는다. ex) DB 의 Lock 해제를 finalizer에서 한다면?
finalizer 작업 중 발생한 예외는 무시되며, 작업이 남았더라도 그 순간 종료된다. -- cleaner 는 괜찮긴 하다.
성능이 매우 안좋다 (12ns 걸리던 게 550ns 걸릴 정도.) -> 객체 소멸자가 가비지 컬렉터의 효율을 떨어뜨리기 때문에
생성자나 직렬화 과정에서 예외가 발생하면, 의도하지 않은 finalizer 가 실행되는 데, 내부에서 자신의 참조를 할당하는 등 보안 문제를 일으킬 수 있다 -> final로 finalizer 메서드를 아무일도 하지 않도록 미리 선언하여 해결 가능
Q. 그럼 파일이나 스레드 같은 자원을 담고 있는 클래스에서 할당 해제를 어디서 해야할까?
A. AutoCloseable 을 구현하여 close 메서드를 호출하는 방식으로 실행을 보장해줄 수 있다 (내부에서 예외가 발생할 수 있으므로 try-with-resource를 사용해야함)
close를 호출하지 않는 것에 대한 안전망으로 사용할 수 있다
네이티브 피어
는 가비지 컬렉터가 그 존재를 알 수 없으므로 소멸자 사용이 괜찮을 수 있다. (즉시 회수해야할 자원이라면 위처럼 close를 사용하자.)
참고.
네이티브 피어 : 네이티브 메서드(C, C++ 와 같은 네이티브 언어로 만든 메서드) 를 통해 기능을 위임한 객체
Java finalizer, cleaner vs Destructor
일반적인 소멸자는 더이상 그 객체를 사용하지 않게되어 그 객체와 관련된 자원들을 회수하는 역할을 하고, 필요로 함
자바의 경우, 그 역할은 가비지 컬렉터가 담당하기에 요구되는 작업이 없음
참고.
정적 중첩 클래스 vs 중첩 클래스
중첩 클래스는 자동으로 바깥 객체의 참조를 가지지만, 정적으로 선언하면 가지지 않는다.
따라서 중첩 클래스만 자원 회수가 필요하다면 정적으로 선언할 필요가 있다.
핵심 정리
cleaner는 안전망 역할이나, 중요하지 않은 네이티브 자원 회수용으로만 사용하자. 물론 이 경우라도 불확실성과 성능 저하에 주의해야 한다.