finalizer
cleaner
cleaner는 API로 제공했던 finalizer처럼 재정의하는 것과 달리 구성을 통해 cleaner를 사용해야 한다. 하지만, cleaner 역시 예측할 수 없고, 느리고, 일반적으로 불필요하다.
finalizer 나 cleaner 는 즉시 실행된다는 보장이 없어 제때 실행되어야하는 작업은 절대 할 수 없다.
finlizer
또는 cleaner
가 바로 실행되지 않을 수도 있다. 그 시간이 얼마나 걸릴지는 아무도 모른다. 따라서 타이밍이 중요한 작업을 절대로 finalizer
또는 cleaner
로 해서는 안된다.finalizer
또는 cleaner
로 처리한다면 실제로 그 파일 리소스가 언제 처리될지 알 수 없고 자원 반납이 안되서 더 이상 새로운 파일을 열 수 없는 상황이 발생할 가능성이 있다.인스턴스 회수가 지연될 뿐만 아니라 제대로 실행되지 않을수도 있다.
finalizer
스레드는 다른 애플리케이션 스레드보다 우선 순위가 낮아서 실행될 기회를 제대로 얻지 못할수도 있다. (다른 작업이 바쁘면 뒤로 미뤄진다, 언제 호출될지 모른다는 의미이다.) 따라서finlizer
안에 어떤 작업이 있고, 그 작업을 쓰레드가 처리 못해서 대기하고 있다면 해당 인스턴스는GC
가 되지 않고 계속해서 쌓이다가 결국OOM(Out Of Memory Exception)
이 발생할 수 있다.cleaner
는 자신을 수행할 스레드를 제어할 수는 있지만, 역시나 가비지 컬렉터에 의존하므로 즉시 사용된다는 보장은 없다.
상태를 영구적으로 수정하는 작업에서는 절대 finalizer 나 cleaner 에 의존해서는 안된다.
- 자바 언어 명세는 finalizer 나 clenaer 의 수행 여부를 보장하지 않는다. 접근할 수 없는 일부 객체에 딸린 종료작업을 수행하지 못한채 프로그램이 종료될 수도 있다.
- 만약 데이터 베이스 같은 자원의 락을 이것들로 반환하는 작업을 한다면 전체 분산 시스템이 멈춰 버릴 수도 있다. 따라서 finalizer 나 clenaer로 저장소 상태를 변경하는 일을 하지 말아야 한다.
```java
public class SampleRunner {
public static void main(String[] args) {
final SampleRunner sampleRunner = new SampleRunner();
sampleRunner.run();
System.gc(); // 실행이 안될수도 있다!
}
}
```
- `System.gc` 나 `System.runFinalization` 메서드에 속지 말자 이들은 실행 가능성을 높여줄 순 있으나, 보장하지는 않는다.
- `System.runFinalizationOnExit` 와 `Runtime.runFinalizationOnExit` 는 실행을 보장해주려 만들었지만 심각한 결함으로 수십년간 `deprecated` 상태이다.
-
finalizer 동작 중 발생한 예외는 무시되며, 처리할 작업이 남았더라도 그 순간 종료된다.
잡지못한 예외때문에 객체가 훼손될 수 있고, 다른 스레드가 이 훼손된 객체에 접근하게 될 수 있다. finalizer 에서 예외가 발생할 경우 경고조차 출력하지 않는다. 그나마 cleaner 를 사용하는 라이브러리는 자신의 스레드를 통제하기 때문에 이러한 문제가 발생하지 않는다.
finalizer
가 가비지 컬렉터의 효율을 떨어뜨리기 때문에 try-with-resource
와 비교했을 때 굉장히 느리다. cleaner
역시 마찬가지이다.finalizer
를 사용한 클래스는 finalizer
공격에 노출되어 심각한 보안 문제를 일으킬 수 있다.
A, B 클래스가 있다고 가정했을 때, B클래스가 A클래스를 상속받는다. 그리고 finalize
를 오버라이딩한 B 클래스의 인스턴스를 생성하는 도중에 예외가 발생하거나, 직렬화 과정에서 예외가 발생하면, 원래 죽었어야 할 객체의 finalizer
가 실행될 수 있다.
그럼 finalize
메서드 안에서 이 인스턴스가 가진 static
필드에 접근할 수 있어서 해당 인스턴스의 레퍼런스를 기록할 수 있고 GC에 의해 수거되지 못하게 할 수도 있다.
원래는 생성자가 예외를 발생시켜 존재하지 않았어야 하는 인스턴스인데 finalizer
때문에 살아남아 있는 것이다.
이 문제에 대한 해결방법은 final
클래스들은 하위 클래스를 만들 수 없으니 공격으로부터 안전하다. 따라서 아무일도 하지 않는 finalizer
메서드를 만들고 final
로 선언하자.
cleaner
과finalizer
는 안전망 역할이나 중요하지 않은 네이티브 자원 회수용으로만 사용하자. 이런 경우라도 불확실성과 성능 저하에 주의해야 한다.