이펙티브 자바(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)는 안전망 역할이다.
중요하지 않은 네이티브 자원 회수용으로만 사용하자.
'프로그래밍 언어 > Java' 카테고리의 다른 글
이펙티브 자바 - 아이템 7. 다 쓴 객체 참조를 해제하라 (0) | 2024.09.04 |
---|---|
이펙티브 자바 - 아이템 6. 불필요한 객체 생성을 피하라 (0) | 2024.08.20 |
이펙티브 자바 - 아이템 5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2024.08.20 |
이펙티브 자바 - 아이템 4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2024.08.17 |
이펙티브 자바 - 아이템3. private 생성자나 열거 타입으로 싱글턴임을 보증하라. (2) | 2024.08.14 |