finalizer와 cleaner는 즉시 수행된다는 보장이 없다.
public class FinalizeObject {

    @Override
    protected void finalize() throws Throwable {
        System.out.print("");
    }
}

public class FinalizeExample {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        int i = 0;
        while (true) {
            i++;

            new FinalizeObject();

            if ((i % 1_000_000) == 0) {
                Class<?> finalizerClass = Class.forName("java.lang.ref.Finalizer");
                Field queue = finalizerClass.getDeclaredField("queue");
                queue.setAccessible(true);
                ReferenceQueue<Object> referenceQueue = (ReferenceQueue) queue.get(null);

                Field queueLength = ReferenceQueue.class.getDeclaredField("queueLength");
                queueLength.setAccessible(true);
                long length = (long) queueLength.get(referenceQueue);
                System.out.format("There are %d references in the queue\n", length);
            }
        }
    }
}
  • finalizer 동작 중에 예외가 발생하면 정리 작업이 처리되지 않을 수 있다.
  • finalizer는 보안 문제가 있다.. (구현체의 finalize 메서드를 통해 부모 객체의 메서드를 호출)
  • Deprecated since Java 9

 

public class CleanerObject {

    private List<String> resources;

    public CleanerObject(List<String> resources) {
        this.resources = resources;
    }

    public static class ResourceCleaner implements Runnable {

        private List<String> cleanableResources;

        public ResourceCleaner(List<String> cleanableResources) {
            this.cleanableResources = cleanableResources;
        }

        @Override
        public void run() {
            cleanableResources = null;
            System.out.println("Clean the resources");
        }
    }
}

public class CleanerExample {

    public static void main(String[] args) throws InterruptedException {
        Cleaner cleaner = Cleaner.create();

        List<String> resources = new ArrayList<>();
        CleanerObject cleanerObject = new CleanerObject(resources);
        cleaner.register(cleanerObject, new CleanerObject.ResourceCleaner(resources));

        cleanerObject = null;
        System.gc();
        Thread.sleep(3000L);
    }
}

Clean을 해주는 Runnable 구현체에서 정리하려는 객체를 참조해서는 안된다.

 

반납할 자원이 있는 클래스는 AutoCloseable을 구현하고 close()를 호출하거나 try-with-resource를 사용
public class CloseableObject implements AutoCloseable{

    // try-with-resources에서 실행됨
    @Override
    public void close() {
        System.out.println("Clean the resources");
    }
}

public class CloseableExample {

    public static void main(String[] args) {
        try(CloseableObject closeableObject = new CloseableObject()) {
            // 자원 반납 처리 됨
        }
    }
}

 

출처 : 백기선 이펙트브 자바 강의

+ Recent posts