본문 바로가기
공부/JAVA

이펙티브 자바 2장 규칙 7. 종료자 사용을 피하라

by 미네밍 2021. 2. 17.

Effective Java 2장 객체의 생성과 삭제

규칙 7. 종료자 사용을 피하라


종료자(finalize)를 사용한다면?

  • 예측불가능하며 불필요함.

  • 종료자의 경우 수행되기 까지 시간이 걸릴 수 있기 때문에 되도록 긴급한 작업은 종료자 안에서 처리하지 않는 것이 좋음.

  • 종료자로 인해 객체 메모리 반환이 지연되는 경우도 생길 수 있음.

  • (ex) 파일을 종료자 안에서 닫을 때 치명적임. JVM은 종료자를 천천히 실행하므로 열린 상태의 파일이 많이 남아있을 수 있음.

  • 종료자를 사용하면 성능면에서도 심각하게 떨어질 수 있음.

  • 데이터베이스와 같은 공유자원에 대한 중요 상태 정보들을 종료자로 갱신하면 안됌.

대안

종료자보단, 명시적 종료메서드를 사용(객체 종료를 보장하기 위해 보통 try-catch문과 자주 쓰임.)

Foo foo = new Foo();
try{
   ...
} finally{
     foo.terminate(); // 명시적 종료메소드
}
  • 사용하지 않는 객체에 대해 클라이언트가 명시적 종료메소드를 호출하도록
  • 클래스 내 종료여부를 저장할 수 있는 변수를 두고, 모든 메소드에서 이를 체크해, 종료된 객체를 통해 메소드를 호출할 시 IllegalStateException이 던져지도록 처리

그럼에도, 종료자는 언제 사용하는 것이 좋을까?

1. 명시적 종료 메소드 호출을 잊을 경우에 대비한 안전망
  • 해당 자원을 발견하게 될 경우, 반드시 경고성 log를 남겨야 함. 코드 버그가 있는 것이므로.
2. 네이티브 피어와 연결된 객체를 다룰 경우

네이티브 피어 - 네이티브 메서드를 통해 기능 수행을 위임하는 네이티브 객체

  • 네이티브 피어의 경우, 일반 객체가 아니기 때문에 GC 가 알 수 없다. 자바 측 피어 객체가 반환될 때 함께 반환되기도 어렵기 때문에, 이 네이티브 피어가 중요자원을 점유하고 있지 않은 이상은 종료자를 통해 반환하는 것이 적합하다.
  • 즉시 종료해야 하는 자원을 포함할 경우엔 명시적 종료 메서드를 클래스에 추가하는 것이 필요함.

주의점

종료자 연결(finalizer chaining)
@Override protected void finalize() throws Throwable{
  try{
    .. // 하위클래스 종료자 호출.
  }finally{
        super.finalize();
  }
}
  • 하위클래스가 상위클래스의 종료자를 재정의 하는 상황
  • 하위클래스의 종료과정에서 예외가 발생해도, 상위클래스의 종료자를 반드시 호출하게끔 만들기 위함.
종료보호자 패턴
  • 종료되어야 하는 객체의 클래스 안에 종료자를 정의하는 대신, 익명클래스 안에 종료자 정의
public class Foo {
    // 이 객체의 경우, 바깥 객체를 종료시키는 역할만 함.
    private final Object finalizerGuardian = new Object(){
        @Override protected void finalize() throws Throwable(){
            ...// 바깥 Foo 객체 종료시킴.
        }
    }
}
  • 바깥 객체의 모든 참조가 사라지는 순간, 종료보호자의 종료자도 실행가능한 상태가 되어, 마치 Foo객체의 종료자인 것처럼 수행됌.

댓글