24

유효 자바 말 :왜 finalizers는 "심각한 성능 저하"를 겪고 있습니까?

파이널 라이저 사용시 심각한 성능 저하가 있습니다.

왜 종결자를 사용하여 개체를 파괴하는 것이 더 느립니까?

+1

finalizer가 객체를 다시 연결할 수있게 만드는 방법 등에 대해 이야기 할 수 있습니다. 또한 구성에 따라 구현 상속 대신 일을 절약 할 수있는 이유도 확인할 수 있습니다. http : //java.sun. com/developer/technicalArticles/javase/finalization/ – SyntaxT3rr0r

답변

22

가비지 수집기가 작동하는 방식 때문입니다. 성능을 위해 대부분의 Java GC는 수명이 짧은 객체가 "eden"메모리 블록에 할당되는 복사 수집기를 사용하며 객체 생성을위한 시간이되면 GC는 객체를 복사해야합니다. 더 영구적 인 저장 공간에 여전히 "살아있다"고하고, 그 다음에는 "eden"메모리 블록 전체를 한 번에 지울 수 있습니다. 이는 대부분의 Java 코드가 수명이 수천 초에 이르는 수천 개의 객체 인스턴스 (박스형 프리미티브, 임시 배열 등)를 생성하므로 효율적입니다.

하지만 믹서에 파이널 라이저가있는 경우 GC는 전체 세대를 한 번에 지울 수 없습니다. 대신, 최종 생성이 필요한 해당 세대의 모든 개체를 파악하고 실제로 종료자를 실행하는 스레드에 대기열에 넣어야합니다. 그 동안 GC는 개체를 효율적으로 정리할 수 없습니다. 그래서 그것들은 그들이해야 할 것보다 오랫동안 살아 있어야하거나, 다른 물체를 수집하는 것을 지연해야합니다. 게다가 실제로 finalizers를 실행하는 임의의 대기 시간이 있습니다.

이러한 모든 요소는 중요한 런타임 패널티를 가중시킵니다. 따라서 결정적으로 파이널 라이즈 (결정적으로 객체 상태를 명시 적으로 마무리하기 위해 close() 메소드 또는 유사한 방법을 사용)가 선호됩니다.

+1

물론 이것은 세대 별 수집가와 관련된 문제에 초점을두고 있습니다. 다른 GC 전략에는 다른 문제점이 있습니다. 그러나 그들은 자유롭게하기 위해 물체를 가로 지르는 최소한 두 번 통과하는 것을 포함하여 여분의 작업을 수행 할 필요가있는 GC로 내려갑니다. 하나는 finalize 큐에 추가하고, 하나는 finalization 후에 실제로 해제합니다. –

+0

일반적으로 사용되는 여러 Java API 클래스에 O/S 리소스를 확보하기위한 finalizer가 있다고 생각하는 것이 맞습니까? 나는'FileOutputStream'을 생각하고있다. 따라서 대부분의 프로그램이 영향을 받기 때문에 일부 객체의 finalizer가 finalizer를 사용하지 않는 객체의 GC를 지연시키는 것은 거의 없습니다. – Raedwald

+1

@ 레이드 월드 : 그렇습니다. 예를 들어,'FileOutputStream'의 OpenJDK 구현체는 OpenJDK 소스를보고 파이널 라이저를 가지고 있습니다. (그러나 finalizer를 사용하려면 * 표준 라이브러리 구현에 필요한 것을 찾을 수 없습니다.) 따라서 실제로는 GC에 적합하지만 아직 완성 대기중인 객체는 다음 이전 세대로 승격됩니다 (생존자 공간 또는 재택 근무자). 그러나 다음 번 세대가 수집 될 때까지 실제 메모리는 회수되지 않습니다. –

1

내 생각은 입니다. Java는 가비지 수집 언어로서 자체 내부 알고리즘을 기반으로 메모리를 할당 해제합니다. 가끔씩 GC는 힙을 검색하고, 더 이상 참조되지 않는 객체를 결정하고, 메모리 할당을 해제합니다. finalizer가이 작업을 중단하고 GC주기 외부에서 메모리 할당을 강제로 취소하여 잠재적으로 비 효율성을 초래합니다. 최선의 방법은 파일 핸들을 해제하거나 결정적으로 수행해야하는 DB 연결을 종료하는 것과 같이 절대적으로 필요한 경우에만 finalizer를 사용하는 것입니다.

+1

정말 강제적입니까, 아니면 단순히 제안합니까? – corsiKa

+0

대부분 그렇지만 finalizer는 GC주기 밖에서 메모리 할당 해제를 일으키지 않습니다. 대신 GC가 객체를 마무리해야한다고 판단하면 객체를 "부활"하고 종료자가 객체를 실행할 때까지 객체가 수집되지 않도록합니다. 그러나 (IIRC) finalizers는 다음에 tenured 세대가 수집 될 때까지 실행되지 않기 때문에 시간이 걸릴 수 있습니다. –

+1

"파이널 라이저가 파일 핸들을 해제하거나 DB 연결을 종료하는 것과 같이 꼭 필요한 경우에만 파이널 라이저를 사용하는 것이 베스트 프랙티스라고 생각합니다. 파이널 라이저가 임의로 늦게 실행되거나 전혀 실행되지 않을 수 있기 때문에 finalizer가 적합하지 않은 곳입니다. – sleske

0

내가 생각할 수있는 한 가지 이유는 리소스가 모든 Java 객체이고 원시 코드가 아니라면 명시적인 메모리 정리가 필요 없다는 것입니다.

1

필자는 자신의 책상에서 실용적인 Java를 사용하여 자신이 무엇을 언급하는지 확인했습니다.

2 장 6 절을 읽으면 다양한 성능 히트에 대해 자세히 설명합니다.

You can't know when the finalizer will run, or even if it will at all. Because those resources may never be claimed, you will have to run with fewer resources.

나는 부분의 전체를 읽고 추천 할 것 - 그것은 내가 여기 앵무새 할 수있는 것보다 훨씬 더 일을 설명합니다.

1

finalize()의 설명서를 자세히 읽으면 finalizers를 사용하면 개체가 GC로 수집되지 않도록 할 수 있습니다.

finalizer가없는 경우 개체를 단순히 제거 할 수 있으므로 더 이상주의 할 필요가 없습니다. 그러나 파이널 라이저가있는 경우 오브젝트가 다시 "가시화"되지 않으면 나중에 확인해야합니다.

실제로 현재 Java 가비지 수집이 구현 된 방법을 알지 못하면 (실제로 다른 Java 구현이 있기 때문에 다른 GC도 있음) 객체가있는 경우 GC가 추가 작업을해야한다고 가정 할 수 있습니다 이 기능 때문에 최종 자입니다. 실제로 데

9

한 이러한 문제는 실행 : 태양 핫스팟 JVM에서

종료자를 고정 낮은 우선 순위가 부여 된 스레드에서 처리된다. 고부하 응용 프로그램에서는 우선 순위가 낮은 최종 마무리 스레드가 처리 할 수있는 것보다 빨리 필요한 개체를 만드는 것이 쉽습니다. 한편, finalization-pending 객체가 사용하는 힙의 공간은 다른 용도로는 사용할 수 없습니다. 최종적으로 보류중인 객체는 사용 가능한 모든 메모리를 사용하기 때문에 최종적으로 응용 프로그램에서 모든 시간을 가비지 수집 할 수 있습니다.

물론 이것은 효과적인 Java에 설명 된 종결자를 사용하지 않는 다른 많은 이유 외에도 있습니다.

관련 문제