2013-10-24 48 views
0

我一直在調試這個問題幾天沒有任何運氣。我必須錯過一些相當明顯的東西。我正在運行一個與JavaFX 2.2打包工具一起打包的Swing應用程序,它通過JNI建立了一個與C .dll的連接。如何診斷我的JNI內存損壞問題?

一切都很好,直到我想添加一個從C調用回Java的函數。當我這樣做時,我開始出現內存腐敗問題。以下是錯誤,其次是我的新JNI代碼:

# 
# A fatal error has been detected by the Java Runtime Environment: 
# 
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x7c82c912, pid=7424, tid=4828 
# 
# JRE version: Java(TM) SE Runtime Environment (7.0_40-b43) (build 1.7.0_40-b43) 
# Java VM: Java HotSpot(TM) Client VM (24.0-b56 interpreted mode windows-x86) 
# Problematic frame: 
# C [ntdll.dll+0x2c912] 
# 
# Core dump written. 
# 
# If you would like to submit a bug report, please visit: 
# http://bugreport.sun.com/bugreport/crash.jsp 
# 

--------------- T H R E A D --------------- 

Current thread (0x008e9800): JavaThread "main" [_thread_in_vm, id=4828, stack(0x00030000,0x00130000)] 

siginfo: ExceptionCode=0xc0000005, reading address 0x00000000 

Registers: 
EAX=0x10d3b510, EBX=0x008e0000, ECX=0x00000000, EDX=0x00000000 
ESP=0x000ff98c, EBP=0x000ff998, ESI=0x10d3b508, EDI=0x10d5d000 
EIP=0x7c82c912, EFLAGS=0x00010246 

Top of Stack: (sp=0x000ff98c) 
0x000ff98c: 008e0000 00000008 008e0004 000ff9d0 
0x000ff99c: 7c8338a2 00000000 10d5d000 000ff9c4 
0x000ff9ac: 00000000 00001000 008e0178 008e0000 
0x000ff9bc: 0cff0304 0706ff12 00001000 10a80000 
0x000ff9cc: 00000000 000ffbfc 7c82b46b 038e0000 
0x000ff9dc: 00008000 00007ff4 008e5458 00007ff4 
0x000ff9ec: 7c829dc9 008e0178 008e0178 10c375c0 
0x000ff9fc: 7c8274b9 77e6958b 000ffa2c 000ffa0c 

Instructions: (pc=0x7c82c912) 
0x7c82c8f2: 3d 00 fe 00 00 0f 87 75 dc ff ff 80 7d 14 00 0f 
0x7c82c902: 85 53 82 02 00 8b 4e 0c 8d 46 08 8b 10 89 4d 08 
0x7c82c912: 8b 09 3b 4a 04 89 55 0c 0f 85 86 4f 01 00 3b c8 
0x7c82c922: 0f 85 7e 4f 01 00 56 53 e8 c9 d6 ff ff 8b 45 0c 


Register to memory mapping: 

EAX=0x10d3b510 is an unknown value 
EBX=0x008e0000 is an unknown value 
ECX=0x00000000 is an unknown value 
EDX=0x00000000 is an unknown value 
ESP=0x000ff98c is pointing into the stack for thread: 0x008e9800 
EBP=0x000ff998 is pointing into the stack for thread: 0x008e9800 
ESI=0x10d3b508 is an unknown value 
EDI=0x10d5d000 is an unknown value 


Stack: [0x00030000,0x00130000], sp=0x000ff98c, free space=830k 
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) 
C [ntdll.dll+0x2c912] 
C [ntdll.dll+0x338a2] 
C [ntdll.dll+0x2b46b] 
C [MSVCR100.dll+0x10269] 
V [jvm.dll+0x145f0c] 
V [jvm.dll+0x76d81] 
V [jvm.dll+0x76f8c] 
V [jvm.dll+0x772ea] 
V [jvm.dll+0x8b674] 
V [jvm.dll+0x188b8a] 
V [jvm.dll+0x156226] 
V [jvm.dll+0x48950] 
V [jvm.dll+0x4b236] 
V [jvm.dll+0x4c094] 
V [jvm.dll+0x4c205] 
V [jvm.dll+0x9de75] 
V [jvm.dll+0xa3cae] 
V [jvm.dll+0xa3b20] 
V [jvm.dll+0xa6d30] 
V [jvm.dll+0xa72f8] 
V [jvm.dll+0x70dfe] 
V [jvm.dll+0x71666] 
V [jvm.dll+0x71927] 
V [jvm.dll+0x6dac0] 
... 

