finalizer와 cleaner의 사용을 피하라
자바가 제공하는 객체 소멸자
- 자바에서는 2가지 객체 소멸자를 제공한다.
finalizer
와cleaner
이다.- 그러나, 이 두 소멸자는 기본적으로 쓰지 말아야 한다.
이후에 나오지만
try-catch-with-resources
방식이 권장된다.
finalizer
와 cleaner
를 지양해야 하는 이유
- 가비지 컬렉터에 의해 실행이 결정되며, 즉시 실행된다는 보장은 없다.
- 객체에 접근하지 못하게 된 뒤로
finalizer
나cleaner
가 실행되는데 얼마나 소요되는지 알 수 없다.
- 객체에 접근하지 못하게 된 뒤로
finalizer
와 cleaner
가 즉시 실행된다는 보장이 없을 때 생기는 문제
- ex) 시스템이 동시에 열 수 있는 파일의 갯수는 한정되어 있다.
- 열었던 파일을 닫아주지 않으면, 더이상 새로운 파일을 열지 못한다.
finalizer
나cleaner
는 실행 시점이 불명확하기 때문에, 파일이 정말 닫혔는지 알 수 없다.- 이로 인해 많은 에러가 발생 가능하다.
자바는 심지어
finalizer
와cleaner
의 실행 여부조차 보장해주지 않는다.
즉, 실행이 안 될 수도 있다는 뜻이다.
상태를 영구적으로 수정하는 작업에서는 특히 절대로 finalizer
와 cleaner
에 의존해선 안된다.
- DB 공유 자원의 lock 해제와 같은 작업은 명시적인 시점에 분명하게 이루어져야 한다.
- lock이 제때 해제되지 않으면, 공유 자원을 필요로 하는 다른 작업들이 전부 lock이 풀리기만 기다리고 분산 시스템은 멈추게 될 것이다.
System.gc()
와 System.runFinalization()
메서드에 현혹되지 말자. 이 메서드들도 여전히 문제를 해결해주지 못한다.
finalizer
의 동작 특징에서 발생하는 또 다른 문제
finalizer
는 처리할 작업이 남았을 때에도 스레드를 종료시킨다.- 해당 객체는 마무리가 덜 된 상태로 남게 되고, 다른 스레드가 이 객체를 다시 사용하려 하면 어떤 동작이 나타날지 예측할 수 없다.
- 예외조차 출력되지 않으며 이상 현상이 발생할 수 있다.
cleaner
는 그나마 위와 같은 문제는 없다.
finalizer
와 cleaner
가 갖고오는 성능 문제
예제 코드를 돌렸을 때 리소스 회수에 각각 아래와 같은 시간이 걸렸다.
AutoClosable
구현 (try-with-resources
): 12nsfinalizer
: 550nscleaner
: 500ns
단
cleaner
와 같은 경우 직접 리소스 회수 외에 안전망 형태로만 사용할 수도 있는데, 이 경우엔 50ns로 상대적으로 매우 빠르다.
finalizer
가 갖고 오는 보안 문제
- 생성자나 직렬화 과정에서 예외가 발생했을 때, 생성되다만 객체에서 악의적인 하위 클래스의
finalizer
가 수행될 수 있다. finalizer
는 정적 필드에 자신의 참조를 할당하여, 가비지 콜렉터의 회수를 피해갈 수도 있다.- 객체 생성을 막을 때, 생성자에서 예외를 던지는 것만으로는
finalizer
보안 문제를 막을 수 없다.- 이 문제를 막으려면 아무일도 하지 않는
final
로 선언된finalizer
메서드를 생성해놓으면 된다.
- 이 문제를 막으려면 아무일도 하지 않는
final
클래스의 경우에는 하위 클래스를 만들 수 없기 때문에 이러한 문제에서 자유롭다.
fianlizer
와 cleaner
대신 AutoClosable
인터페이스를 구현하자
AutoClosable
인터페이스를 구현할 때는close()
메서드에try-with-resources
를 주로 사용한다.close()
메서드에서는 추가적으로 필드에 이 객체가 닫혔는지 기록해두는 것이 좋다.- 닫힌 객체에 접근하면,
IllegalStateException
을 던져주자.
- 닫힌 객체에 접근하면,
cleaner
와 finalizer
의 용도
- 일반적인 리소스 회수는
try-with-resources
를 통해AutoClosable
인터페이스를 구현하는 게 낫다고 배웠다. cleaner
와finalizer
는.close()
메서드를 호출하지 않았을 때를 대비해 안전망 역할로 제공할 수 있다.FileInputStream
,FileOutputStream
,ThreadPoolExecutor
가 이러한 방식을 사용하고 있다.
- 네이티브 피어와 연결된 객체를 회수할 때도
cleaner
와finalizer
를 사용할 수 있다.- 네이티브 피어란 네이티브 메서드를 통해 기능을 위임한 네이티브 객체인데 JVM에 의해 발견되지 않아 자동회수가 되지 않는다.
cleaner를 안전망으로 활용하는 예제 코드
public class Item8Test {
static class Room implements AutoCloseable {
private static final Cleaner cleaner = Cleaner.create();
private static class State implements Runnable {
int numJunkPile;
public State(int numJunkPile) {
this.numJunkPile = numJunkPile;
}
@Override
public void run() {
System.out.println("방 청소");
numJunkPile = 0;
}
}
private final State state;
private final Cleaner.Cleanable cleanable;
public Room(int numJunkPile) {
this.state = new State(numJunkPile);
cleanable = cleaner.register(this, state);
}
@Override
public void close() throws Exception {
cleanable.clean();
}
}
}
- 좀 더 현실성 있게 만들자면
int numJunkPile
이 아니라,final long nativePeer
와 같이 만들 수도 있다. - 위와 같이
Cleaner
를 넣어주면, 이Room
객체가phantom reachable
상태가 됐을 때,cleaning action
인State
내부run
메서드가 호출될 것이다.- 물론 명시적으로
.close()
를 호출하거나,try-with-resources
에 의해서 호출될 때도cleaning action
은 수행된다.
- 물론 명시적으로
관련 공식문서 링크를 참조하자.
Phantomly Reachable
상태에 대해 잘 모른다면 네이버의 기술 블로그 포스팅을 참고하는 것도 도움이 많이 될 것이다.
코드 테스트해보기
@Test
public void tryWithResourcesTest() {
try(Room room = new Room(100)) {
System.out.println("하이");
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void useCleaner() throws Exception {
Room room = new Room(100);
System.gc();
System.out.println("하이");
}
tryWithResourcesTest()
에서는"방 청소"
가 잘 출력되었다.useCleaner
는 어떻게든 출력해보려고System.gc()
도 이용해보았지만, 출력되지 않았다.- 자바 공식문서에 따르면, 인스턴스가
phantom reachable
되었을 때 호출된다고만 한다.
- 자바 공식문서에 따르면, 인스턴스가
정리
cleaner
(자바8까지는finalizer
)는 오직 네이티브 자원 회수 용도 혹은 안전망 역할로만 활용하자.- 대신
AutoCloseable
을 구현하고,try-with-resources
를 적극 활용하자.
'Java > 이펙티브 자바' 카테고리의 다른 글
이펙티브 자바, 쉽게 정리하기 - item 10. equals는 일반 규약을 지켜 재정의하라 (0) | 2021.12.29 |
---|---|
이펙티브 자바, 쉽게 정리하기 - item9. try-finally보다는 try-with-resources를 사용하라 (0) | 2021.12.28 |
이펙티브 자바, 쉽게 정리하기 - item7. 다 쓴 객체 참조를 해제하라 (0) | 2021.12.27 |
이펙티브 자바, 쉽게 정리하기 - item6. 불필요한 객체 생성을 피하라 (0) | 2021.12.26 |
이펙티브 자바, 쉽게 정리하기 - item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2021.12.25 |