본문 바로가기

Dev Book Review/Effective Java

[Effective Java] item8. finalizer와 cleaner 사용을 피해라

1. 자바의 객체 소멸자

finailzer : 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요하다. 오동작, 낮은 성능, 이식성 문제의 원인 "쓰지말자"

cleaner : finailzer보단 덜 위험하지만, 여전히 예측할 수 없고, 느리고, 일반적으로 불필요하다.

C++의 파괴자 <-> try-with-resources, try-finally

 

2. finalizer와 cleaner 사용을 피해야하는 이유

a. 즉시 수행된다는 보장이 없다.

객체에 접근할 수 없게 된 이후부터 실행되기까지 얼마나 걸릴지 알 수 없다.= 제때 실행되어야하는 작업은 절대 할 수 없다.

수행 시점이 전적으로 GC 알고리즘에 달려있으며, 구현 방식에 따라 천차만별이다.

finalizer 쓰레드는 다른 애플리케이션보다 우선순위가 낮다.

 

b. 수행 여부도 보장하지 않는다.

접근할 수 없는 객체에 딸린 종료작업을 수행하지 못한채 프로그램이 중단될 수도 있다.

상태를 영구적으로 수정하는 작업에서 절대 finalizer나 cleaner에 의존해서는 안된다.

System.gc, System.runFinalization 메서드에 현혹되지 말자 : 실행 가능성은 높여주나 보장해주진않는다.

System.runFinalizerOnExit / Runtime.runFinalizerOnExit : 심각한 결함

 

c. finalizer 동작중 발생한 예외가 무시되며, 처리할 작업이 남았어도 그 순간 종료된다.

잡지못한 예외로 객체가 마무리가 덜된 상태로 남아있을 수 있다 > 훼손된 객체를 사용하려 할 때 예측할 수 없다.

 

d. finalizer와 cleaner는 심각한 성능문제도 동반한다.

AutoCloseable 객체를 생성해 GC 수거시간 : 12ns
finalizer 수거시간 : 550ns -> GC의 효율을 떨어뜨린다.

 

e. finalizer 공격에 노출되어 심각한 보안 문제를 일으킬 수도 있다.

생성자나 직렬화 과정에서 예외가 발생하면 생성되다 만 객체에서 악위적인 하위 클래스의 finalizer가 수행될 수 있다.
일그러진 객체 -> 애초에 허용되지 않았을 작업 수행 가능

객체 생성을 막으려면 생성자에서 예외를 던지면 되는데, finalizer가 있으면 그렇지 않다.

final이 아닌 클래스를 finalizer 공격으로부터 방어하는 방법아무일도 하지 않는 finalize 메서드를 만들고 final로 선언 (하위클래스가 안만들어짐)

 

3. finalizer, cleaner의 대안

AutoCloseable을 구현하고, 클라이언트에서 인스턴스를 다 쓰면 close 메서드를 호출한다.예외가 발생해도 잘 종료 되도록 try-with-resources를 사용한다

 

4. finalizer, cleaner의 쓰임새

a. 자원의 소유자가 close() 메서드를 호출하지 않는 것에 대비한 안전망 역할

안전망 역할의 finalizer를 작성할때 그럴만한 값어치가 있는지 고민하자

자바라이브러리 : FileInputStream, FileOutputStream, ThreadPoolExecutor

 

b. 네이티브 피어(native peer)와 연결된 객체

네이티브 피어 = 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체자바 객체가 아니라 GC가 존재를 알지 못한다.

성능 저하를 감당할 수 있고, 네이티브 피어가 심각한 자원을 갖고있지 않을 경우에 적당하다.즉시 회수해야한다면 .close()를 사용하자