규칙7 종료자 사용을 피해라
![]() |
|
종료자(finalizer)는 예측 불가능하며, 대체로 위험하고, 일반적으로 불필요하다.
종료자가 뭔지 부터 알아보자.
public class object 에는 finalize() 가 정의되어 있고 모든 클래스는 override 해서 사용할 수 있다.
finalize 메소드는 garbage collection 되기 전에 호출되기 때문에 subclass 들은 시스템 리소스를 반환할때 finalize 메소드를 사용할 수 있다.
리소스를 사용하는 클래스에서 사용하면 편할 것 같은데 왜 사용하지 말라는지 읽어보자.
사용하면 안되는 이유 1) 즉시 실행되리라는 보장이 젼혀 없다.
=> JVM 은 finalizer 메소드를 천천히 실행 시킬 수 있다. 만약에 종료자에서 파일 리소스를 닫는 명령어를 사용한다면 JVM 이 종료자를 천천히 실행하기 때문에 유한한 파일 리소스 사용에 제한이 걸릴 수 있다.
사용하면 안되는 이유 2) 클래스에 종료자를 붙여 놓으면, 객체 메모리 반환이 지연될 수 있다.
=> 작가의 경험에 따르면 종료자 큐에 객체들이 반환되기를 기다리고 있는 상태에서 다른 응용프로그램 스레드 우선순위에 밀려서 OutOfMemoryError 가 발생하였다고 한다.
사용하면 안되는 이유 3) 성능이 심각하게 떨어진다
=> 작가의 컴퓨터에서 간단한 객체를 만들고 삭제하는 데는 5.6ns면 충분한데, 종료자를 붙이자 2,400ns 로 430배 가량 느려졌다고 한다.
그럼 종료자를 대체하는 방법에는 뭐가 있을까?
종료자를 대체하는 방법 1) 명시적인 종료 메서드(termination method)를 정의한다.
=> 선언한 종료 메서드를 클라이언트가 호출하도록 한다. 이 때 종료 여부를 객체 안에 private 필드로 보관해야 한다. 모든 메서드 앞에 종료 여부를 확인하는 코드를 두어 종료된 객체가 호출되면
IllegalStateException 을 던진다.
이런 명시적 종료 메서드는 try-finally 문과 함께 쓰인다. 객체 종료를 보장하기 위해서다
(자바 1.7 부터는 try-with-resources 문을 지원한다. 유용할 것 같으니 한번 찾아보자.)
그렇다면 종료자를 어디에 써먹어야 하는 것인가.
종료자 써먹을 곳 1) 명시적 종료 메서드 호출을 잊을 경우에 안전망으로서의 역할
=> 클라이언트가 명시적 종료 메서드를 호출하지 않은 경우 종료자를 이용해서 자원을 반환하면 보험 역할을 충분히 해 낼 수 있다. 하지만 이때 꼭 로그를 남겨서 클라이언트에서 명시적 종료 메서드를 호출 할 수 있도록 한다.
종료자 써먹을 곳2) 네이티브 피어(native peer)와 연결된 객체를 다룰 때