--------------- P R O C E S S --------------- 

Java Threads: (=> current thread) 
    0x10c88400 JavaThread "Framework Connection" [_thread_in_native, id=5452, stack(0x117a0000,0x118a0000)] 
    0x10c7e800 JavaThread "TimerQueue" daemon [_thread_blocked, id=5544, stack(0x11680000,0x11780000)] 
    0x10bca800 JavaThread "AWT-EventQueue-0" [_thread_blocked, id=7748, stack(0x11560000,0x11660000)] 
    0x10bbc800 JavaThread "Image Fetcher 0" daemon [_thread_blocked, id=808, stack(0x11460000,0x11560000)] 
    0x10ab0400 JavaThread "AWT-Windows" daemon [_thread_in_native, id=3040, stack(0x112b0000,0x113b0000)] 
    0x10b58800 JavaThread "AWT-Shutdown" [_thread_blocked, id=5728, stack(0x111b0000,0x112b0000)] 
    0x0f56b800 JavaThread "Java2D Disposer" daemon [_thread_blocked, id=5440, stack(0x110b0000,0x111b0000)] 
    0x0f52e000 JavaThread "Service Thread" daemon [_thread_blocked, id=7628, stack(0x10880000,0x10980000)] 
    0x0f528400 JavaThread "C1 CompilerThread0" daemon [_thread_blocked, id=5092, stack(0x10780000,0x10880000)] 
    0x0f526800 JavaThread "Attach Listener" daemon [_thread_blocked, id=1612, stack(0x10680000,0x10780000)] 
    0x0f525000 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=8040, stack(0x10580000,0x10680000)] 
    0x0f523800 JavaThread "Surrogate Locker Thread (Concurrent GC)" daemon [_thread_blocked, id=7456, stack(0x10480000,0x10580000)] 
    0x0f513000 JavaThread "Finalizer" daemon [_thread_blocked, id=6404, stack(0x10380000,0x10480000)] 
    0x0f50d000 JavaThread "Reference Handler" daemon [_thread_blocked, id=4892, stack(0x10280000,0x10380000)] 
=>0x008e9800 JavaThread "main" [_thread_in_vm, id=4828, stack(0x00030000,0x00130000)] 

Other Threads: 
    0x0f50b800 VMThread [stack: 0x10180000,0x10280000] [id=5676] 
    0x0f538c00 WatcherThread [stack: 0x10980000,0x10a80000] [id=3400] 

VM state:not at safepoint (normal execution) 

VM Mutex/Monitor currently owned by a thread: None 

Heap 
par new generation total 36864K, used 10756K [0x03380000, 0x05b80000, 0x05b80000) 
    eden space 32768K, 32% used [0x03380000, 0x03e01100, 0x05380000) 
    from space 4096K, 0% used [0x05380000, 0x05380000, 0x05780000) 
    to space 4096K, 0% used [0x05780000, 0x05780000, 0x05b80000) 
concurrent mark-sweep generation total 90112K, used 0K [0x05b80000, 0x0b380000, 0x0b380000) 
concurrent-mark-sweep perm gen total 12288K, used 7375K [0x0b380000, 0x0bf80000, 0x0f380000) 

Card table byte_map: [0x00fa0000,0x01010000] byte_map_base: 0x00f86400 

Polling page: 0x008f0000 

Code Cache [0x01080000, 0x010d0000, 0x03080000) 
total_blobs=184 nmethods=0 adapters=155 free_code_cache=32453Kb largest_free_block=33232576 

Compilation events (0 events): 
No events 

GC Heap History (0 events): 
No events 

Deoptimization events (0 events): 
No events 

