이펙티브 자바 - item 8. finalizer와 cleaner 사용을 피하라
on JAVA
이펙티브 자바 - item 8. finalizer와 cleaner 사용을 피하라
자바는 접근할 수 없게 된 객체를 회수하는 역할을 가비지 컬렉터가 담당하며 비메모리 자원을 회수하는 용도는 try-with-resources, try-finally로 해결한다.
사용하지 말아야 하는 이유
자바는 finalizer, cleaner 두 가지 객체 소멸자를 제공한다. 두 경우 모두 기본적으로 사용하지 말아야 한다. 사용하지 말아야 하는 이유는 다음과 같다.
- finalizer와 cleaner는 즉시 수행된다는 보장이 없다.
객체가 접근 할 수 없게 된 후 finalizer와 cleaner가 실행되기까지 얼마나 걸릴지 알 수 없다. 즉, finalizer와 cleaner로는 제때 실행되어야 하는 작업은 절대 할 수 없다.
예컨대 파일 닫기를 한다면 시스템이 동시에 열 수 있는 파일 개수에 한계가 있기에 중대한 오류를 일으킬 수 있다. 시스템이 finalier나 cleaner 실행을 게을리해서 파일을 계속 열어 둔다면 새로운 파일을 열지 못해 프로그램이 실패할 수 있다.
- 수행 여부를 보장하지 않는다. 종료 작업을 전혀 수행하지 못한 채 중단될 수도 있다. 따라서 프로그램 생애주기와 상관없는 상태를 영구적으로 수정하는 작업에서는 사용해서는 안된다.
예를 들어 데이터베이스 같은 공유 자원의 영구 락(lock) 해제를 finalizer와 cleaner에게 맡기면 분산 시스템 전체가 멈출 수 있다.
예외 처리 finalizer 동작 중 발생한 예외는 무시되며, 처리할 작업이 남았더라도 그 순간 종료된다. 잡지 못한 예외로 인해 해당 객체는 마무리가 덜 된 상태로 남을 수 있다.
성능 문제 가비지 컬렉터의 효율을 떨어뜨리고 안전망의 설치의 대가로 AutoCloseble 객체를 사용하는 것에 비해 약 5배 정도 느려진다.
보안 문제 finalizer는 생성자나 직렬화 과정에서 예외가 발생한다면 이 생성되다만 객체에서 악의적인 하위 클래스의 finalizer가 수행될 수 있게 된다. 또한 이 finalizer는 정적 필드에 자신의 참조를 할당해 가비지 컬렉터가 수집하지 못하게 막을 수 있다. finalizer를 final로 선언해 해결할 수 있다.
Cleaner, Finalizer를 쓸만한 경우
자원의 소유자가 close 메서드를 호출하지 않는 것에 대한 안정망 역할 호출된다고 보장은 없지만 자원 회수를 늦게라도 해주기 위한 안전망 역할로 사용할 수 있다. 예를 들어, FileInputStream, FileOutputStream, ThreadPoolExecutor가 존재한다.
네이티브 피어와 연결된 객체에서의 사용 native peer란 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체를 말한다. 네이티브 피어는 자바 객체가 아니여서 가비지 컬렉터는 그 존재를 알지 못한다. 따라서 자바 피어를 회수할 때 네이티브 피어도 회수하지 못해서 cleaner나 finalizer가 나서서 처리할 수 있다.
단 성능 저하를 감당할 있고 심각한 자원을 가지고 있지 않을 때만 해당.
정리
그냥 try-with-resource 쓰면 될 것 같다.