본문 바로가기
프로그래밍 언어/Java

이펙티브 자바 - 아이템 8. finalizer와 cleaner 사용을 피하라

by 데브조이 2024. 9. 4.
반응형
이펙티브 자바(Effective java)를 읽고 정리한 글입니다.

 

자바는 finalizer, cleaner 두 가지 객체 소멸자를 제공한다. 

finalizer는 기본적으로 사용하지 않아야 한다. 예측할 수 없고, 오동작 및 낮은 성능 등의 원인이 되기도 한다. 

finalizer 대안으로 등장한 cleaner는 상대적으로 덜 위험하다. 하지만 동일하게 예측할 수 없고, 불필요하다


왜 사용하면 안 될까

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

즉, 이들을 이용하여 제 때 실행되어야 하는 작업은 수행할 수 없다. 

 

finalizer와 cleaner의 수행 시점은 가비지 컬렉터 알고리즘에 달려 있다. 

finalizer를 달아두면 인스턴스 자원 회수가 지연될 수 있다. 

 

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

cleaner는 자신을 수행할 스레드를 제어할 수 있다는 차이가 있다. 

하지만 가비지 컬렉터의 통제하에 있다는 사실은 변하지 않는다. 즉, 언제 수행될지 알 수 없다.

 

2. 자바 언어 명세는 finalizer와 cleaner 의 수행 여부조차 보장하지 않는다.

그렇기 때문에 상태를 영구적으로 수정하는 작업에서는 절대 이에 의존하면 안 된다. 

ex. 데이터베이스의 락을 해제하는 작업

 

3. finalizer 동작 중 발생한 예외는 무시되고, 남은 작업도 종료된다. 

예외로 인해 작업이 비정상적으로 종료된다. 

이러한 상태에서 다른 스레드가 객체를 사용하면 또다른 문제를 야기할 수 있다. 

finalizer는 경고도 출력하지 않는다. 

 

4. 이들은 성능 문제를 동반한다. 

finalizer와 cleaner는 가비지 컬렉터의 효율을 떨어뜨린다. 

 

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

final이 아닌 클래스를 finalizer 공격으로부터 방어하려면 아무 일도 하지 않는 finalize 메서드를 만들고 final로 선언하자.

 

*finalizer 공격: 생성자나 직렬화 과정에서 예외 발생 시, 미완의 객체에서 악의적인 하위 클래스의 finalizer 가 수행될 수 있게 된다. 이 finalizer는 정적 필드에 자신의 참조를 할당하여 GC가 수집하지 못하게 할 수 있다. 


대안

AutoCloseable 을 구현하고, 인스턴스를 다 사용하고 나면 close 메서드를 호출하자 (일반적으로 try-with-resources 사용).

각 인스턴스는 자신이 닫혔는지를 추적하는 것이 좋다. 방법은 아래와 같다.

 

1. close 메서드에 해당 객체가 유효하지 않음을 기록한다. 

2. 다른 메서드는 이를 검사한다. 객체가 닫힌 후 사용하려 했다면 예외를 던진다.


쓰임

그럼에도 cleaner와 finalizer의 목적은 두 가지가 있다. 

  • 자원의 소유자가 close메서드를 호출하지 않는 것에 대비한 안전망 역할 → 늦게라도 자원 회수
  • 네이티브 피어와 연결된 객체
    → 자바 객체가 아니기 때문에 가비지 컬렉터가 존재를 알지 못해 회수할 수 없다.
    → 성능 저하 감당 가능할 때 / 네이티브 피어가 중요한 자원을 가지고 있지 않을 때

*네이티브 피어(native peer): 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체


정리

cleaner(자바 8 이하 버전은 finalizer)는 안전망 역할이다.

중요하지 않은 네이티브 자원 회수용으로만 사용하자.

 

 

반응형