종료자(finalizer)는 예측 불가능하며, 대체로 위험하고 일반적으로 불필요하다.
c++에서 소멸자는 메모리 이외의 자원을 반환하는 데도 사용되는데, 자바에서는 보통 try-finally 블록이 그런 용도로 사용된다.
종료자의 한가지 단점은 즉시 실행되리라는 보장이 전혀 없다는 것이다.
따라서 긴급한(time-critical) 작업을 종료자 안에서 처리하면 안된다.
예를 들면 종료자 안에서 파일을 닫도록 하면 치명적이다.
그 외, 주요정보, 동기화나 입출력 및 자원을 점유하고 반환해야 하는 곳에서 종료자를 사용하여 처리하면 메모리 타이밍이 맞질 않을 것 같다.
System.gc나 System.runFinalization 같은 함수에 마음이 흔들리면 곤란하다.
이런 함수들은 종료자가 실행될 가능성을 높여주긴 하는데 보장하진 않는다.
종료자를 보장하는 함수는 System.runFinalizersOnExit와 Runtime.runFinalizersOnExit 뿐인데 이 함수는 심각한 결함을 갖고 있어 폐기 되었다.
종료자를 사용하면, 프로그램 성능도 떨어진다 한다.
예를 들면 객체 삭제 시, 5.6ns 걸리는 것이 종료자를 붙이자 시간이 2,400ns로 늘어난다 한다.
파일이나 스레드처럼 명시적 반환하거나 삭제해야 하는 자원을 포함하는 객체의 클래스는 어떻게 작성해야 하는 걸까?
답은 명시적인 종료 함수를 하나 구현하고 더 이상 필요하지 않은 객체를 해당 함수에서 호출하게 해야한다.
이런 명시적 종료 함수를 예로는 OutputStream이나 InputStream, Java.sql.Connection에 정의된 close 함수가 있다.
책의 저자에 의하면 객체의 반환이 성능에 영향을 많이 미친다고 한다.
위에 종료 함수는 보통 try-finally 문과 함께 쓰인다. 객체 종료를 보장하기 위해서다.
1 2 3 4 5 6 7 8 9 | //try-finally 블록을 통해 종료 함수 실행 보장 Foo foo = new Foo(...); try { //실행 작업 ... }finally{ //종료 함수 호출 foo.terminate(); } | cs |
그럼 종료자는 정말로 써먹을 데가 없는 건 아니다.
적합한 곳이 두 군데 정도 있다.
1. 명시적 종료 함수 호출을 잊을 경우에 대비하는 안전망(종료자가 언제 호출될 지 알 수 없지만, 어쨋든 자원은 반환된다.)
2. 네이티브 피어(native peer)와 연결결된 객체를 다룰 때다.(네이티브 피어는 일반 자바 객체가 네이티브 함수를 통해 기능 수행을 위임하는 네이티브 객체를 말한다.)
만약 종료자를 써야 한다면. 자세하게 맞는 코드를 찾아 쓰는걸 추천한다.
자바7이상부터 try-with-resources를 지원한다고 한다.
사용법은 다음과 같다.
1 2 3 4 5 | try(SampleClass sampleClass = new SampleClass){ //작업 }catch(...) { ... } | cs |
C#에서 사용법은 다음과 같다.
1 2 3 4 5 | using(SampleClass sampleClass = new SampleClass) { //작업 ... } | cs |
예는 일반 클래스 객체로 들었지만 FileStream 같은 경우는 Close같은 종료를 자동으로 해준다 한다.
'Java' 카테고리의 다른 글
Effective Java #09 equals를 재정의할 때는 반드시 hashCode도 재정의하라 (0) | 2016.04.17 |
---|---|
Effective Java #08 equals를 재정의할 때는 일반 규약을 따르라 (0) | 2016.04.17 |
Effective Java #06 유효기간이 지난 객체 참조는 폐기하라 (0) | 2016.04.13 |
Effective Java #05 불필요한 객체는 만들지 말라 (0) | 2016.04.13 |
Effective Java #04 객체 생성을 막을 때는 private 생성자를 사용하라 (0) | 2016.04.13 |