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

gang_shik·2022년 2월 19일
0

Effective Java 2장

목록 보기
8/9
post-custom-banner
  • 자바는 두 가지 객체 소멸자를 제공함

  • finalizer는 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요함, 다양한 문제의 원인이 되어서 쓰임새가 있긴 하지만 기본적으로 안 씀

  • cleaner가 대안으로 나왔지만 finalizer보다는 덜 위험하지만, 여전히 예측할 수 없고, 느리고, 일반적으로 불필요함

  • 그리고 이 2개는 즉시 수행된다는 보장이 없음, 얼마나 걸릴지 몰라서 제때 실행되어야 하는 작업은 절대 할 수 없음(파일 닫기와 같이)

  • 이는 전적으로 가비지 컬렉터 알고리즘에 달려있어 천차만별임

  • 클래스에 finalizer를 달아두면 그 인스턴스의 자원 회수가 제멋대로 지연될 수 있음 , 어떤 스레드finalizer를 수행할지 명시되어 있지 않아서

  • 결국 finalizercleaner수행 시점뿐 아니라 수행 여부조차 보장을 못함, 접근할 수 없는 일부 객체에 딸린 종료 작업을 전혀 수행하지 못한 채 프로그램이 중단될 수 있는 것, 상태를 영구적으로 수정하는 작업에서는 finalizercleaner에 의존해서는 안됨(데이터베이스 같은 공유 자원의 영구 락 해제등)

  • System.gc,System.runFinalization메서드가 있지만, 실행될 가능성을 높여줄 수 있으나 보장해주진 않음, 이를 보장해주는 메서드조차도 심각한 결함이 있음

  • finalizer동작 중 발생한 예외는 무시되며, 처리할 작업이 남았더라도 그 순간 종료됨, 잡지 못한 예외때문에 해당 객체는 자칫 마무리가 덜 된 상태로 남을 수 있음, 그리고 이런 훼손된 객체를 사용하려면 어떻게 동작할지 예측할 수 없음(Cleaner의 경우 스레드를 통제해서 이런 문제는 발생하지 않음)

  • finalizercleaner심각한 성능 문제도 동반함, 가비지 컬렉터의 효율떨어뜨림

  • finalizer를 사용한 클래스는 finalizer 공격에 노출되어 심각한 보안 문제를 일으킬 수 있음, 생성자나 직렬화 과정에서 예외가 발생하며나, 이 생성되다 만 객체에서 악의적인 하위 클래스의 finalizer가 수행될 수 있게 됨, 그리고 정적 필드에 자신의 참조를 할당해 가비지 컬렉터가 수집하지 못하게 막을 수 있음

  • 이렇게 일그러진 객체가 만들어지면 이 객체의 메서드를 호출해 애초에 허용되지 않았을 작업을 수행할 수 있음

  • 객체 생성을 막으려면 생성자에서 예외를 던지는 것만으로 충분하지만, finalizer가 있다면 그렇지도 않음, final이 아닌 클래스를 finalizer 공격으로부터 방어하려면 아무 일도 하지 않는 finalize 메서드를 만들고 final로 선언해야함

  • 이런 finalizercleaner를 대신해줄 묘안은 AutoCloseable을 구현해주고, 클라이언트에서 인스턴스를 다 쓰고 나면 close 메서드를 호출하면 됨

  • close 메서드에서 이 객체는 더 이상 유효하지 않음을 필드에 기록하고, 다른 메서드는 이 필드를 검사해서 객체가 닫힌 후에 불렸다면 IllegalStateException을 던지는 것임

AutoCloseable?

이 AutoCloseable은 close 메서드를 통해서 close 처리를 자동으로 처리해주는 것임

기존에 자원을 해제하는 방식은 try-catch-finally 방식이었고 여기서 좀 더 향상된 방식인 try-with-resources를 통해서 try를 벗어나면 try 안에 선언된 객체의 close 메서드를 호출해 close한 상황을 명시할 수 없게 되었는데 하지만 그렇다고 무조건 모든 객체를 close 해주지 않음

그래서 AutoCloseable을 구현한 객체를 통해서 close 메서드를 통해서 자원을 처리할 수 있는 것임

아래와 같이 구현을 함으로써 알아서 close가 호출이 되는 것임

public class AutoCloseableExample {
    public static void main(String[] args) {
        try (CustomResource customResource = new CustomResource()){
            customResource.doSomething();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

public class CustomResource implements AutoCloseable {

    public void doSomething(){
        System.out.println("Do something ...");
    }

    @Override
    public void close() throws Exception {
        System.out.println("CustomResource Closed!");
    }
}

  • 그렇다면 이 finalizercleaner에 쓰임새가 없는 것인가 생각할 수 있지만 그래도 어느정도의 쓰임새는 존재함

  • 먼저 자원의 소유자가 close 메서드를 호출하지 않는 것에 대비한 안전망 역할임, finalizercleaner가 즉시 호출되리라는 보장은 없지만, 클라이언트가 하지 않은 자원 회수를 늦더라도 해주는 것이 아예 안 하는 것보다 나음(대신 그런 값어치가 있는지 잘 생각해야함)

  • 그러고 두 번째로는 네이티브 피어임, 네이티브 피어란 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임네이티브 객체를 말함

  • 네이티브 피어는 자바 객체가 아니니 가비지 컬렉터는 그 존재를 알지 못함, 그 결과 자바 피어를 회수할 때 네이티브 객체까지 회수하지 못함, 이때 finalizercleaner가 나서서 처리를 함

  • 단, 성능 저하를 감당할 수 있고, 네이티브 피어가 심각한 자원을 가지고 있지 않을 때에만 해당함, 성능 저하를 감당할 수 없거나 네이티브 피어가 사용하는 자원을 즉시 회수해야 한다면 close 메서드를 사용해야함

네이티브 피어?

네이티브 피어

네이티브 피어란 자바로 만든 것이 아닌 C,C++같은 언어로 작성된 것을 의미함, 그래서 네이티브 메서드는 이런 언어로 작성한 메서드네이티브 피어는 이런 상황에서 자바 객체가 네이티브 메서드를 통해 기능 수행위임한 상황을 말함

그래서 이 상황에서 메모리 할당가비지 컬렉터에서 처리를 하지 못함, 네이티브 메서드위임되면서 자원의 반납 여부를 알 수 없기 때문임

그래서 이런 경우 finalizercleaner를 통해 객체 소멸을 할 수 있다고 한 것임

profile
측정할 수 없으면 관리할 수 없고, 관리할 수 없으면 개선시킬 수도 없다
post-custom-banner

0개의 댓글