2013-08-04 6 views
12

마무리.는 <strong>종료 자 스레드</strong>이 <strong>무한 루프가있는 경우 수행하거나 <strong>자바</strong> 교착 방법</strong>을 마무리 무슨 방법

+4

사용하는 동안 확인한 행동을 알려 주실 수 있습니까? –

+0

JVM은 이것을 관찰하기가 어렵다. JVM은'finalize'가 호출 될 것을 보장하지 못하며, 바늘은 언제 호출 될지 모른다. – morgano

+0

@morgano 간단한'println'만으로 충분하다. 나는 오래전에 이걸 가지고 놀았고,'finalize'는 매우 즉각적으로 불려 가고 있습니다. 시스템 종료까지 도달 할 수없는 개체가 확정되지 않는 드문 경우가 있습니다. –

답변

13

사양 글 : 객체의 스토리지가 가비지 컬렉터에 의해 회수

하기 전에, Java 가상 머신이 해당 개체의 종료자를 호출합니다.

Java 프로그래밍 언어는 객체 저장 공간을 재사용하기 전에 발생한다고하는 경우를 제외하고는 파이널 라이저가 호출되는 시간을 지정하지 않습니다.

저장 용량을 다시 사용하기 전에 마무리 도구가 완료되어야한다는 의미입니다.

Java 프로그래밍 언어는 주어진 개체에 대해 최종 스레드를 호출 할 스레드를 지정하지 않습니다.

많은 파이널 라이저 스레드가 활성 상태 일 수 있으며 (대용량 공유 메모리 멀티 프로세서에서 필요함) 큰 연결된 데이터 구조가 가비지가되는 경우 해당 객체의 모든 객체에 대한 모든 finalize 메소드가 데이터 구조는 동시에 호출 될 수 있으며, 각 파이널 라이저 호출은 다른 스레드에서 실행됩니다.

즉, 마무리 작업은 가비지 수집기 스레드, 별도의 스레드 또는 별도의 스레드 풀에서 발생할 수 있습니다.

JVM은 파이널 라이저 실행을 중단하는 것만 허용되지 않으며 제한된 수의 스레드 만 사용할 수 있습니다 (스레드는 운영 체제 자원이며 운영 체제는 임의의 수의 스레드를 지원하지 않습니다). 따라서 종료되지 않는 파이널 라이저는 필요에 따라 스레드 풀을 굶어 버리므로 종료 가능한 객체의 수집을 금지하고 메모리 누수가 발생합니다.

오라클 JDK 7에
public class Test { 

    byte[] memoryHog = new byte[1024 * 1024]; 

    @Override 
    protected void finalize() throws Throwable { 
     System.out.println("Finalizing " + this + " in thread " + Thread.currentThread()); 
     for (;;); 
    } 

    public static void main(String[] args) { 
     for (int i = 0; i < 1000; i++) { 
      new Test(); 
     } 
    } 
} 

이 인쇄 :

다음 테스트 프로그램이 동작을 확인

Finalizing [email protected] in thread Thread[Finalizer,8,system] 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
     at tools.Test.<init>(Test.java:5) 
     at tools.Test.main(Test.java:15) 
+1

+1, 어느 정도 동일한 테스트를 수행했는데 동일한 결과를 얻었습니다. finalizer 스레드는 하나만 포함됩니다. –

+0

@mriton, ** _의 의미는 무엇입니까? 많은 파이널 라이저 스레드가 활성화 될 수 있습니다 (대용량 공유 메모리 멀티 프로세서에서 때때로 필요합니다) _ **. CMS와 같이 우리가 선택한 GC 알고리즘과 관련이 있습니다. Finalizer 스레드 수와 GC 알고리즘 사이에는 관계가 있습니다. – andy

+1

JVM이 원하는대로이 작업을 수행 할 수 있으며 다른 JVM이 다르게 수행 할 수 있습니다. 특정 JVM에 관심이 있다면 해당 문서 (또는 그보다 더 많은 소스 코드)를 확인하십시오. lpiepiora의 anwer는 사용중인 가비지 콜렉션 알고리즘과 상관없이 Oracle JVM에 항상 하나의 finalizer 스레드가 있음을 나타냅니다. – meriton

4

내가 말할 것을 자바 사양 말하지 않기 때문에 그 finalize 메소드를 호출해야하는 방법 (객체가 가비지 수집되기 전에 호출되어야 함), 동작은 구현에 따라 다릅니다.

