자바는 두 객체 소멸자가 있다.
자바의 finalizer와 cleaner는 c++의 파괴자(destructor)와는 다른 개념이다. C++의 파괴자는 비메모리 자원을 회수하는 용도로도 쓰이지만, 자바에서는 try-with-resources
와 try-finally
를 사용해 해결한다.
finalizer와 cleaner는 즉시 수행된다는 보장이 없다. 객체에 접근할 수 없게 된 후 finalizer나 cleaner가 실행되기까지 얼마나 걸릴지 알 수 없다.
그래서 파일 닫기를 finalizer나 cleaner에 맡기면 오류가 발생할 수도 있다. 시스템이 동시에 열 수 있는 파일 개수에 한계가 있기 때문이다.
finalizer나 cleaner를 얼마나 신속히 수행할지는 전적으로 가비지 컬렉터 알고리즘에 달려있다.
수행 시점뿐 아니라 수행 여부조차 보장하지 않는다. 접근할 수 없는 일부 객체에 딸린 종료 작업을 전혀 수행하지 못한 채 프로그램이 중단될 수도 있다. 따라서 프로그램 생애주기와 상관 없는 상태를 영구적으로 수정하는 작업에선느 절대 finalizer나 cleaner에 의존해서는 안된다.. 에를 들어 db 같은 공유 자원의 영구 락 해제를 finalizer나 cleaner에 맡기면 시스템 전체가 서서히 멈춘다.
finalizer 동작 중 발생한 예외는 무시되며, 처리할 작업이 남았더라도 그 순간 종료된다.
그나마 cleaner를 사용하는 라이브러리는 자신의 스레드를 통제하기 때문에 이런 문제는 발생하지 않는다.
finalizer를 사용한 클래스는 finalizer 공격에 노출되어 심각한 보안 문제를 일으킬 수도 있다.
finalizer를 사용한 클래스는 finalizer 공격에 노출되어 보안 문제도 발생한다.
AutoCloseable을 구현해주고, 클라이언트를 다 쓰고 나면 close 메서드를 호출하면 된다. 각 인스턴스는 자신이 닫혔는지를 추적하는 것이 좋다. 다시 말해, close 메서드에서 이 객체는 더 이상 유효하지 않음을 기록하고, 다른 메서드는 이 필드를 검사해서 객체가 닫힌 후에 불렸다면 IllegalStateException을 던지는 것이다.
자원의 소유자가 close 메서드를 호출하지 않는 것에 대비한 안전망. cleaner나 finalizer가 언제 호출되는지 보장은 없지만, 아예 안하는 것보다 낫긴하다. 하지만 그럴만한 값어치가 있는지 생각해보자.
네이티브 피어와 연결된 객체에서. 네이티브 메서드를 통해 기능을 위임한 객체가 네이티브 피어다. 자바 객체가 아니니 가비지 컬렉터가 그 존재를 알지 못한다.