태그 보관물: garbage-collection

garbage-collection

Object.finalize ()를 재정의하는 것이 실제로 좋지 않습니까? 나는 그것이

재정의에 대한 주요 두 가지 주장 Object.finalize()은 다음과 같습니다.

  1. 언제 호출되는지 결정할 수 없습니다.

  2. 전혀 호출되지 않을 수 있습니다.

내가 이것을 올바르게 이해한다면, 나는 그것이 Object.finalize()그렇게 미워할만한 충분한 이유라고 생각하지 않습니다 .

  1. 객체를 할당 해제하기위한 적절한 시간이 개발자가 아닌시기를 결정하는 것은 VM 구현과 GC에 달려 있습니다. 언제 Object.finalize()부름을받는 것이 중요 합니까?

  2. 일반적으로, 내가 틀렸다면 나를 수정하십시오 Object.finalize().GC를 실행할 기회를 얻기 전에 응용 프로그램이 종료 된 때만 호출되지 않습니다. 그러나 응용 프로그램 프로세스가 종료되면 객체가 할당 해제됩니다. 그래서 Object.finalize()그것을 호출 할 필요가 없기 때문에 전화를받을하지 않았다. 개발자가 관심을 갖는 이유는 무엇입니까?

파일 핸들 및 연결과 같이 수동으로 닫아야하는 객체를 사용할 때마다 매우 실망합니다. 객체에 구현이 있는지 지속적으로 확인해야하며 close()과거에 어떤 시점에서 객체에 대한 호출을 놓친 것으로 확신합니다. 왜 close()구현을 넣어서 이러한 객체를 처리하기 위해 VM과 GC에 맡기는 것이 더 간단하고 안전하지 Object.finalize()않습니까?



답변

내 경험에는 재정의의 단 하나의 이유가 Object.finalize()있지만 매우 좋은 이유입니다 .

finalize()호출을 잊어 버린 경우 알려주 는 오류 로깅 코드를 배치합니다 close().

정적 분석은 사소한 사용 시나리오에서만 누락을 포착 할 수 있으며 다른 답변에 언급 된 컴파일러 경고에는 사소한 작업을 수행하기 위해 실제로 사용하지 않도록 설정해야하는 간단한 뷰가 있습니다. (알거나 알고있는 다른 프로그래머보다 훨씬 많은 경고가 활성화되어 있지만 어리석은 경고는 활성화되어 있지 않습니다.)

마무리는 리소스가 방해받지 않도록하는 좋은 메커니즘 인 것처럼 보이지만 대부분의 사람들은이를 완전히 잘못된 방식으로보고 있습니다. 즉, 대체 폴백 메커니즘, “두 번째 기회”보호 수단으로 생각합니다. 그들이 잊어 버린 자원을 처분함으로써 하루. 이것은 잘못되었습니다 . 주어진 일을 수행하는 한 가지 방법 만 있어야합니다. 항상 모든 것을 닫거나 마무리는 항상 모든 것을 닫습니다. 그러나 마무리는 신뢰할 수 없으므로 마무리는 불가능합니다.

그래서, 거기에 내가 부르는이 제도입니다 필수 처리를 하고, 프로그래머가 담당하는 규정 항상 명시 적으로 모든 닫는 구현 Closeable또는 AutoCloseable. (.은 try-와-자원 문 여전히 명시 적 폐쇄로 계산) 물론은, 프로그래머는, 그래서 마무리가 활동하기 시작하지만,의 잊을 수 없는 경우에는 마무리 발견한다 : 마술 오른쪽 결국 일을 할 것입니다 마법의 요정 등을 것을 close()호출되지 않은, 그것은 수행 하지(수학적 확실성으로) 그들이 너무 게 으르거나 마음이 결여 된 일을하기 위해 그것에 의존하는 n00b 프로그래머들이 있기 때문에 정확하게 그것을 호출하려고 시도하십시오. 따라서 강제 처리를 통해 최종화 close()가 호출되지 않은 것을 발견 하면 밝은 빨간색 오류 메시지를 기록하여 프로그래머에게 큰 뚱뚱한 모든 대문자를 사용하여 프로그래머에게 자신의 물건을 고칠 것을 지시합니다.

