JAVA/Effective Java

아이템8. finalizer와 cleaner 사용을 피하라

hyun-1200 2022. 5. 21. 23:41

아이템8. finalizer와 cleaner 사용을 피하라

  • 자바는 두 가지 객체 소멸자 제공을 제공한다 : finalizer / cleaner

finalizer

  • 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요하다.
  • 기본적으로 '쓰지 말아야'하며, 자바9에서는 finalizer을 사용자제 API로 지정했다.

cleaner

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

사용을 피해야 하는 이유

1. finalizer 와 cleaner은 즉시 수행된다는 보장이 없다.

  • 둘다 제때 실행되어야 하는 작업은 절대 할 수 없다.

2. 수행 시점뿐 아니라 수행 여부조차 보장하지 않는다.

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

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

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

5. finalizer를 사용한 클래스는 finalizer 공격에 노출되어 심각한 보안 문제를 일으킬 수도 있다.

  • finalizer의 공격 원리 : 생성자나 직렬화과정에서 예외가 발생하면, 이 생성자는 되다 만 객체에서 악의적인 하위 클래스의 finalizer가 수행될 수 있게 된다.
  • 객체 생성을 막으려면 생성자에 예외를 던지는 것만으로 충분하지만, finalizer가 있다면 그렇지도 않다.
  • final이 아닌 클래스를 finalizer 공격으로부터 방어하려면 아무 일도 하지 않는 finalize 메서드를 만들고 final로 선언하자.

AutoCloseable : finalizer와 cleaner의 묘안

  • AutoCloseable을 구현해주고, 클라이언트에서 인스턴스를 다 쓰고 나면 close 메서드를 호출하면 된다.
  • close 메서드에서 이 객체는 더 이상 유효하지 않음을 필드에 기록하고, 다른 메서드는 이 필드를 검사해서 객체가 닫힌 후에 불렀다면 IllegalStateException을 던진다.

네이티브 피어 (native peer)와 연결된 객체 : cleaner와 finalizer을 적절히 활용하는 두번째 예

  • 네이티브 피어? 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체
  • 네이티브 피어는 잦바 객체가 아니라서 가비지 컬렉터는 그 존재를 알지 못한다.
    • 자바 피어를 회수 할때 네이티브 객체까지 회수하지 못하므로, 이 때 cleaner나 finalizer가 나서서 처리하기에 적당한 작업이다.
    • 다만, 성능저하를 감당할 수 있고 네이티브 피어가 심각한 자원을 가지고 있지 않을 때에만 해당한다.
      • 성능 저하를 감당할 수 없거나 네이티브 피어가 사용하는 자원을 즉시 회수해야 한다면 close 메서드를 사용해야 한다.

정리

cleaner(자바 8까지는 finalizer)은 안전한 역할이나 중요하지 않은 네이티브 자원 회수용으로만 사용하자.
물론 이런 경우라도 불확실성과 성능 저하에 주의해야 한다.