-
Notifications
You must be signed in to change notification settings - Fork 2
Description
Chapter : 2. 객체 생성과 파괴
Item : 8. finalizer와 cleaner 사용을 피하라.
Assignee : eunpyeong114
🍑 서론
자바는 두 가지 객체 소멸자를 제공함
1) finalizer
: 예측할 수 없고, 상황에 따라 워험할 수 있어 일반적으로 불필요함
: 오동작, 낮은 성능, 이식성 문제의 원인이 됨
=> 기본적으로 쓰지 말아야 한다
2) cleaner
: finalizer보다는 덜 위험하지만, 여전히 예측할 수 없고, 느리고, 일반적으로 불필요하다.
⚡ C++에서의 파괴자
- 자바의 객체 소멸자와는 다른 개념!
- C++에서의 파괴자는 생성자의 꼭 필요한 대척점으로, 특정 객체와 관련된 자원을 회수하는 보편적인 방법이다.
↔ (자바에서는 가비지 컬렉터가 접근할 수 없게 된 객체를 회수하는 역할을 담당하고 있기 때문에, 프로그래머에게는 아무런 작업도 요구하지 않음!)- 비메모리 자원을 회수하는 용도로도 쓰인다.
↔ (자바에서는 try-with-resources와 try-finally를 사용해 해결)
🍑 본론
finalizer와 cleaner 사용을 피하라!!
🚀 사용을 지양해야 하는 이유?
-
finalizer와 cleaner로는 제때 실행되어야 하는 작업은 절대 할 수 없다.
finalizer나 cleaner를 얼마나 신속히 수행할지는 전적으로 가비지 컬렉터 알고리즘에 달렸으며, 이는 가비지 컬렉터 구현마다 천차만별이다. 이는 객체에 접근할 수 없게 된 후 finalizer나 cleaner가 실행되기까지 얼마나 걸릴지 알 수 없다라는 의미이다. 그렇기에 finalizer와 cleaner는 즉시 수행된다는 보장이 없다. -
수행 여부 또한 보장하지 않는다.
종료 작업을 전혀 수행하지 못한 채 중단될 수도 있다. 따라서 프로그램 생애주기와 상관없는 상태를 영구적으로 수정하는 작업에서는 사용해서는 안된다. -
예외 처리
finalizer 동작 중 발생한 예외는 무시되며, 처리할 작업이 남았더라도 그 순간 종료된다. 잡지 못한 예외로 인해 해당 객체는 마무리가 덜 된 상태로 남을 수 있다. -
성능 문제
가비지 컬렉터의 효율을 떨어뜨리고 안전망의 설치의 대가로 약 5배 정도 느려진다. -
보안 문제
finalizer는 생성자나 직렬화 과정에서 예외가 발생한다면 이 생성되다만 객체에서 악의적인 하위 클래스의 finalizer가 수행될 수 있게 된다. 또한 이 finalizer는 정적 필드에 자신의 참조를 할당해 가비지 컬렉터가 수집하지 못하게 막을 수 있다. finalizer를 final로 선언해 해결할 수 있다.
🚀 파일이나 스레드 등 종료해야 할 자원을 담고 있는 객체의 클래스에서 finalizer나 cleaner를 대신할 묘안은?
AutoCloseable을 구현해주고, 클라이언트에서 인스턴스를 다 쓰고 나면 close 메서드를 호출해주는 방식이 있다. 여기서 close 메서드에서는 이 객체는 더 이상 유효하지 않음을 필드에 기록하고, 다른 메서드는 이 필드를 검사해 객체가 닫힌 후 불렸다면 IllegalStateException을 던지면 된다.
🚀 Cleaner, Finalizer가 (아마도) 적절한 경우
-
자원의 소유자가 close 메서드를 호출하지 않는 것에 대한 대비망
즉시 호출된다고 보장은 없지만 자원 회수를 늦게라도 해주므로 안전망 역할이다. 예를 들어, FileInputStream, FileOutputStream, ThreadPoolExecutor가 존재한다. -
네이티브 피어와 연결된 객체에서의 사용
native peer란 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체를 말한다. 네이티브 피어는 자바 객체가 아니여서 가비지 컬렉터는 그 존재를 알지 못한다. 따라서 자바 피어를 회수할 때 네이티브 피어도 회수하지 못해서 cleaner나 finalizer가 나서서 처리할 수 있다.
다음은 cleaner를 이용해 안전망으로 사용하는 예제이다.
public class Room implements AutoCloseable{
private static final Cleaner cleaner = Cleaner.create();
//Room을 참조하면 순환으로 참조하기에 가비지 컬렉터의 대상이 되지 않으므로 Room을 참조해서는 안된다.
//청소가 필요한 자원, cleaner가 청소할 때 수거할 자원을 가진다.
private static class State implements Runnable{
int numJunkPiles; // 수거대
public State(final int numJunkPiles) {
this.numJunkPiles = numJunkPiles;
}
//close나 cleaner메소드가 호출한다.
@Override
public void run() {
System.out.println("방청소");
numJunkPiles = 0;
}
}
private final State state; //방 상태
private final Cleaner.Cleanable cleanable; //수거 대상이 된다면 방을 청소한다.
public Room(final int numJunkFiles) {
this.state = new State(numJunkFiles);
cleanable = cleaner.register(this, state);
}
@Override
public void close() throws Exception {
cleanable.clean();
}
}