📌 요약
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.
}
}