2012-03-09 8 views
12

Mam JNI oddzwaniania:JNI Attach/Odłącz zarządzanie pamięcią wątek

void callback(Data *data, char *callbackName){ 
    JNIEnv *env; 
    jvm->AttachCurrentThread((void **)&env, NULL); 
    /* start useful code*/ 

    /* end useful code */ 
    jvm->DetachCurrentThread(); 
} 

Gdy go tak (pusty użytecznego kodu), pojawia się przeciek pamięci. Jeśli skomentuję całą metodę, nie ma wycieku. Jaki jest właściwy sposób dołączania/odłączania wątków?

Moja aplikacja przetwarza dane dźwiękowe w czasie rzeczywistym, więc wątki odpowiedzialne za przetwarzanie danych muszą zostać wykonane tak szybko, jak to możliwe, aby być gotowym na kolejną partię. Tak więc dla tych wywołań zwrotnych tworzę nowe wątki. W każdej sekundzie są ich dziesiątki lub nawet setki, dołączają się do JVM, wywołują funkcję zwrotną, która odświeża wykres, odrywa się i ginie. Czy to jest właściwy sposób robienia tego? Jak radzić sobie z nieszczelną pamięcią?

EDIT: literówka

OK Stworzyłem mimimal kod potrzebny:

package test; 

public class Start 
{ 
    public static void main(String[] args) throws InterruptedException{ 
     System.loadLibrary("Debug/JNITest"); 
     start(); 
    } 

    public static native void start(); 
} 

i

#include <jni.h> 
#include <Windows.h> 
#include "test_Start.h" 

JavaVM *jvm; 
DWORD WINAPI attach(__in LPVOID lpParameter); 

JNIEXPORT void JNICALL Java_test_Start_start(JNIEnv *env, jclass){ 
    env->GetJavaVM(&jvm); 
    while(true){ 
     CreateThread(NULL, 0, &(attach), NULL, 0, NULL); 
     Sleep(10); 
    } 
} 


DWORD WINAPI attach(__in LPVOID lpParameter){ 
    JNIEnv *env; 
    jvm->AttachCurrentThread((void **)&env, NULL); 
    jvm->DetachCurrentThread(); 
    return 0; 
} 

i kiedy uruchomić profilera VisualJM, pojawia się zwykle piłokształtny wzór, nie ma tam przecieku. Wykorzystanie sterty osiągnęło wartość około 5 MB. Obserwowanie eksploratora procesu pokazuje jednak pewne dziwne zachowanie: pamięć powoli rośnie i rośnie, 4K sekunda na minutę, a potem nagle cała ta przydzielona pamięć spada. Krople te nie odpowiadają zbieraniu śmieci (występują rzadziej i zwalniają mniej pamięci niż te zęby w profilerze).

Moim najlepszym rozwiązaniem jest to, że niektóre zachowania OS obsługują dziesiątki tysięcy milisekundowych wątków. Czy jakiś guru ma wytłumaczenie tego?

Odpowiedz

7

Wyobraziłem sobie problem. To zwisało z miejscowych odniesień w kodzie JNI, którego nie zniszczyłem. Każde wywołanie zwrotne tworzy nowe lokalne odwołanie, powodując przeciek pamięci. Kiedy przekonwertowałem lokalne odwołanie do globalnego, więc mogłem je ponownie użyć, problem zniknął.

14

Kilka punktów o oddzwanianie do Javy z natywnego kodu:

  • AttachCurrentThread powinna być wywoływana tylko jeśli jvm-> getenv() zwraca wartość zero. Zwykle jest to no-op, jeśli wątek jest już podłączony, ale możesz zaoszczędzić trochę narzutu.
  • DetachCurrentThread powinien być wywoływany tylko wtedy, gdy wywołałeś AttachCurrentThread.
  • Unikaj odłączania, jeśli spodziewasz się, że zostaniesz wezwany w tym samym temacie w przyszłości.

W zależności od wątku natywnego kodu, możesz chcieć uniknąć odrywania i zamiast tego zapisywać odniesienia do wszystkich natywnych wątków w celu usunięcia po zakończeniu (jeśli nawet musisz to zrobić, możesz polegać na aplikacji shutdown do czyszczenia).

Jeśli stale dołączasz i oddzielasz nić rodzime, maszyna wirtualna musi nieustannie kojarzyć (często te same) wątki z obiektami Java. Niektóre maszyny wirtualne mogą ponownie używać wątków lub tymczasowo buforować odwzorowania w celu zwiększenia wydajności, ale uzyskasz lepsze i bardziej przewidywalne zachowanie, jeśli nie polegasz na maszynie wirtualnej, aby zrobić to za Ciebie.

+0

Rozumiem twój punkt widzenia i zwykle się zgadzam. Wątki, które tworzę, są naprawdę krótkotrwałe. Tworzę je (używając WinApi CreateThread), a następnie natychmiast dołączam je do JVM.W Javie odświeża wykres Swing z nową wartością. Po ich zakończeniu odrywają i przerywają wykonywanie (powrót 0), w którym to momencie powinni zostać zniszczeni przez system operacyjny. Są używane do przekazywania nowej wartości do Javy. Jest około 40 z nich na sekundę dla każdego kanału dźwiękowego (pracuję z maksymalnie 32 kanałami). –

+0

Możesz rozważyć połączenie wątków. Spraw, aby wątki pozostały dłużej i pobieraj dane z kolejki, zamiast ciągle tworzyć nowe wątki. Zmniejszy to obciążenie zarządzania wątkami zarówno w systemie operacyjnym, jak i Java. – technomage

+0

Jeśli robisz jakieś rzeczy Javy w JNI oprócz zwykłego wywoływania metody, możesz także popchnąć/popować lokalną ramkę wokół tych akcji. – technomage