종료자는 예측 불가능하며, 대체로 위험하고 일반적으로 불필요하다. 종료자를 사용하면 시스템 오류, 성능 문제, 이식성 문제가 발생할 수 있다. 종료자의 단점은 즉시 실행되리라는 보장이 전혀 없다는 것이다. 어떤 객체에 대한 모든 참조가 사라지고 나서 종료자가 실행되기까지는 긴 시간이 걸릴 수도 있다. 따라서 긴급한 작업을 종료자 안에서 처리하면 안된다. 예를 들어 종료자 안에 파일을 닫도록 하면 치명적이다.
종료자 실행 시점은 GC 알고리즘에 좌우되는데, 이 알고리즘은 JVM 마다 크게 다르다.
자바 명세에는 종료자가 즉시 실행되어야 한다는 문구도 없지만, 종료자가 결국에는 반드시실행되어야 한다는 문구도 없다. 따라서 종료자가실행되지 않은 객체가 남은 상태로 프로그램이 끝나게 되는 일도 충분히 가능하다. 그러므로 지속성이 보장되어야 하는 중요 상태 정보는 종료자로 갱신하면 안된다.
종료자를 사용하면 프로그램 성능이 심각하게 떨어진다. 간단한 객체를 만들고 삭제하는데 5.6ns가 충분한데, 종료자를 붙이면 2,400ns로 늘어났다. 430배 가량이 프로그램이 느려졌다.
파일이나 스레드처럼 명시적으로 반환하거나 삭제해야 하는 자원을 포함하는 객체의 클래스는 어떻게 작성해야 하는 것일까 ? 그냥 명시적인 종료 메서드를 하나 정의하고, 더 이상 필요하지 않는 객체라면 클라이언트가 해당 메서드를 호출하도록 하라. 명시적인 종료 메서드의 예로는 Connection, Stream의 close() 가 있다. 명시적 종료 메소드는 보통 try-finally 문과 함께 쓰인다. 객체 종료를 보장하기 위해서다. 객체 사용 과정에서 예외가 던져져도 종료 메서드가 실행되도록 만들 수가 있다.
주의할 것은 자바 1.7부터 try-with-resource문을 지원하는데 이 문법을 사용하면 finally 블록을 사용하지 않아도 된다.
종료자는 정말 써먹을 데가 없는가 ? 적합한 곳은 2 군데가 있다.
1. 명시적 종료 메서드 호출을 잊을 경우에 대비하는 안전망으로서의 역할.
2. 네이티브 피어와 연결된 객체를 다룰 때. 네이티브 피어는 일반 자바 객체가 네이티브 메서드를 통해 기능 수행을 위임하는 네이티브 객체를 말한다.
종료자를 사용할 때 주의할 것은 "종료자 연결"이 자동으로 이루어지지 않는다는 것이다. 어떤 크래스가 종료자를 갖고 있고 하위 클래스가헤당 메서드를 재정의 하는 경우, 하위 클래스의 종료자는 상위 클래스 종료자를 명시적으로 호출해야 한다. 상위 클래스의 종료자 호출을 잊으면 상위 클래스 종료자는 절대로 호출되지 않는다.
정리하자면 자원 반환에 대한 최종적 안전장치를 구현하거나, 그다지 중요하지 않은 네이티브 자원을 종료시키려는 것이 아니라면 종료자는 사용하지 않는 것이 좋다. 굳이 종료자를 사용해야 하는 드문 상황에 처했다면 super.finalize 호출은 잊으면 안된다. 하위 클래스 정의가 가능한 public 클래스에 종료자를 추가해야 하는 상황이라면, 하위 클래스에서 실수로 super.finalize 호출을 잊어도 종료 작업이 진행될 수 있도록 종료 보호자 패턴을 도입하면 좋을지 고려해 봐야 한다.
'Effective Java > 1장 객체의 생성과 삭제' 카테고리의 다른 글
Effective Java #6 유효기간이 지난 객체 참조는 폐기하라 (0) | 2018.10.20 |
---|---|
Effective Java #5 불필요한 객체는 만들지 마라 (0) | 2018.10.20 |
Effective Java #4 객체 생성을 막을 때는 private 생성자를 사용하라 (0) | 2018.10.18 |
Effective Java #3 private 생성자나 enum 자료형은 싱글턴 패턴을 따르도록 설계하라 (0) | 2018.10.18 |
Effective Java #2 생성자 인자가 많을 때는 빌더 패턴 적용을 고려하라 (0) | 2018.10.17 |