📌 요약
finalizer와cleaner는 즉시 수행된다는 보장이 없으며, 실행되지 않을 수도 있다.
finalizer동작 중에 예외가 발생하면 정리 작업이 처리되지 않을 수도 있다.
finalizer와cleaner는 심각한 성능 문제가 있다.
finalizer는 보안 문제가 있다.반납할 자원이 있는 클래스는
AutoCloseable을 구현하고, 클라이언트에서close()를 호출하거나,try-with-resource를 사용해야 한다.
📌 Tip
Item 1 ~ 9 : 객체의 생성과 소멸에 관련된 내용
finalizer와 cleaner : 객체가 정리될 때 리소스를 제대로 반환하기 위한 의도로 있는 아이들
:
Object가 죽을 때 자원을 반납하기 위해 만들어짐
cleaner보다 위험함
finalizer는 public class가 아닌 package 레벨 : 접근이 불가능함
Java 9버전부터 안 쓰는 것을 권장하고 있음
@Deprecated(since="9")
protected void finalize() throws throwable ...
public class MyClass {
@Override
protected void finalize() throws Throwable {
System.out.println("");
}
}
while(true) {
new MyClass();
}
참고 가능한 ref code
www.baeldung.com/java-finalizer
직접 돌려보면 알게 되는데, finalizer의 queue를 처리하는 스레드의 우선순위가 낮음 → 권장되지 않는 이유!
finalize를 잘못 쓰면, 이 finalizer 안에서 다른 오브젝트를 참조하거나 자기 자신을 또 참조 또는 인스턴스를 만들어 쓰게 되면 오브젝트가 결국 또 다시 생기게 됨
:
public class BigObject {
private List<Object> resource;
public BigObject(List<Object> resource) { this.resource = resource }
public static class ResourceCleaner implements Runnable {
private List<Object> resourceToClean;
public ResourceCleaner(List<Object> resourceToClean) {
this.resourceToClean = resourceToClean;
}
@Override
public void run() {
resourceToClean = null;
System.out.println("cleaned up.");
}
}
}
public class CleanerIsNotGood {
public static void (String[] args) throws InterruptedException{
Cleaner cleaner = Cleaner.create();
List<Object> resourceToCleanUp = new ArrayList<>();
BigObject bigObject(bigObject, new BigObject.ResourceCleaner(resourceToCleanUp));
bigObject = null;
System.gc();
Thread.sleep(mills: 3000L);
}
}
안전망 : 사람들이 try resources를 쓰지 않더라도 gc가 될 때 제대로 되기 위해서
자원이 반납될 수 있는 기회를 가질 수 있도록
호출되리라는 보장은 없지만 그래도 기회를 준다.
import java.lang.ref.Cleaner;
public class SampleResource implements AutoCloseable{
private static final Cleaner cleaner = Cleaner.create();
private final Cleaner.Cleanable cleanable;
private final ResourceCleaner resourceCleaner;
public SampleResource(final int numJunkFiles) {
this.resourceCleaner = new ResourceCleaner(numJunkFiles);
cleanable = cleaner.register(this, state); // Runnable 객체를 등록
}
// Cleanable은 별도의 쓰레드로 clean을 함.
// SampleResource을 참조하면 순환참조가 되어버림.
// 서로를 계속 참조하기 때문에 gc에의해 수거되지 않는다.
// 정적클래스가 아니면 자동으로 바깥객체의 참조를 가짐.
private static class ResourceCleaner implements Runnable {
int numJunkPiles; // clean할 대상
public ResourceCleaner(final int numJunkPiles) {
this.numJunkPiles = numJunkPiles;
}
@Override
public void run() { // 1. close를 호출할 때, 2. cleaner(안전망)
System.out.println("방청소");
numJunkPiles = 0;
}
}
@Override
public void close() throws Exception {
cleanable.clean();
}
}
public class Room implements AutoCloseable {
private static final Cleaner cleaner = Cleaner.create();
// 청소가 필요한 자원. 절대 Room을 참조해서는 안 된다!
private static class State implements Runnable {
int numJunkPiles; // 방(Room) 안의 쓰레기 수
State(int numJunkPiles) {
this.numJunkPiles = numJunkPiles;
}
// close 메서드나 cleaner가 호출된다.
@Override public void run() {
Syste.out.println("방 청소");
numJunkPiles = 0;
}
}
// 방의 상태. cleannable과 공유한다.
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();
}
}
public class Adult {
public static void main(String[] args) {
try (Room myRoom = new Room(7)) {
System.out.println("안녕~");
}
}
}
public class Teenager {
public static void main(String[] args) {
new Room(99);
System.out.println("아무렴");
}
}
: 권장되는 사용법
public class AutoClosableIsGood implements AutoCloseable {
private BufferedInputStream inputStream;
@Override
public void close() {
try {
inputStream.close();
} catch (IOException e) {
throw new RuntimeException("failed to close " + inputStream);
}
}
}
public class App {
public static void main(String[] args) {
try(AutoCloseableIsGood good = new AutoClosableIsGood()) {
// TODO 자원 반납 처리가 됨
}
}
}
public class BrokenAccount extends Account {
public BrokenAccount () {
this.
}
}