2013-05-04 2 views
1

일부 기능에는 기본 라이브러리를 사용하는 Java 응용 프로그램이 있습니다. JNI를 사용하여 기본 라이브러리를 제어하고 라이브러리에서 비동기 콜백을 수신합니다. 서로 통신하는 Java 프론트 엔드 및 기본 백엔드라고 생각할 수 있습니다.JVM 메모리 누수 디버깅

메모리 누수가 있습니다. 응용 프로그램을 시작하자 마자 메모리가 천천히 그러나 꾸준히 증가합니다. 그래서 누출을 일으킬 수있는 것을 보려고했습니다.

먼저 자바 프론트 엔드를 간단한 C++ 텍스트 인터페이스로 대체 해 보았습니다. 그런 식으로 애플리케이션은 어떤 방식 으로든 Java를 사용하지 않으며 누수가 중지됩니다. 따라서 문제는 자바 프론트 엔드에 있어야합니다.

그래서 힙이 증가하는지 확인하기 위해 jvisualVM을 실행했습니다. Java 힙 크기는 상당히 일정했습니다. 심지어 xmx32m으로 프로그램을 시작했지만 메모리는 OutOfMemoryError없이 100m 지나서도 계속 증가했습니다. 실제로 jvisualVM은 약 7m에서 Java 힙을 보여주었습니다.

그래서 나는 WinDbg로 프로그램을 더 깊이 파고 들었다. 나는 !heap -s 명령을 사용하여 힙의 패턴을 분석하고 나는이 가지고 : 약 30 분 동안 실행 된 프로그램에

0:059> !heap -s 
LFH Key     : 0x382288b9 
Termination on corruption : ENABLED 
    Heap  Flags Reserv Commit Virt Free List UCR Virt Lock Fast 
        (k)  (k) (k)  (k) length  blocks cont. heap 
----------------------------------------------------------------------------- 
00330000 00000002 2048 1704 2048  22 71  2 0  0 LFH 
005b0000 00001002 1088 212 1088  68  3  2 0  0 LFH 
00aa0000 00001002 1088 108 1088  15  7  2 0  0 LFH 
004f0000 00001002 15424 12876 15424 1372 89  9 0  1 LFH 
... 

0:059> !heap -stat -h 004f0000 
heap @ 004f0000 
group-by: TOTSIZE max-display: 20 
    size  #blocks  total  (%) (percent of total busy bytes) 
    2b110 20 - 562200 (60.36) 
    98 166e - d5150 (9.33) 
    6cd20 1 - 6cd20 (4.77) 
    ... 

힙 :

0:046> !heap -s 
LFH Key     : 0x5e47ba72 
Termination on corruption : ENABLED 
    Heap  Flags Reserv Commit Virt Free List UCR Virt Lock Fast 
        (k)  (k) (k)  (k) length  blocks cont. heap 
----------------------------------------------------------------------------- 
006b0000 00000002 2048 1744 2048  46 92  2 0  0 LFH 
00200000 00001002 1088 220 1088  68  3  2 0  0 LFH 
00950000 00001002 1088 108 1088  15  7  2 0  0 LFH 
001b0000 00001002 47808 31936 47808 1855 102 12 0  0 LFH 
... 

0:046> !heap -stat -h 001b0000 
heap @ 001b0000 
group-by: TOTSIZE max-display: 20 
    size  #blocks  total  (%) (percent of total busy bytes) 
    98 59d1 - 355418 (36.67) 
    2b110 10 - 2b1100 (29.61) 
    6cd20 1 - 6cd20 (4.68) 
    ... 
갓 실행 프로그램에

힙을

이제 블록 크기가 늘어남에 따라 누수가 발생한다는 것을 분명히 알 수 있습니다. 그러나 블록 중 하나를 !heap -p -a으로 분석하면 다음과 같이 나타납니다.

*** ERROR: Symbol file could not be found. Defaulted to export symbols for jvm.dll

스택 추적이 없습니다. 따라서 블록은 jvm.dll의 어딘가에 할당되고 JVM에 대한 pdbs가 없으므로 누출을 더 디버깅 할 수 없습니다.

코드에서 어디에서 누출이 발생했는지 정확히 알 수있었습니다. 자바 프론트 엔드에 대한 모든 callbacs는 하나 개의 함수를 통과 :

void callback(JNIEnv *env, int stream, double value, char *callbackName){ 
    jclass jni = env->FindClass("nativ/Callbacks"); 
    jmethodID callbackMethodID = env->GetStaticMethodID(jni, callbackName, "(ID)V"); 
    jvalue params[2]; 
    params[0].i = (long)(stream); 
    params[1].d = value; 
    env->CallStaticVoidMethodA(jni, callbackMethodID, params); //commenting this out stops the leaks 
} 

은 언제 마지막 명령을 주석 누출 중지,하지만 난 다시 프론트 엔드에 대한 피드백을 얻을 수 없습니다.

JVM 버그 일 수 있습니까? 어떻게 알 수 있습니까?

+0

Windows 힙은 이전 API입니다. 대부분의 malloc() 구현체는이를 사용하지 않습니다. –

+0

@brian 제발 좀 자세히 설명해 주시겠습니까? 그렇다면 다른 API는 무엇입니까? Windbg 출력에서 ​​볼 수있는 증가량을 어떻게 설명 할 수 있습니까? –

+1

모든 malloc()은 VirtualAlloc()을 사용하여 보았습니다. 힙 성장이 프로세스 크기 증가와 일치하면 Windows 힙을 사용하는 API가 사용됩니다. –

답변

0

malloc()은 내부적으로 HeapAlloc()을 호출합니다. JVM의 내부 상태에 대한 참조를 보유하고있는 한 JVM에서 할당 한 메모리를 해제하려면 'Release'메서드가 필요합니다.

관련 문제