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

[Design Pattern/Java] finalizer ( 파이널라이저 ) 사용을 피하자.

by 돼지왕 왕돼지 2012. 2. 13.
반응형


안녕하세요 돼지왕왕돼지입니다.
오늘은 finalizer (파이널라이저) 사용을 피하자. 라는 주제로 이야기를 해볼까 합니다.
이 글은 "Effective Java" 를 기반으로 작성하였습니다.


Finalizer 왜 피해야 하는가?


- Finalizer 는 예측 불가에다가 위험하기도 하며 일반적으로 불필요하다. 파이널라이저를 사용하면 예측이 어려운 프로그램 실행과 성능 저하 및 이식성의 문제가 생길 수 있다.



파이널라이저의 문제점


- 신속하게 실행된다는 보장이 없다. 
  객체가 사용할 수 없게 되는 시점부터 파이널라이저가 실행되는 시점까지는 긴 시간이 소요될 수 있다.
  즉! 실행시간이 중요한 작업을 "절대!" 하지 말아야 한다는 것. ( ex) finalizer 에서 파일 닫기 )

- Finalizer 의 수행 속도와 수행 타이밍은 G.C. Algorithm 에 따라 다르다.
  따라서 클래스에 파이널라이저를 사용하면 간혹 인스턴스들의 메모리 회수와 재활용이 지연될 수 있고, OutOfMemoryError 가 생기기도 한다.

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

그렇다고 이런걸 쓰지 말아라.


- System.gc 와 System.runFinalization 메소드들을 사용하지 말자. 
  이 메소드들은 파이널라이저가 실행될 가능성을 높여줄 뿐, 반드시 실행됨을 보장하지는 못한다.

- Finalize 를 보장하는 메소드들은 System.runFinalizersOnExit 와 Runtime.runFinalizersOnExit 가 유일한데,
  치명적인 결함이 발견되어 현재는 사용이 금지되었다.



Finalizer 의 문제는 이것뿐일까?


- Finalizer 에서는 Exception 도 무시해버린다. 심지어 경고조차 출력하지 않는다.

- Finalizer 를 사용하면 엄청난 성능 저하가 발생한다. ( 無 : 5.6ns, 有 : 2,400ns. )



그럼 Finalizer 대신 무엇을 이용해야 할까?

- 작업이나 자원을 정상적으로 종료하는 메소드를 추가하고 수행시켜준다. ( ex) Cursor.close, InputStream.close )
  한 가지 고려할 것은 종료 여부를 유지 관리해주어야 한다는 것. 유효하지 않음을 private 필드로 기록하고,
  종료된 이후에 호출이 되면 IllegalStateException 을 반환해야 한다.

- 위의 종료 메소드들은 보통 try{} finally {} 구문을 통해 finally 에서 처리해주는 것이 좋다.



그럼 Finalizer 는 "무조건" 쓰지 말아야 하나?



- 다음과 같은 경우에서는 쓸 "수" 는 있다.

 1. 생성된 객체를 갖고 있는 코드에서 그 객체의 종료 메소드 호출을 빠뜨렸을 경우의 "안전망"으로서의 역할
   늦게라도 호출되어 자원을 해지하는 것이 안 하는 것보다는 낫기 때문에.... 추가로 메세지를 출력하면 좋다. ( 경고 )

 2. Native Peer ( 네이티브 피어 ) 와 관련된 작업 수행시..
   네이티브 메소드를 통해 일반 자바 객체가 자신의 일을 위임하는 네이티브 객체가 네이티브 피어인데..
   일반 자바 객체가 아니므로, 그것이 소멸되었을 때 G.C. 를 해야하는지 모른다.
   이 때 finalizer 에서 처리해주며, 이 때!!! 마찬가지로 중요한 자원을 갖지 않고, 타이밍 문제가 없을 때 사용한다.



Finalizer 쓸 때 추가적인 주의사항은 없나?


- 있다. Finalizer 의 Chaining 은 자동으로 실행되지 않는다.
   어떤 클래스가 파이널라이저 메소드를 갖고 있고, 서브 클래스에서 그 메소드를 오버라이드 한다면, 서브 클래스의 파이널라이저에서 슈퍼 클래스의 파이널라이저를 반드시 호출해주어야 한다. 아래와 같이..

 @Override
 protected void finalize() throws Throwable{
      try{
         ... // finalize code들.
      }
      finally{
         super.finalize();
      }
 }

- 위의 방법도 좋지만, 실수로 빼먹을 가능성이 높다. 
  이 때를 대비해 Finalizer Guardian ( 파이널라이저 가디언 ) 을 쓰는 것이 좋다.

public class Foo{
      private final Object finalizerGuardian = new Object(){
          @Override
          protected void finalize() throws Throwable{
              ... // finalize 코드
          }
      }
}


객체가 소멸될때 자동 finalize 를 수행하기 때문에 훨씬 안전하다.


로그인 없이 추천 가능합니다. 손가락 꾸욱~

반응형

댓글