본문 바로가기
프로그래밍 놀이터/디자인 패턴, 리펙토링

[Effective Java] 파이날라이저 ( finalizer ) 의 사용을 피하자.

by 돼지왕 왕돼지 2015. 12. 15.
반응형

 [Effective Java] 파이날라이저 ( finalizer ) 의 사용을 피하자.



-
파이널라이저는 예측 불가에다가 위험하기도 하며 일반적으로 불필요하다.


-
파이널라이저의 큰 단점은 신속하게 실행된다는 보장이 없다는 것.
즉 파이널라이저에서 time critical 하게 중요한 일을 해서는 안 된다는 것.


-
파이널라이저가 얼마나 빨리 실행되는가는 가비지 컬렉션 알고리즘에 달려있으며,
이는 JVM 종류에 따라 다양하다.


-
자바 언어 명세에는 파이널라이저가 신속하게 실행된다는 보장이 없는 것은 물론, 반드시 실행될것인지도 보장하고 있지 않다.


-
System.gc() 와 System.runFinalization() 메소드들을 사용하지 말자.
그 메소드들은 파이널라이저가 실행될 가능성을 높여주긴 하지만, 실행됨을 보장하지는 않는다.
파이널라이즈를 보장하는 유일한 메소드들은 System.runFinalizersOnExit() 와 Runtime.runFinalizersOnExit() 인데
이 메소드들은 치명적 결함이 있어 현재는 사용금지되었다.


-
파이널라이저의 또 다른 단점은 파이널라이저에서 예외가 발생하면,
그 예외는 무시되고 그 객체의 파이널라이즈는 종결된다.
즉 불완전한 상황에 빠지기 매우 쉽다.
심지어 stacktrace 도 찍지 않는다.


-
파이널라이저를 사용하면 엄청난 성능 저하가 발생한다.
실례로 객체 하나를 생성하고 소멸시키는 시간은 약 5.6ns 인데, 파이널라이저를 추가하면 2,400ns 가 소요된다.


-
가급적 종료 메소드는 try-finally 와 함께 사용하여 확실하게 실행되도록 하자.
finally 는 예외가 생기더라도 항상 실행된다.


-
파이널라이저가 적합한 용도를 굳이 생각해내라면 두 가지가 있을 것이다.
1. 생성된 객체를 갖고 있는 코드에서 그 객체의 종료 메소드 호출을 빠드렸을 경우 "안전망" 역할.
2. 네이티브 객체를 release 할 때.


-
파이널라이저는 연쇄 호출이 자동으로 실행되지 않는다.
finally 코드 블럭에서 반드시 super.finalize() 를 호출해야 한다.


-
파이널라이즈 가디언(finalizer guardian) 을 이용하면 부주의하게 super.finalize() 를 부르지 않은 경우에 대비할 수 있다.

public class Foo{
    // Foo 객체가 GC 를 타면서 finalizerGuardian 객체도 GC 를 타며 finalize() 가 불린다.
    private final Object finalizerGuardian = new Object(){
        @Override
        protected void finalize() throws Throwable{
            ... // Foo 의 finalize 를 수행하는 코드를 넣는다.
        }
    }
}

-
Summary


종료 메소드 호출을 빼먹은 경우를 대비한 안전망 역할이나, 중요하지 않은 네이티브 자원을 종결하는 경우 외에는 파이널라이저를 사용하지 말자.


어쩔 수 없이 파이널라이저를 사용해야 하는 경우에는 super.finalize 를 호출하는 것을 잊지 말자.


안전망의 역할로 파이널라이저를 사용한다면, 부적절한 상황(자원 사용이 끝나지 않은 객체를 파이널라이즈 하는)을 메세지로 기록해야 한다는 것응 잊지 말자.


public 이고 final 이 아닌 클래스에 파이널라이저가 필요하다면, 파이널라이저 가디언의 사용을 고려하자. 서브 클래스의 파이널라이저에서 super.finalize 호출에 실패하더라도 파이널라이즈가 될 수 있기 때문이다.






반응형

댓글