자바는 두 가지 객체 소멸자를 제공함
finalizer는 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요함, 다양한 문제의 원인이 되어서 쓰임새가 있긴 하지만 기본적으로 안 씀
cleaner가 대안으로 나왔지만 finalizer보다는 덜 위험하지만, 여전히 예측할 수 없고, 느리고, 일반적으로 불필요함
그리고 이 2개는 즉시 수행된다는 보장이 없음, 얼마나 걸릴지 몰라서 제때 실행되어야 하는 작업은 절대 할 수 없음(파일 닫기와 같이)
이는 전적으로 가비지 컬렉터 알고리즘에 달려있어 천차만별임
클래스에 finalizer를 달아두면 그 인스턴스의 자원 회수가 제멋대로 지연될 수 있음 , 어떤 스레드가 finalizer를 수행할지 명시되어 있지 않아서
결국 finalizer나 cleaner의 수행 시점뿐 아니라 수행 여부조차 보장을 못함, 접근할 수 없는 일부 객체에 딸린 종료 작업을 전혀 수행하지 못한 채 프로그램이 중단될 수 있는 것, 상태를 영구적으로 수정하는 작업에서는 finalizer나 cleaner에 의존해서는 안됨(데이터베이스 같은 공유 자원의 영구 락 해제등)
System.gc
,System.runFinalization
메서드가 있지만, 실행될 가능성을 높여줄 수 있으나 보장해주진 않음, 이를 보장해주는 메서드조차도 심각한 결함이 있음
finalizer동작 중 발생한 예외는 무시되며, 처리할 작업이 남았더라도 그 순간 종료됨, 잡지 못한 예외때문에 해당 객체는 자칫 마무리가 덜 된 상태로 남을 수 있음, 그리고 이런 훼손된 객체를 사용하려면 어떻게 동작할지 예측할 수 없음(Cleaner의 경우 스레드를 통제해서 이런 문제는 발생하지 않음)
finalizer와 cleaner는 심각한 성능 문제도 동반함, 가비지 컬렉터의 효율을 떨어뜨림
finalizer를 사용한 클래스는 finalizer 공격에 노출되어 심각한 보안 문제를 일으킬 수 있음, 생성자나 직렬화 과정에서 예외가 발생하며나, 이 생성되다 만 객체에서 악의적인 하위 클래스의 finalizer가 수행될 수 있게 됨, 그리고 정적 필드에 자신의 참조를 할당해 가비지 컬렉터가 수집하지 못하게 막을 수 있음
이렇게 일그러진 객체가 만들어지면 이 객체의 메서드를 호출해 애초에 허용되지 않았을 작업을 수행할 수 있음
객체 생성을 막으려면 생성자에서 예외를 던지는 것만으로 충분하지만, finalizer가 있다면 그렇지도 않음, final이 아닌 클래스를 finalizer 공격으로부터 방어하려면 아무 일도 하지 않는 finalize 메서드를 만들고 final로 선언해야함
이런 finalizer나 cleaner를 대신해줄 묘안은 AutoCloseable을 구현해주고, 클라이언트에서 인스턴스를 다 쓰고 나면 close 메서드를 호출하면 됨
close 메서드에서 이 객체는 더 이상 유효하지 않음을 필드에 기록하고, 다른 메서드는 이 필드를 검사해서 객체가 닫힌 후에 불렸다면 IllegalStateException을 던지는 것임
AutoCloseable?
이 AutoCloseable은 close 메서드를 통해서 close 처리를 자동으로 처리해주는 것임
기존에 자원을 해제하는 방식은 try-catch-finally 방식이었고 여기서 좀 더 향상된 방식인 try-with-resources를 통해서 try를 벗어나면 try 안에 선언된 객체의 close 메서드를 호출해 close한 상황을 명시할 수 없게 되었는데 하지만 그렇다고 무조건 모든 객체를 close 해주지 않음
그래서 AutoCloseable을 구현한 객체를 통해서 close 메서드를 통해서 자원을 처리할 수 있는 것임
아래와 같이 구현을 함으로써 알아서 close가 호출이 되는 것임
public class AutoCloseableExample {
public static void main(String[] args) {
try (CustomResource customResource = new CustomResource()){
customResource.doSomething();
} catch (Exception e){
e.printStackTrace();
}
}
}
public class CustomResource implements AutoCloseable {
public void doSomething(){
System.out.println("Do something ...");
}
@Override
public void close() throws Exception {
System.out.println("CustomResource Closed!");
}
}
그렇다면 이 finalizer와 cleaner에 쓰임새가 없는 것인가 생각할 수 있지만 그래도 어느정도의 쓰임새는 존재함
먼저 자원의 소유자가 close 메서드를 호출하지 않는 것에 대비한 안전망 역할임, finalizer나 cleaner가 즉시 호출되리라는 보장은 없지만, 클라이언트가 하지 않은 자원 회수를 늦더라도 해주는 것이 아예 안 하는 것보다 나음(대신 그런 값어치가 있는지 잘 생각해야함)
그러고 두 번째로는 네이티브 피어임, 네이티브 피어란 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체를 말함
네이티브 피어는 자바 객체가 아니니 가비지 컬렉터는 그 존재를 알지 못함, 그 결과 자바 피어를 회수할 때 네이티브 객체까지 회수하지 못함, 이때 finalizer나 cleaner가 나서서 처리를 함
단, 성능 저하를 감당할 수 있고, 네이티브 피어가 심각한 자원을 가지고 있지 않을 때에만 해당함, 성능 저하를 감당할 수 없거나 네이티브 피어가 사용하는 자원을 즉시 회수해야 한다면 close 메서드를 사용해야함
네이티브 피어?
네이티브 피어란 자바로 만든 것이 아닌 C,C++같은 언어로 작성된 것을 의미함, 그래서 네이티브 메서드는 이런 언어로 작성한 메서드고 네이티브 피어는 이런 상황에서 자바 객체가 네이티브 메서드를 통해 기능 수행을 위임한 상황을 말함
그래서 이 상황에서 메모리 할당과 가비지 컬렉터에서 처리를 하지 못함, 네이티브 메서드로 위임되면서 자원의 반납 여부를 알 수 없기 때문임
그래서 이런 경우 finalizer나 cleaner를 통해 객체 소멸을 할 수 있다고 한 것임