Internal exceptions (10 events): 
Event: 0.547 Thread 0x008e9800 Threw 0x03996270 at C:\jdk7u2_32P\jdk7u40\hotspot\src\share\vm\prims\jni.cpp:717 
Event: 1.082 Thread 0x008e9800 Threw 0x03b85978 at C:\jdk7u2_32P\jdk7u40\hotspot\src\share\vm\prims\jni.cpp:717 
Event: 1.082 Thread 0x008e9800 Threw 0x03b85b10 at C:\jdk7u2_32P\jdk7u40\hotspot\src\share\vm\prims\jni.cpp:717 
Event: 1.082 Thread 0x008e9800 Threw 0x03b85c78 at C:\jdk7u2_32P\jdk7u40\hotspot\src\share\vm\prims\jni.cpp:717 
Event: 1.266 Thread 0x10d23400 Threw 0x03d44b08 at C:\jdk7u2_32P\jdk7u40\hotspot\src\share\vm\prims\jni.cpp:717 
Event: 1.266 Thread 0x10d23400 Threw 0x03d44ca0 at C:\jdk7u2_32P\jdk7u40\hotspot\src\share\vm\prims\jni.cpp:717 
Event: 1.266 Thread 0x10d23400 Threw 0x03d44e08 at C:\jdk7u2_32P\jdk7u40\hotspot\src\share\vm\prims\jni.cpp:717 
Event: 2.082 Thread 0x008e9800 Threw 0x03b863f8 at C:\jdk7u2_32P\jdk7u40\hotspot\src\share\vm\prims\jni.cpp:717 
Event: 2.082 Thread 0x008e9800 Threw 0x03b86590 at C:\jdk7u2_32P\jdk7u40\hotspot\src\share\vm\prims\jni.cpp:717 
Event: 2.082 Thread 0x008e9800 Threw 0x03b866f8 at C:\jdk7u2_32P\jdk7u40\hotspot\src\share\vm\prims\jni.cpp:717 

Events (10 events): 
Event: 2.091 loading class 0x10d25cb0 
Event: 2.091 loading class 0x10d25cb0 done 
Event: 2.092 loading class 0x10d28388 
Event: 2.092 loading class 0x10d28388 done 
Event: 2.095 loading class 0x10d283e8 
Event: 2.095 loading class 0x10d283e8 done 
Event: 2.096 loading class 0x10c44670 
Event: 2.096 loading class 0x10c44670 done 
Event: 2.096 loading class 0x10d27cc8 
Event: 2.096 loading class 0x10d27cc8 done 

我.h文件中聲明我全局:

extern jclass javaEntryPointClass; 
extern jobject javaEntryPointObject; 
extern JavaVM* cachedJVM; 

我定義我的.c文件我的全局:

// Required definition of the global variables declared in .h 
jclass javaEntryPointClass = NULL; 
jobject javaEntryPointObject = NULL; 
JavaVM* cachedJVM = NULL; 

我有一個JNI_OnLoad函數保存指針的JavaVM:

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) 
{ 
    cachedJVM = jvm; 

    return JNI_VERSION_1_6; 
} 

我有一個是從Java,我儲存起來的指針JCLASS叫另一個功能:後來

JNIEXPORT jint JNICALL Java_com_foo_FrameworkServices_Connect(
    JNIEnv *env, jobject obj, jstring string) 
{ 
    jclass cls1 = NULL; 
    PSTR szCmdLine = NULL; 
    jboolean isCopy = FALSE; 

    const char *str = (*env)->GetStringUTFChars(env, string, &isCopy); 
    szCmdLine = (CHAR*)str; 

    cls1 = (*env)->GetObjectClass(env, obj); 
    if (cls1 == NULL) 
     return -1; 

    javaEntryPointClass = (*env)->NewGlobalRef(env, cls1); 
    if (javaEntryPointClass == NULL) 
     return -2; 

    javaEntryPointObject = (*env)->NewGlobalRef(env, obj); 
    if (javaEntryPointObject == NULL) 
     return -3; 

    SomeLongRunningFunctionThatNeverEndsUntilTheProgramDoes(szCmdLine); 

    (*env)->ReleaseStringUTFChars(env, string, str); 

    return 0; 
} 

然後在我的本地代碼,我所有的插座連接的初始化之後,我已經準備好了開始接受所有JNI從Java調用,我用一個回調方法,以使Java知道,我已經準備好:

