[Effective Java] 종료자 사용을 피하라

종료자(finalizer)은 불필요하다

C나 C++ 사용자라면 소멸자를 많이들 사용한다. 다 사용한 자원들을 반환하기 위해서 사용하는데 java에서는 종료자(finalizer)의 사용을 피하는 것이 좋다. 종료자를 사용하면 시스템 오류, 성능 오류, 이식성 문제가 발생할 가능성이 크다. 또 종료자를 사용하면 프로그램의 성능이 급격하게 떨어진다. JVM은 모든 참조가 사라지기 전까지 종료자의 실행을 미루기 때문이다. 그래서 긴급하게 종료가 필요한 것을 종료자 안에 두면 언제 종료될지 모른다.

그럼 어떻게?

종료자 대신에 명시적인 종료 메소드를 추가하면 된다. 한 가지 명심해야할 점은 유효하지 않은 객체임을 표시하는 private 필드를 하나두고 맨 앞에서 유효한지 아닌지 검사한다. 이런 명시적인 종료 메소드는 try-finally문과 함께 쓰인다. 객체 종료를 보장하기 위해서이다.

Foo foo = new Foo(...);
try {
    // foo로 해야 하는 작업 수행
} fanally {
    foo.terminate();
}

정말 종료자는 불필요한가?

적당한 곳이 두 군데 있긴하다. 종료 메소드의 호출을 잊었을 경우에 종료자안에 넣어둔다. 종료자는 그런 자원을 발견하게 될 경우 반드시 경고 메시지를 로그로 남겨야 한다. 네이티브 피어(native peer)와 연결된 객체를 다룰 때다. 네이티브 피어는 일반 자바 객체가 네이티브 메서드를 통해 기능 수행을 위임하는 네이티브 객체를 말한다. 네이티브 피어는 일반 객체가 아니므로, 쓰레기 수집기가 알 수 없을 뿐더러 자바 측 피어 객체가 반환될 때 같이 반환할 수도 없다.

주의할 점

종료자 연결이 자동으로 이루어지지 않는다. 상속으로 클래스를 구현할 경우 하위 클래스의 종료자는 상위 클래스의 종료자를 명시적으로 호출해야 한다. 이때 하위 클래스의 상태는 try 블록 안에서 종료시켜야 하고, 상위 클래스 종료자는 finally블록 안에서 호출해야 한다. 하위 클래스에서 상위 클래스 종료자를 재정의 하면서 상위 클래스 종료자 호출을 잊으면 상위 클래스 종료자는 절대로 호출되지 않는다.