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