추가 이점으로 소문에 따르면 “JVM은 사소한 finalize () 메소드 (예 : Object 클래스에 정의 된 것과 같은 작업을 수행하지 않고 리턴하는 메소드)를 무시하므로 강제로 폐기하면 모든 마무리를 피할 수 있습니다. 다음 과 같이 메소드 를 코딩 하여 전체 시스템의 오버 헤드 ( 이 오버 헤드가 얼마나 끔찍한 지에 대한 정보는 alip의 답변 참조 ) finalize():

@Override
protected void finalize() throws Throwable
{
    if( Global.DEBUG && !closed )
    {
        Log.Error( "FORGOT TO CLOSE THIS!" );
    }
    //super.finalize(); see alip's comment on why this should not be invoked.
}

이것 뒤에 숨겨진 아이디어 는 컴파일 타임에 값을 알 수 Global.DEBUG있는 static final변수이므로 false컴파일러는 전체 if명령문에 대해 전혀 코드를 생성하지 않으므로 사소한 (빈) 종결자가됩니다. 클래스가 마치 파이널 라이저가없는 것처럼 취급됩니다. (C #에서는 이것이 멋진 #if DEBUG블록 으로 수행 되지만, 우리가 할 수있는 일은 뇌에서 추가 오버 헤드가있는 코드에서 명백한 단순성을 지불하는 Java입니다.)

필수 처분에 대한 자세한 내용, 닷넷에서의 자원 처분에 대한 추가 논의와 함께 여기에 : michael.gr : 강제 처분과 “처분 처분”가증


답변

파일 핸들 및 연결과 같이 수동으로 닫아야하는 객체를 사용할 때마다 매우 실망합니다. […] 왜 close()구현을 넣어서 이러한 객체를 처리하기 위해 VM과 GC에 그대로 두는 것이 더 간단하고 안전한 Object.finalize()가요?

파일 핸들 및 연결 ( Linux 및 POSIX 시스템의 파일 디스크립터 )은 매우 드문 자원이므로 일부 시스템에서는 256 개로 제한되거나 다른 시스템에서는 16384로 제한 될 수 있습니다 (setrlimit (2) 참조 ). 이러한 제한된 자원의 고갈을 피하기 위해 GC가 충분히 자주 (또는 적시에) 호출 될 것이라는 보장은 없습니다. 그리고 GC가 충분히 불려지지 않으면 (또는 마무리가 적시에 실행되지 않으면) 그 한계에 도달하게 될 것입니다.

결말은 JVM에서 “최선의 노력”입니다. 호출되지 않거나 상당히 늦게 호출 될 수 있습니다. 특히 RAM이 많거나 프로그램이 많은 개체를 할당하지 않은 경우 (또는 대부분 오래된 개체로 전달되기 전에 대부분의 개체가 죽는 경우) 복사 세대 GC에 의한 생성)의 경우 GC를 거의 호출 할 수 없으며 최종 결과가 자주 실행되지 않거나 전혀 실행되지 않을 수도 있습니다.

따라서 close가능한 경우 명시 적으로 파일 디스크립터를 파일 화하십시오. 누출을 두려워하는 경우 최종 조치를 기본 조치가 아닌 추가 조치로 사용하십시오.


답변

