IT/Programming / / 2023. 4. 27. 09:45

<Effective Java> RULE 7 종료자 사용을 피하라

반응형

 

종료자 사용은 예측 불가능 하며, 대체로 위험하고 일반적으로 불필요하다.

 시스템 오류, 성능 문제, 이식성 문제가 발생할 수 있다.

 JVM에서는 종료자가 즉시 실행되리라는 보장이 전혀 없다. 따라서 긴급한 작업을 종료자 안에서 처리하면 안된다.

 

종료자 실행 시점은 GC알고리즘에 좌우되는데 이 알고리즘은 JVM구현마다 크게 다르다.

 자바 명세에는 종료자가 즉시 실행되어야 한다는 문구도 없지만 종료자가 반드시 실행되어야한다는 문구도 없다.

 중요 상태 정보는 종료자로 갱신하면 안된다.

 

System.gc나 System.runFinalization들은 종료자가 실행될 가능성을 높여주긴 하지만 보장하진 않는다.

종료자 실행을 보장하는 메서드인 System.runFinalizersOnExit, Runtime.runFinalizersOnExit는 심각한 결함이 있어 폐기되었다.

 

종료자 안에서의 무점검 예외들은 스레드는 종료되고 스택 추적정보가 표시되지 않고 경고문구도 출력되지 않는다.

 

종료자를 사용하면 성능이 심각하게 떨어진다.

→ 본 컴퓨터에서는 약 430배가량 느려졌다.

 

그럼 어떻게 해야하나??

 명시적은 종료 메서드 하나 정의

 종료 여부 객체를 보관하여 유효하지 않은 객체임을 표시하도록

 종료된 객체를 호출하면 IllegalStateException이 던져지도록

 ex) OutputStream, InputStream, java.sql.Connection.close, java.util.Timer.cancel, java.awt.Graphics.dispose, java.awt.Window.dispose..

 

위와같은 명시적 종료는 try-finally와 함께 사용하여 객체 종료를 보장한다.

// try-finally블록 종료자
Foo foo = new Foo(..);
try{
    ...
} finally {
    foo.terminate(); //명시적 종료 메서드
}
 

종료자가 적합한 곳

1) 명시적 종료 메서드 호출을 잊을 경우에 대비하는 안전망의 역활

 자원의 반환이 늦진하겠지만, 클라이언트가 명시적 종료 메서드를 호출을 잊더라도 자원은 반환한다.

 

2) 네이티브 피어와 연결된 객체를 다룰 때.

 네이티브 피어 : 일반 자바 객체가 네이티브 메서드를 통해 기능수행을 위임하는 네이티브 객체를 말한다.

 네이티브 피어는 일반 객체가 아니므로 GC가 알수 없을 뿐더러 자바 측 피어객체가 반환될 때 같이 반환할 수도 없다.

 

주의)

 “종료자연결”이 자동으로 이뤄지지 않는다.

 만일 어떤 클래스가 종료자를 가지고 있고 하위 클래스에서 재정의한다면, 하위클래스종료자는 상위클래스 종료자를 명시적으로 호출해야한다.

 하위 클래스 상태는 try블록안에서 종료시켜야 하며 상위 클래스 종료자는 finally종료자안에서 호출해야 한다.

 하위클래스의 종료과정에서 예외가 발생하더라도 상위 클래스 종료자는 호출되도록 할 수 있다.

// 수동 종료자 연결
@Override
protected void finalize() throws Throwable{
    try{
        ..//하위 클래스 상태 종료
    } fianlly{
        super.finalize();
    }
}
 

주의)

재정의하면서 상위 클래스종료자 호출을 잊으면 상위클래스 종료자는 절대 호출되지 않는다.

방지하기 위해서 익명 클래스를 생성하여 모든 객체를 종료.

// 종료 보호자 숙어
public class Foo{
    // 이 객체는 바깥 객체 Foo를 종료시키는 역할만 한다.
    pricate final Object finalizerGuardian = new Object(){
        @Override 
        protected void finalize() throws Throwable {
            ...// 바깥 객체를 종료시킴
        }
    };
    ...
}
 

public 클래스 Foo에는 종료자가 없다는것에 유의하자.

따라서 하위 클래스에 종료자가 상위 클래스의 종료자를 호출하건 말건 상관 없다.

 

결론

 자원 반환에 대한 최종적 안전장치를 구현하거나 그다지 중요하지 않은 네이티브 자우너을 종료시키려는 것이 아니라면 종료자 사용을 피하라.

 굳이 종료자사용을 해야하는 드문 상황에 처했다면 super.finalize호출을 잊지 말자.

 하위 클래스 정의가 가능한 public 클래스에 종료자를 추가하는 상황이라면 하위 클래스에서 실수로 super.finalize호출을 잊어도 종료작업이 진행 될 수 있도록 종료 보호자 패턴을 도입하면 좋을지 고려하라.

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유