2023-java-study / book-study

북 스터디 기록 레포지토리
0 stars 0 forks source link

[item 8] finalizer의 문제점 #17

Closed NuhGnod closed 1 year ago

NuhGnod commented 1 year ago

p. 41에서 "자바 언어 명세는 finalizer나 cleaner의 수행 시점 뿐 아니라 수행 여부조차 보장하지 않는다." 라는 문장에서 '수행 여부조차 보장하지 않는다 '-> 수행을 보장하지 않는다 라고 받아들였습니다.

Q. 왜 수행을 보장하지 않지?

혹시 이러한 이유 외에 수행 여부를 보장하지 않는다 의 이유가 무엇이 있을지 궁금합니다!

ssstopeun commented 1 year ago

저는 프로그램이 종료되기 전 finalize나 cleaner가 수행을 하는지 안하는지는프로그램의 종료에 영향을 끼치지 않는 다는 것으로 이해했습니다. 수행이 되지않고 프로그램이 종료되어도 보장하지 않겠다는 뜻으로요..

예시로 들었던 공유자원의 영구 락 해제에서 finalizer나 cleaner에 영구 락 해제를 하도록 해놓고 프로그램을 실행헸을 때 만약 finalizer쓰레드가 우선순위가 낮아 실행되지 않고 프로그램이 종료되는 경우가 생긴다면 책에서와 같이 시스템 전체가 서서히 멈추게 됩니다. 이러한 경우를 수행이 보장되지 않는다고 말한 것 같습니다.


찾아보니 객체의 참조가 끊겨 gc가 해당 객체를 회수할때 finalize() 메소드를 호출하게 되는데 해당 메소드가 override되어있는 경우 바로 실행되는 것이 아니라 해당 객체가 'Finalization Queue'에 쌓이게 되고 Finalizer쓰레드가 실행될때 이를 처리한다고 합니다. 이때 finalizer쓰레드의 우선순위가 낮은 것이 문제가 되는 것 같습니다. Finalization Queue에 쌓인 객체들은 finalizer쓰레드의 실행만 기다리게 되고 그렇게 해당 개체가 오랫동안 메모리를 점유하게 되면서 p.41의 동료 얘기의 Out Of Memory Exception같은 상황이 발생하게 된 것 같아요.

package Item8;

import static java.lang.Thread.sleep;

public class SimpleRunner {
    public static void main(String[] args) throws InterruptedException {

        for(int i = 0; i < 20; i++) {
            Finalize test = new Finalize(i);
            test = null; // 참조 해제
            System.gc(); // GC 호출
        }
        sleep(7);
    }
}

class Finalize {
    private int num;

    public Finalize(int num) {
        this.num = num;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println(num+ "번 finalize 수행");
    }
}

이렇게 작성을 했을때

image

이 처럼 수행되는 순서도 항상 다를 뿐더러 finalize가 수행되지 않고 프로그램이 종료되는 경우도 발생하였습니다.