이 방법으로 문제를보십시오 : 당신은 (a) 정확하고 (그렇지 않으면 프로그램이 잘못되었습니다) (b) 필요한 (그렇지 않으면 코드가 너무 커서 더 많은 RAM이 필요하고 더 많은 사이클을 소비하는 코드 만 작성해야합니다 쓸모없는 것들, 이해하기위한 더 많은 노력, 그것을 유지하는데 더 많은 시간을 소비하는 등

이제 파이널 라이저에서 원하는 것이 무엇인지 고려하십시오. 하나 는 필요가있다. 이 경우 호출 여부를 알 수 없으므로 종료 자에 넣을 수 없습니다. 충분하지 않습니다. 또는 필요하지 않습니다-처음에는 쓰지 않아야합니다! 어느 쪽이든, 파이널 라이저에 넣는 것은 잘못된 선택입니다.

(주 해당 파일 스트림, 폐쇄와 같은 당신의 이름 예, 모양을 그들이 정말 필요하지,하지만 그들은 인 경우로. 그것은 당신이 시스템에 열린 파일 핸들의 한계를 칠 때까지, 당신은하지 않습니다의 통보 당신의 그러나이 제한은 운영 체제의 기능이므로 종료 자에 대한 JVM의 정책보다 예측할 수 없으므로 파일 핸들을 낭비하지 않는 것이 중요합니다.)


답변

파이널 라이저에 의존하지 않는 가장 큰 이유 중 하나는 파이널 라이저에서 정리하려고하는 대부분의 리소스가 매우 제한되어 있기 때문입니다. 가비지 컬렉터는 무언가를 릴리스 할 수 있는지 여부를 결정하는 참조를 순회하기 때문에 너무 자주 실행됩니다. 이것은 물체가 실제로 파괴되기 전에 ‘시간이 걸릴 수 있음’을 의미합니다. 예를 들어, 수명이 짧은 데이터베이스 연결을 여는 많은 오브젝트가있는 경우, 종료자를 사용하여 이러한 연결을 정리하면 가비지 콜렉터가 마지막으로 실행되고 완료된 연결이 해제 될 때까지 연결 풀이 소모 될 수 있습니다. 그런 다음 대기로 인해 대기중인 요청의 많은 백 로그가 발생하여 연결 풀이 빨리 소모됩니다. 그것’

또한 try-with-resources를 사용하면 완료시 ‘닫을 수있는’개체를 쉽게 닫을 수 있습니다. 이 구문에 익숙하지 않은 경우 https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html 을 확인하는 것이 좋습니다.


답변

리소스를 릴리스하기 위해 종료 자에게 맡기는 것이 일반적으로 나쁜 생각 인 이유 외에도, 완료 가능한 개체에는 성능 오버 헤드가 있습니다.

에서 자바 이론과 실습 : 쓰레기 수집 및 성능 (브라이언 게츠), 종료 자 당신의 친구되지 않습니다 :

종료자가있는 객체 (사소한 finalize () 메서드가있는 객체)는 종료자가없는 객체와 비교하여 상당한 오버 헤드가 있으므로 드물게 사용해야합니다. 최종화 가능 객체는 할당 속도가 느리고 수집 속도가 느립니다. 할당시 JVM은 완료 가능 오브젝트를 가비지 콜렉터에 등록해야하며 (최소한 HotSpot JVM 구현에서) 완료 가능 오브젝트는 대부분의 다른 오브젝트보다 느린 할당 경로를 따라야합니다. 마찬가지로, 최종화 가능한 객체도 수집 속도가 느립니다. 종료 가능한 객체를 회수하려면 가비지 수집주기 (최상의 경우)가 2 회 이상 필요하며 가비지 수집기는 마무리 작업을 호출하기 위해 추가 작업을 수행해야합니다. 결과적으로 객체를 할당하고 수집하는 데 더 많은 시간이 걸리고 가비지 수집기에 더 많은 압력이 가해집니다. 도달 할 수없는 최종 개체에 사용되는 메모리가 더 오래 유지되기 때문입니다. 이를 파이널 라이저가 예측 가능한 기간 또는 전혀 실행하지 않을 수 있다는 사실과 결합하면 최종화가 올바른 도구로 사용되는 상황이 상대적으로 적다는 것을 알 수 있습니다.


답변

내가 피하는 가장 좋아하는 이유 Object.finalize는 객체가 예상 한 후에 완성 될 수는 없지만 예상 하기 전에 완성 될 수 있기 때문입니다. 문제는 Java가 더 이상 도달 할 수 없다고 결정하면 범위가 종료되기 전에 여전히 범위에있는 오브젝트를 완료 할 수 없다는 것입니다.

void test() {
   HasFinalize myObject = ...;
   OutputStream os = myObject.stream;

   // myObject is no-longer reachable at this point, 
   // even though it is in scope. But objects are finalized
   // based on reachability.
   // And since finalization is on another thread, it 
   // could happen before or in the middle of the write .. 
   // closing the stream and causing much fun.
   os.write("Hello World");
}

자세한 내용은 이 질문 을 참조하십시오. 더 재미있는 점은 핫스팟 최적화가 시작된 후에 만이 결정을 내릴 수 있기 때문에 디버깅이 어렵다는 것입니다.


답변

객체에 close () 구현이 있는지 지속적으로 확인해야하며 과거에 어떤 시점에서 객체에 대한 호출을 놓친 것으로 확신합니다.

Eclipse에서 Closeable/ 구현하는 것을 닫는 것을 잊을 때마다 경고가 표시 AutoCloseable됩니다. 이것이 Eclipse 일인지 공식 컴파일러의 일부인지 확실하지 않지만 비슷한 정적 분석 도구를 사용하여 도움을 줄 수 있습니다. 예를 들어 FindBugs는 리소스를 닫는 것을 잊었는지 확인하는 데 도움이 될 수 있습니다.