자바는 두 가지 객체 소멸자를 제공한다 finailzer , cleaner
- finailzer는 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요하다
- 자바 9 에서는 finalizer 사용 자제 API로 지정하고 cleaner를 대안으로 소개하고 있다
- cleaner는 finalizer보다는 덜 위험하지만, 여전히 예측할 수 없고, 느리고, 일 반 적으로 불필요하다
- 프로그램 생애주기와 상관없는, 상태를 영구적으로 수정하는 작업에서는 절대 finalizer나 cleaner에 의존하면 안 된다
cleaner와 finalizer 적절한 쓰임새 2가지 방법
- 1. 자원 소유자가 close 메서드를 호출하지 않는 것에 대비한 안전망 역할
- cleaner나 finalizer가 즉시 호출되리라는 보장은 없지만, 클라이언트가 하지 않은 자원회수를 늦게라도 해주는 것이 아예 안 하는 것보다 낫다
- 2. 네이티브 피어와 연결된 객체에서 활용하는 것
- 네이티브 피어란 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체를 말한다
- 자바 객체가 아니니 가비지 컬렉터는 그 존재를 알지 못한다.
- 단, 성능 저하를 감당할 수 있고 네이티브 피어가 심각한 자원을 가지고 있지 않을 때에만 해당한다
코드 8-1 cleaner를 안전망으로 활용하는 AutoCloseable클래스
// 코드 8-1 cleaner를 안전망으로 활용하는 AutoCloseable 클래스 (44쪽)
public class Room implements AutoCloseable {
private static final Cleaner cleaner = Cleaner.create();
// 청소가 필요한 자원. 절대 Room을 참조해서는 안 된다!
private static class State implements Runnable {
int numJunkPiles; // Number of junk piles in this room
State(int numJunkPiles) {
this.numJunkPiles = numJunkPiles;
}
// close 메서드나 cleaner가 호출한다.
@Override public void run() {
System.out.println("Cleaning room");
numJunkPiles = 0;
}
}
// 방의 상태. cleanable과 공유한다.
private final State state;
// cleanable 객체. 수거 대상이 되면 방을 청소한다.
private final Cleaner.Cleanable cleanable;
public Room(int numJunkPiles) {
state = new State(numJunkPiles);
cleanable = cleaner.register(this, state);
}
@Override public void close() {
cleanable.clean();
}
}
- static으로 선언된 중첩 클래스인 State는 cleaner가 방을 청소할 때 수거할 자원을 담고 있다
- 쓰레기 수 : numJunkPiles -> 수거할 자원
- State는 Runnable을 구현하고, 그 안의 run 메서드는 cleanable에 의해 딱 한 번만 호출될 것이다.
- cleanable 객체는 Room 생성자에서 cleaner에 Room과 State를 등록할 때 얻는다
- run 메서드가 호출되는 상황은 2가지이다
- Room의 close 메서드를 호출할 때 close 메서드에서 Cleanable의 clean을 호출한다
- 가비지 컬렉터가 Room을 회수할때까지 클라이언트가 close를 호출하지 않는다면,cleaner가 State의 run 메서드호출
- State 인스턴스는 '절대로' Room 인스턴스를 참조해서는 안된다
- Room 인스턴스를 참조할 경우 순환 참조가 생겨 가비지 컬렉터가 Room 인스턴스를 회수해갈 기회가 오지 않는다
- State가 정적 중첩 클래스다 , 정적이 아닌 중첩 클래스는 자동으로 바깥 객체의 참조를 갖게 된다
8-2 모든 Room 생성을 try-with-resources 블록으로 감싼다
// cleaner 안전망을 갖춘 자원을 제대로 활용하는 클라이언트 (45쪽)
public class Adult {
public static void main(String[] args) {
try (Room myRoom = new Room(7)) {
System.out.println("안녕~");
}
}
}
- 자동 청소는 전혀 필요하지 않다
8-3 결코 방을 청소를 하지 않는 프로그램
// cleaner 안전망을 갖춘 자원을 제대로 활용하지 못하는 클라이언트 (45쪽)
public class Teenager {
public static void main(String[] args) {
new Room(99);
System.out.println("Peace out");
// 다음 줄의 주석을 해제한 후 동작을 다시 확인해보자.
// 단, 가비지 컬렉러를 강제로 호출하는 이런 방식에 의존해서는 절대 안 된다!
// System.gc();
}
}
cleaner(자바 8까지는 finalizer) 안전망 역할이나 중요하지 않은 네이티브 자원 회수 용으로만 사용하자. 물론 이런 경우라도 불확실성과 성능 저하에 주의해야 한다.
'Books > Effective-Java 3판' 카테고리의 다른 글
10.equals는 일반 규약을 지켜 재정의하라(1) (0) | 2021.09.28 |
---|---|
9.try-finally보다는 try-with-resources를 사용하라 (0) | 2021.09.27 |
7.다 쓴 객체 참조를 해제하라 (0) | 2021.09.24 |
6.불필요한 객체 생성을 피하라 (0) | 2021.09.23 |
5.자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2021.09.18 |