일부 기능에는 기본 라이브러리를 사용하는 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 버그 일 수 있습니까? 어떻게 알 수 있습니까?
Windows 힙은 이전 API입니다. 대부분의 malloc() 구현체는이를 사용하지 않습니다. –
@brian 제발 좀 자세히 설명해 주시겠습니까? 그렇다면 다른 API는 무엇입니까? Windbg 출력에서 볼 수있는 증가량을 어떻게 설명 할 수 있습니까? –
모든 malloc()은 VirtualAlloc()을 사용하여 보았습니다. 힙 성장이 프로세스 크기 증가와 일치하면 Windows 힙을 사용하는 API가 사용됩니다. –