1. finalizer와 cleaner
- 자바에서 제공하는 개체 소멸자이다.
finalizer
- 참조하지 않는 배열이나 객체를 Garbage Collector를 사용해 힙 영역에서 제거시킨다.
- Object 클래스에서 제공하는 기본 메서드이기 때문에 어느 클래스에서든지 finalize 메서드를 오버라이드 할 수 있다.
- 오동작, 낮은 성능, 이식성 등의 문제로 자바9에서 deprecated되었다. 웬만하면 사용하지 않아야한다.
cleaner란?
- finilizer 대안으로 등장했지만 이 역시 느리고, 보통 불필요하다.
2. finalizer와 cleaner의 단점
1)즉시 실행됨이 보장되지 않는다.
때문에 제때 실행되어야 하는 작업은 수행할 수 없다.
예를 들어 파일 닫기를 filnalizer/cleaner에 맡긴다면,
파일이 닫히지 않는 경우가 생길 수 있다.
또한 파일을 계속 열어둔다면 새로운 파일을 열지 못해 프로그램이 실패할 수 있다.
더군다나 filnalize는 다른 어플리케이션 스레드보다 우선순위가 낮아 자원회수가 지연된다.
또한 동작도중에 발생한 예외는 무시되고, 남은 작업은 수행하지 않고 종료된다.
-> 작업이 완료되지 않고 객체가 훼손된다. 다른 스레드가 이 객체를 사용하려하면 원래 의도와 다른 동작을 할 수 있다.
cleaner는 자신이 수행할 스레드를 제어할 수 있다는 측면에서는 finalizer보다 낫지만
이 역시 gc 통제하에 있어 즉각 수행될거라는 보장은 없다.
2) 수행 여부가 보장되지 않는다.
즉각 수행을 보장해주는 메서드도 등장했었지만(System.runFilizerOnExit, Runtime.runFinalizersOnExit)
심각한 결함때문에 잘 사용하지 않는다.
3) 느리다
finalizer와 cleaner는 가비지 컬텍터의 효율을 떨어뜨린다.
AutoClosable객체를 생성하는 것(현재까지 가장 좋은 자원 반납 방법)보다 50배나 느리다.
4) 보안문제
생성자나 직렬화 과정에서 예외가 발생하면,
생성되다 만 객체에서 악의적인 하위 클래스의 finalize가 수행될 수 있다.
solution) final class로 생성하거나 아무일도 하지 않는 finalize메서드를 만들고 final로 선언한다.
-> final클래스는 하위 클래스를 만들 수 없기 때문에!!
3. 대안) AutoClosable
- 정상적으로 자원을 반납하는 방법.
- AutoClosable을 구현하고, 클라이언트에서 인스턴스를 다 쓰고 close메서드를 호출한다.
- 예외가 발생해도 제대로 종료되도록 try-with-resources(아이템9)를 사용한다.
- 각 인스턴스가 자신이 닫혔는지 추적할 수 있도록 필드에 기록한다. 다른 메서드는 이 필드를 검사해 객체가 닫힌후에 close가 불렸다면 예외를 던진다.
4. 그렇다면 finalizer와 cleaner는 아예 쓸모가 없을까?
1) close메서드가 호출되지 않았을 때 늦게라도 자원을 반납하기 위해 사용할 수 있다.
AutoCloseable을 구현하지 않았을 경우를 대비한 안전망 역할로 사용할 수 있다.
ex: FileInputStream, FileOutputStream, ThreadPoolExecutor
[Java8 - FileInputStream.class]
public
class FileInputStream extends InputStream // InputStream은 Closable을 implements
{
...
public void close() throws IOException {
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
if (channel != null) {
channel.close();
}
fd.closeAll(new Closeable() {
public void close() throws IOException {
close0();
}
});
}
...
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
close();
}
}
...
}
2) 네이티브 피어(Native Peer)와 연결된 객체에서 사용할 수 있다.
- 네이티브 피어(Native Peer)란? 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체
- 네이티브 메소드(Native Method)란? 다른 언어로 작성된 코드를 자바에서 호출하도록 만들어진 규약
즉 자바 객체가 아니기 때문에, 가비지 컬렉터는 이 객체의 존재를 알지 못한다.
이럴 때 finalizer나 cleaner로 회수한다.
다만 성능저하가 발생하므로 이를 감당할 수 있어야 하고,
네이티브 피어가 심각한 자원을 가지고 있지 않을 때 해당한다.
'Java' 카테고리의 다른 글
[Effective Java] equals 재정의 일반 규약 (아이템10. equals는 일반 규약을 지켜 재정의하라) (0) | 2022.03.24 |
---|---|
[Effective Java] 아이템9. try-finally보다는 try-with-resources 사용하라 (0) | 2022.03.20 |
[Effective Java] 아이템7. 다 쓴 객체 참조를 해제하라 (0) | 2022.03.20 |
[Effective Java] 아이템6. 불필요한 객체 생성을 피하라 (0) | 2022.03.20 |
[Effective Java] 아이템5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) | 2022.03.14 |