사양 프로세스를 실행하는 여러 스레드를 가진 배제하지 않지만, 필요하지 않습니다

많은 종료 자 스레드가 활성화 할 수 있다는 것을 인식하는 것이 중요하다 (이 때때로에 필요 큰 공유 메모리 다중 프로세서) 및 과 같이 큰 연결된 데이터 구조가 가비지가되는 경우 해당 데이터 구조의 모든 개체에 대한 finalize 메서드는 이 동시에 호출 될 수 있으며 각 finalizer 호출은 다른 스레드에서 실행됩니다.

- JDK7의 소스 찾고

에서, FinalizerThread이 종결 예정 오브젝트의 큐 유지 (ReferenceQueue 문서를 체크 실제로 오브젝트는 GC에 의해 큐에 추가를 검증 할 때 도달 될)

private static class FinalizerThread extends Thread { 
    private volatile boolean running; 
    FinalizerThread(ThreadGroup g) { 
     super(g, "Finalizer"); 
    } 
    public void run() { 
     if (running) 
      return; 
     running = true; 
     for (;;) { 
      try { 
       Finalizer f = (Finalizer)queue.remove(); 
       f.runFinalizer(); 
      } catch (InterruptedException x) { 
       continue; 
      } 
     } 
    } 
} 

각 개체가 큐에서 제거되고 runFinalizer 메서드가 실행됩니다. 검사가 객체에서 실행되면 확인이 수행되고 호출되지 않으면 기본 메소드 invokeFinalizeMethod에 대한 호출로 검사가 수행됩니다. 이 방법은 단순히 개체에 finalize 메소드를 호출한다 :

JNIEXPORT void JNICALL 
Java_java_lang_ref_Finalizer_invokeFinalizeMethod(JNIEnv *env, jclass clazz, 
                jobject ob) 
{ 
    jclass cls; 
    jmethodID mid; 

    cls = (*env)->GetObjectClass(env, ob); 
    if (cls == NULL) return; 
    mid = (*env)->GetMethodID(env, cls, "finalize", "()V"); 
    if (mid == NULL) return; 
    (*env)->CallVoidMethod(env, ob, mid); 
} 

FinalizerThread 차례로 연결되어야합니다 결함이 객체에 차단하면서는, 개체가 목록에서 대기받을 상황에 연결되어야합니다 ~ OutOfMemoryError.

자바에서 무한 루프 또는 교착 상태가 방법을 확정있는 경우 종료 자 스레드가 무엇을 할 것 :

그래서 원래의 질문에 대답합니다.

그냥 거기 앉아서 OutOfMemoryError까지 무한 루프를 실행합니다.

public class FinalizeLoop { 
    public static void main(String[] args) { 
     Thread thread = new Thread() { 
      @Override 
      public void run() { 
       for (;;) { 
        new FinalizeLoop(); 
       } 
      } 
     }; 
     thread.setDaemon(true); 
     thread.start(); 
     while (true); 
    } 

    @Override 
    protected void finalize() throws Throwable { 
     super.finalize(); 
     System.out.println("Finalize called"); 
     while (true); 

    } 
} 

참고 "이라고 마무리"를 JDK6과 JDK7에 한 번만 인쇄합니다.

+0

죄송합니다, 위의 예제 코드에 동의하지 않습니다. for 루프는 메인 쓰레드와 동시에 종료 할 것이므로, ** 어떤 것도 볼 수 없다. – andy

+0

@andy 네 말이 맞아. 내가 코드를 약간 변경 했어 결국 언젠가 너는 뭔가를 볼 수있을거야 - GC가 언제 시작하는지에 따라 달라질거야. 고마워! – lpiepiora

0

개체가 "해제"되지 않습니다. 즉, 개체가 메모리에서 되돌려지지 않고 finalize 메서드에서 해제 된 리소스가 전체적으로 예약되어 유지됩니다.

기본적으로 finalize() 메소드가 실행되기를 기다리는 모든 객체를 포함하는 대기열이 있습니다. Finalizer 스레드는이 대기열에서 객체를 가져 와서 finalize를 실행하고 객체를 해제합니다.

이 스레드가 교착 상태가되면 ReferenceQueue 큐가 커지고 어느 시점에서 OOM 오류가 냉혹 해집니다. 또한 리소스는이 대기열에있는 객체에 의해 호깅됩니다. 희망이 도움이 !!

for(;;) 
{ 
    Finalizer f = java.lang.ref.Finalizer.ReferenceQueue.remove(); 
    f.get().finalize(); 
}