이번 포스팅은 이펙티브 자바 중 "finalize와 cleaner 사용을 피하라"입니다.

finalizer와 cleaner는 주로 gc가 일어날 때, 사용한 리소스를 close하기 위해서 사용됩니다.
만약 사용하고 있는 리소스를 반납하지 않는다면, 사용하지 않는 리소스가 메모리를 차지하고 있어 메모리 누수가 발생하게 될 것 입니다.


finalize란?

finalizer는 finalize 메소드를 오버라이드하여(Object 클래스에 있음) gc가 일어날 때 수행됩니다. 자바 9부턴 deprecated 되었고, cleaner를 사용하도록 권장하고 있습니다.

@Override //Object 클래스에 있는 것을 오버라이드
public void finalize() {
    // ...
}

cleaner란?

  • gc가 일어날 경우, 등록한 스레드에서 정의된 클린 작업을 수행합니다.
  • 자원 반납용 안정망으로 사용합니다.
    - PhantomReference를 사용합니다.
    - 호출되리라는 보장이 없지만 없는 것 보다는 나을 수 있는 느낌입니다.
public class BigObject {

    private List<Object> resource;

    public BigObject(List<Object> resource) {
        this.resource = resource;
    }

	// 내부 클래스를 만들고 Runnable을 구현한다.
    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.");
        }
    }
}
  • 네이티브 피어(native peer)와 연결된 객체에서 사용됩니다.
    • 네이티브 객체는 자바 객체가 아니므로 가비지 컬렉터가 그 존재를 알지 못하고,
      이 때문에 가비지 컬렉터가 네이티브 객체 자원을 회수하지 못합니다.
    • 이러한 상황을 방지하기 위해 finalizer나 cleaner가 사용됩니다.
    • 단, 성능 저하를 감당할 수 있거나 네이티브 피어가 심각한 자원을 가지고 있지 않을 때에만 해당합니다.

네이티브 피어란 ,
일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체를 말합니다.(자바 클래스 -> 네이티브 메소드 호출 -> 네이티브 객체 (네이티브 Peer))


finalzer와 cleaner 사용을 자제해야 하는 이유

1. 즉시 수행된다는 보장이 없다.

  • finalize나 cleaner에서 사용되는 레퍼런스 큐는 우선순위가 낮기
    때문에 즉시 수행된다는 보장이 없습니다.

2. 실행되지 않을 수도 있다.

  • 1번과 마찬가지의 이유입니다.

3. 동작 중에 예외가 발생하면 정리 작업이 처리되지 않을 수도 있다.

  • finalize에서 uncheked Exception이 발생한다면, finalize에 구현한 로직들은 무시됩니다.

4. 성능에 문제가 있다.

  • gc 과정에서 수행하기 때문에 성능 저하가 발생할 수 있습니다. gc의 포인트는 stop the world를 줄이기 위함인데, gc과정 중 finalize 메서드의 할 일이 늘어나면, 전체적인 성능 저하가 일어날 수 있습니다.

그렇다면 뭘 사용해야 할까?

반납할 자원이 있는 클래스는 AutoCloseable을 구현하고 클라이언트에서 close()를 호출하거나 try-with-resource를 사용해 자동으로 close될 수 있도록 해야합니다.


정리

  • finalizer와 cleaner 대신 AutoClosable과 try-with-resource를 사용하는 것이 좋을 것 같습니다.

reference

profile
집중

0개의 댓글

Powered by GraphCDN, the GraphQL CDN