[Effective Java] 아이템 8 : finalizer와 cleaner 사용을 피하라
2022. 3. 30. 10:41ㆍJava/Effective Java
728x90
반응형
자바의 객체 소멸자
- Finalizer : 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요, 그냥 쓰지 마세요.. 그냥 쓰지 말라고
- Cleaner : Finalizer 보다는 덜 위험하지만, 여저힌 위험하고, 불필요하다.
C++의 destructor와 Java의 finalizer & cleaner
파괴자는 특정 객체와 관련된 자원을 회수하는 보편적인 방법이고 자바의 소멸자들은 접근할 수 없게 된 객체를 회수하는 역할을 가비지 컬렉터가 담당한다.
자바에서는 try-with-resources와 try-finally를 사용해 해결한다.
왜 Finalizer와 Cleaner의 사용을 자제해야하나?
- Finalizer와 Cleaner는 즉시 수행된다는 보장이 전혀 없다.
- 위 두 가지로는 데때 실행되야 하는 작업은 절대 할 수 없다. 파일 닫기를 맡긴다면 중대한 오류를 야기할 수 있고, 동시에 실행 가능한 개수에 한계가 있기 때문이다.
- 위 두 가지의 속도는 전적으로 가비지 컬렉터가 담당하고 있다.
- 가비지 컬렉터도 어떻게 코드를 짜냐에 따라 속도가 천차만별이다.
- 수행 시점과 수행 여부조차 보장할 수 없다.
- 접근 할 수 없는 객체에 딸린 종료 작업을 전혀 수행하지 못한 채 프로그램이 중단될 가능성이 있다.
- 영구적으로 지속되어야 하는 작업에서는 절대 두 가지 소멸자를 사용하면 안된다.
- Finalizer는 예외는 무시되며, 처리할 작업이 남았더라도 순간 종료된다. 마무리가 덜 된 상태에 놓이게 된다.
- 이런 일이라도 Finalizer는 경고 조차 출력하지 않지만, Cleaner는 자신의 스레드를 통제하기 때문에 이런 일이 없다.
- 심각한 성능 문제도 동반한다.
- AutoCloseable 객체보다 약 50배나 느리게 작동한다. 왜냐하면 가비지 컬렉터의 성능을 저 두 소멸자가 떨어뜨리기 때문이다.
- Finalizer를 사용한 클래스는 공격에 노출될 수 있다.
- 생성자나 직렬화 과정에서 예외가 발생되면, 악의적인 하위 클래스의 finalizer가 수행될 수 있게 된다. 이는 정적 필드에 자신의 참조를 할당하여 가비지 컬렉터의 수집을 막게되는 현상이다.
- 이를 막기위해 아무 일도 하지 않는 Finalize 메서드를 만들고 Final로 선언하자.
대안책은?
AutoCloseable을 구현하, 다 쓴 인스턴스는 close 메소드를 호출하자
만약 Cleaner를 사용해야 한다면 IllegalStateException 예외를 던져주자
쓰면 안되면 어디에 써야 하는 소멸자들인가?
- 자원의 소유자가 close 메소드를 호출하지 않는 것에 대비한 안전망
- 네이티브 피어와 연결된 객체인데 네이티브 피어는 가비지 컬렉터와 연결되지 않았기 때문이다. 이를 회수하기 위해서는 close 메소드를 사용하자.
예제
import java.lang.ref.Cleaner;
public class Room implements AutoCloseable {
private static final Cleaner cleaner = Cleaner.create();
private static class State implements Runnable {
int numJunkpiles;
State(int numJunkpiles) {
this.numJunkpiles = numJunkpiles;
}
@Override
public void run() {
System.out.println("방 청소");
numJunkpiles = 0;
}
}
private final State state;
private final Cleaner.Cleanable cleanable;
public Room(int numJunkpiles) {
state = new State(numJunkpiles);
cleanable = cleaner.register(this, state);
}
@Override
public void close() throws Exception {
cleanable.clean();
}
}
책의 예제이다.
이 예제 코드에서는 절대 State 클래스가 Room을 참조하면 안된다.
public calss Adult {
public static vodi main(String[] args) {
try (Room myRoom = new Room(7)) {
System.out.println("안녕~");
}
}
}
결과로 ''안녕~''이 출력된다
public calss Teenager {
public static vodi main(String[] args) {
new Room(99);
System.out.println("아무렴");
}
}
결과로 방 청소가 먼저 출력되고 아무렴이 출력된다. Room 메소드 안에 보면 state를 선언할 때 run() 메소드에서 방 청소를 먼저 실행하기에 가능한 일이다.
728x90
반응형
'Java > Effective Java' 카테고리의 다른 글
[Effective Java] 아이템 9 : try-finally 보다는 try-with-resources를 사용하라 (0) | 2022.03.31 |
---|---|
[Effective Java] 아이템 7 : 다 쓴 객체 참조를 해제하라 (0) | 2022.03.16 |
[Effective Java] 아이템 6 : 불필요한 객체 생성을 피하라 (0) | 2022.03.16 |
[Effective Java] 아이템 5 : 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2022.03.15 |
[Effective Java] 아이템 4 : 인스턴스화를 막으려거든 Private 생성자를 사용해라 (0) | 2022.03.14 |