[Effective Java] 2장 객체 생성과 파괴 - 아이템 8. finalizer와 cleaner 사용을 피하라

배상규·2023년 9월 21일
1

이펙티브 자바

목록 보기
8/12
post-thumbnail

개요

자바에서 두 가지 객체 소멸자인 finalizer과 cleaner를 제공한다. 하지만 이를 사용하는 것은 지양해야한다. 자바 9 에서 finalizer은 deprecated 되었고, clenaner가 대안으로 나왔으나 여전히 예측할 수 없고, 느리며, 불필요 하다.

예측 불가능성 문제

public class FinalizerTest {

    @Override
    protected void finalize() throws Throwable {
        System.out.println("Clean test");
    }

    public void test() {
        System.out.println("test");
    }
}
public class Main {

    public static void main(String[] args) throws InterruptedException {
        new Main().run();
        Thread.sleep(100);
    }

    private void run() {
        FinalizerTest finalizerTest = new FinalizerTest();
        finalizerTest.test();
    }
}

FinalizerTest의 인스턴스에 대한 GC(가비자 컬렉터)가 수행될때 "Clean test"라는 문자열을 출력되도록 finalize 메서드를 오버라이드 한 후 Main 클래스를 실행 해보자.

예상해 본다면 run 메서드가 종료되면 FinalizerTest의 인스턴스 참조가 존재하지 않기에 GC가 실행되어 0.1초 기다리는 사이에 "Clean test"가 출력되어야 한다.

하지만 실제 finalize 메서드는 호출되지 않는다.

자바 언어 명세는 어떤 스레드가 finalizer나 cleaner의 수행 시점뿐 아니라 수행 여부조차 보장하지 않는다.

그렇기에 상태를 영구적으로 수정하는 작업에서는 더욱이 finalizer나 cleaner의 사용을 지양하여야 한다.


사용지양의 이유

  • 성능 문제
    • 간단한 AutoCloseable 객체를 생성하고 가비지 컬렉터가 수거시와 finalizer을 사용한 객체 생성 파괴를 살펴보면 무려 50배가 느리다. 이는 finalizer가 가비지 컬렉터의 효율을 떨러어뜨리기 때문이다.
  • 보안 문제
    • finalizer은 생성자나 직렬화 과정에서 예외가 발생하면, 생성되다 만 객체에서 악의적인 하위 클래스의 finalizer가 수행될 수 있게 된다. 이는 절대 일어나설 안되는 일이다. 다른 객체 생성 방지로는 생성자에 예외를 던지면 되지만finalizer은 그것을 방해한다.

그렇담 언제 사용하는가?

1. 안정망 역할

cleaner나 finalizer가 즉시 호출되지 않을순 있지만, 클라이언트가 하지 않은 자원 회수를 늦게라도 해주는 것이 안전하다. 대표적으로 FileInputStrean, FileOupputStrea, ThreadPoolExecutor가 있다.

2. 네이티브 피어 정리

네이티브 객체는 일반적인 객체가 아니라서 GC가 그 존재를 모른다. 따라서 네이티브 피어가 들고 있는 리소스를 Cleaner나 Finalizer를 사용해서 해당 자원을 반납할 수도 있다.

하지만 이는 해당 자원이 중요하지 않거나, 성능상 영향이 크지 않다면 사용해야 한다.

profile
기록에 성장을

0개의 댓글