Item8. finalizer와 cleaner 사용을 피해라
Java는 두가지 객체 소멸자를 제공한다.
finalizer
cleaner
여기서 객체 소멸자란 생성자와 정반대의 개념으로, 객체가 소멸될떄(GC될때)
자동으로 호출되는 함수이다.
Java 9에서부터는 finalizer를 사용 자제해야 할 API로 지정하고, cleaner를 그 대안으로 소개하였다. 하지만 책에서는 cleaner또는 finalizer의 사용을 피하라고 얘기하고 있다,.그렇다면 finalizer와 cleaner는 왜 사용하면 안될까?
1 | // deprecated |
단점 1. finalizer와 cleaner는 즉시 수행된다는 보장이 없다.
객체의 소멸을 GC가 담당하므로, finalizer와 cleaner를 언제 수행할지의 역할은 전적으로 GC 알고리즘에 달려있다. 즉 GC 구현마다 차이가 있다.
위와 같은 특징들을 보면 당연히 finalizer와 cleaner로는 제때 실행되어야 하는 작업은 절대 할 수 없다.
단점 2. finalizer는 instance 반납을 지연시킬 수도 있다.
finalizer 쓰레드는 우선순위가 낮아서 , 언제 실행될지 모른다. -> 계속 대기 된다면 GC되지 않고, OutOfMemoryException이 발생할수도 있다.
단점 3. finalize 동작 중에 발생한 에러는 무시되며, 처리할 작업이 남았더라도, 그 순간 종료된다.
중간에 예외가 터지면, 객체가 훼손된 상태로 남아있을수도 있다.
단점 4. 성능 문제
AutoCloseable 객체를 만들고, try-with-resoucre 로 자원 반납을 하는데 걸리는 시간은 12ns 인데 비해, Finalizer는 550ns 거의 약 50배가 걸렸다. cleaner도 60ns로, 5배정도 느리다.
그렇다면 finalizer나 cleaner를 어떻게 대체해주어야 하는가?
- 반환할 자원 class에 AutoCloseable 을 구현해주고, client에서 instance를 다 쓰고 나면 close method을 호출해주면 된다.
finalizer와 cleaner의 적절한 용도
자원 소유자가 close method를 깜빡하고 호출하지 못하였을때, 안전망 역할을 해준다.
자바 객체가 아닌 native peer 회수
cleaner 사용예시 - 리소스 반환을 위한 안전망 역할
1 |
|
위 예시처럼 Room client가 명시적으로 close를 호출하는것을 깜빡하였다면 room이 GC에 의해 회수될때 cleaner가 state의 run method를 호출해줌으로서 , 리소스를 회수하는 역할을 해준다. (안전망 역할)
하지만 이조차도 try-with-resouces 블록으로 감싸면 필요하지 않다.
예를 들면 다음과 같다.
1 |
|
정리하면 java 8 : finalizer / java 9 이후 : cleaner는 안전망 역할이나 네이티브 피어 객체 회수용 정도로만 사용하고, 이후에는 사용하지 말고, 대신 try-with-resource로 안전하게 자원을 반환하자는 내용이다.