JNIEnv *env = NULL; 
    jmethodID mid = NULL; 
    int envStatus = 0; 
    int attached = 0; 

    // Get a current handle to the JNI environment. 
    envStatus = (*cachedJVM)->GetEnv(cachedJVM, (void **)&env, JNI_VERSION_1_6); 
    if (envStatus == JNI_EDETACHED) 
    { 
     // If we're not attached, try to attach to the current thread. 
     (*cachedJVM)->AttachCurrentThread(cachedJVM, (void **) &env, NULL); 
      attached = 1; 
    } 

    // Make sure the JNIEnv object we have isn't NULL. 
    if (env != NULL) 
    { 
      mid = (*env)->GetMethodID(env, javaEntryPointClass, "callback", "()V"); 
      if (mid != NULL) 
      { 
        // Call Java to tell it that GUI is ready to process requests. 
        (*env)->CallVoidMethod(env, javaEntryPointObject, mid); 
      } 

      // Free the global references so that Java can garbage collect. 
      if (javaEntryPointClass != NULL) 
      { 
        (*env)->DeleteGlobalRef(env, javaEntryPointClass); 
        javaEntryPointClass = NULL; 
      } 
      if (javaEntryPointObject != NULL) 
      { 
        (*env)->DeleteGlobalRef(env, javaEntryPointObject); 
        javaEntryPointObject = NULL; 
      } 
    } 

    // Detach the current thread from the JavaVM. Must be done before exiting thread. 
    if (attached == 1) 
     (*cachedJVM)->DetachCurrentThread(cachedJVM); 
... 

現在我知道在功能上,這個工作。在功能上,我的應用程序很好。但20次左右的時間中有1次,在本地代碼完成後不久就會崩潰。看起來它可能會在每次運行時破壞堆。但有時候這種腐敗會導致崩潰。

我在這裏錯過了什麼?我正在刪除我的全局引用並清空指針。我正在附加和從線程分離。遍歷調試器,事情看起來相當不錯。

+2

你應該測試(env!= NULL)。 – EJP

+3

如果你在函數中做了附加,你可能只想分離。 –

+0

不幸的是,這些變化都沒有改變。 – Splaktar

回答

2

這竟然是深圖書館堆損壞許多層面。所有的JNI代碼(在這裏有一些有用的評論之後)都是乾淨且無問題的。

問題原來是一個字符串中額外參數的傳遞。這個字符串在被解析並用於配置後端處理之前會傳遞給幾個級別(C庫)。我傳遞了最終庫未知的參數,而不是提供錯誤消息,當試圖解析此字符串時,庫會破壞堆。

最終,它與Java或JNI無關。這只是在底層C庫底層編寫的另一個工件。不幸的是,這種事情很常見,因爲時間不是用於清理或重構的預算。我很高興我現在可以回到Java了:)

謝謝你對此的幫助。我真的用細齒梳去了JNI位,學到了很多東西。

5

IIRC,您在調用實例方法時必須傳遞一個對象。您可能想要通過CallStaticVoidMethod替換CallVoidMethod。

好吧,看起來你的問題不在CallVoidMethod調用中。 我的建議:使用ExceptionOccured,ExceptionDescribe最後ExceptionClear:

  1. 試圖通過逐步註釋掉的代碼部分,每個JNI調用後
  2. 過程例外縮小問題。
  3. 仔細檢查您的malloc /免費的使用(包括的strdup,新建,刪除...)
+0

我不認爲這是真的,因爲http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp16656中的內容和示例(http ://www.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html)。我測試了將Java函數靜態化,但是在Java端引入了更多的多線程問題。 – Splaktar

+1

@Splaktar仔細看看你提到的作爲「例子」的頁面,特別是看到「C實現 - TestJNICallBackMethod.c」。 GetmethodID需要一個類對象,並且CallVoidMethod需要GetMethodID中使用的類的對象。看起來合乎邏輯,當你想到它通過。 – manuell

+0

我想我明白你的意思了。當我需要將它傳遞給工作項目時,我將它傳遞給了一個jclass。讓我試試看! – Splaktar