У меня есть приложение Java, которое использует собственную библиотеку для некоторых своих функций. Он использует JNI для управления собственной библиотекой, а также получает асинхронный обратный вызов из библиотеки. Вы можете думать об этом как о внешнем интерфейсе Java и внутреннем интерфейсе, которые общаются друг с другом.
Я сталкиваюсь с утечкой памяти. Вскоре после запуска приложения память медленно, но неуклонно увеличивается. Поэтому я попытался посмотреть, что может вызвать утечку.
Сначала я попытался заменить интерфейс Java простым текстовым интерфейсом C ++. Таким образом, приложение никак не использует Java — и утечки прекратились. Так что проблема должна быть во внешнем интерфейсе Java.
Поэтому я запустил jvisualVM, чтобы увидеть, увеличивается ли куча — и оказалось, что нет. Размер кучи Java был довольно постоянным. Я даже запустил программу с xmx32m, но память продолжала увеличиваться за 100 м без каких-либо OutOfMemoryError
s. Фактически, jvisualVM показывал кучу Java около 7 метров.
Поэтому я углубился в программу с WinDbg. Я проанализировал шаблоны кучи с !heap -s
команда и я получил это:
Кучи на только что запущенной программе:
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)
...
Теперь ясно видно, что утечки вызваны растущим числом блоков размером 98. Но когда я пытаюсь проанализировать один из блоков с !heap -p -a
, Я получил:
*** ОШИБКА: файл символов не найден. По умолчанию для экспорта символов для jvm.dll
без каких-либо следов стека. Таким образом, блоки расположены где-то внутри jvm.dll, и, поскольку нет никаких pdbs для JVM, я не могу отлаживать утечку дальше.
Мне удалось точно определить, где происходит утечка в моем коде. Все вызовы к интерфейсу Java проходят через одну функцию:
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? Как я узнаю?
malloc () внутренне вызывает HeapAlloc (). Я предполагаю, что вам нужен метод Release для освобождения памяти, выделенной JVM, если ваша библиотека содержит ссылку на внутреннее состояние JVM.
Других решений пока нет …