barchart-udt
barchart-udt copied to clipboard
Reloading barchart UDT for a second time within the same JVM crashes
It seems if you reload barchart for a second time within the JVM, it causes the JVM process to crash with a SIGSEGV. Here is what I have found so far with various stack traces
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 15104.0x3140]
0x6d885aa4 in jvm!JNI_GetCreatedJavaVMs () from C:\Program Files (x86)\Java\jre6\bin\client\jvm.dll
(gdb) bt
#0 0x6d885aa4 in jvm!JNI_GetCreatedJavaVMs () from C:\Program Files (x86)\Java\jre6\bin\client\jvm.dll
#1 0x6d885e4f in jvm!JNI_GetCreatedJavaVMs () from C:\Program Files (x86)\Java\jre6\bin\client\jvm.dll
#2 0x05ad963f in JNIEnv_::NewObject (this=0x2521528, clazz=0x0, methodID=0x6cf5058)
at C:/src/barchart-udt-github/barchart-udt-core/src/cdt/jni/include/jni.h:854
#3 0x05aca0ba in UDT_NewExceptionUDT (env=0x2521528, socketID=19235400, errorCode=-3,
message=0x5af1984 "unsupported option class in OptionUDT")
at ..\src\main\c++\jni\com_barchart_udt_SocketUDT.cpp:236
#4 0x05aca14b in UDT_ThrowExceptionUDT_Message (env=0x2521528, socketID=19235400,
comment=0x5af1984 "unsupported option class in OptionUDT")
at ..\src\main\c++\jni\com_barchart_udt_SocketUDT.cpp:249
#5 0x05acc658 in Java_com_barchart_udt_SocketUDT_setOption0@20 (env=0x2521528, self=0x59deb00, enumCode=2,
klaz=0x59deaf8, objValue=0x59deaf4) at ..\src\main\c++\jni\com_barchart_udt_SocketUDT.cpp:971
Java Stack Trace
j com.barchart.udt.SocketUDT.setOption0(ILjava/lang/Class;Ljava/lang/Object;)V+0
j com.barchart.udt.SocketUDT.setOption(Lcom/barchart/udt/OptionUDT;Ljava/lang/Object;)V+28
j com.barchart.udt.SocketUDT.setBlocking(Z)V+34
j com.barchart.udt.nio.ServerSocketChannelUDT.implConfigureBlocking(Z)V+5
j java.nio.channels.spi.AbstractSelectableChannel.configureBlocking(Z)Ljava/nio/channels/SelectableChannel;+55
It looks like it's actually trying to make a call to setOption0 with the UDT_RCVSYN enum value, but then determines that it's an unsupported option, which is obviously wrong. Checking further up the C code it seems it's checking using this method.
if (env->IsSameObject(klaz, jdk_clsBoolean)){
}
So my guess is that jdk_clsBoolean is from a the first class loader, so when it get's reloaded, klaz is actually pointing to a different version of the Boolean class in the new class loader.
So I think we need to somehow check that barchart is being used without the previous one being unloaded correctly or similar. Not sure how yet though
- commit your double-loading test case so I can verify on linux. can annotate
@Ignore
for now, to start via main(). - see if basic printf of jdk_clsBoolean hash code confirms your theory
- if you are right, may need to revisit / cleanup init/close. say, take a look on RegisterNatives, JNI_OnLoad, JNI_OnUnload, as @hepin1989 suggested.
see p. 103 form http://www.amazon.com/Java-Native-Interface-Programmers-Specification/dp/0201325772
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
//
cls = (*env)->FindClass(env, "C");
//
/* Use weak global ref to allow C class to be unloaded */
Class_C = (*env)->NewWeakGlobalRef(env, cls);
In my case it's not necessarily barchart udt that's the issue, since the TestLoadUnload works correctly. I use it inside JBoss, and when redeploying a WAR app, it's the second deploy that crashes. Which leads me to believe that there must be other leaks preventing the class loader from unloading, including barchart-udt.
Investigating further, I think it's the fact that cleanup is being called in my app under the assumption the class loader for barchart udt will be unloaded, but then because it doesn't when the SocketUDT class is used for the first time after the WAR reloads, the JNI resources have been released. We need a dynamic way of calling cleanup I guess, which at this stage I'm not sure how that can be done, since finalize wont get called on a custom class loader until it's ready to be unloaded. I'm wondering if we can add our own class inside barchart for the JNI layer.
it seems we should hide/remove SocketUDT.cleanup() and switch to implicit JNI_OnLoad / JNI_OnUnload instead.
Trouble is, JNI_OnUnload only gets called once the class loader is being garbage collected, but it wont even be a candidate for garbage collection since it will be holding onto the global references. Maybe changing them to weak references might work, I'd imagine that the CCC and CCCFactory classes would have to remain global refs though, but they are released along with the SocketUDT
well, that needs be tested.
how about crazy ideas, like: have native library be extracted with time stamp suffix, so they all look different from class loader point of view. then with bit more hacking on java level, you could have multiple SocketUDT classes with different life cycles :-)
more crazy ideas: say SocketUDT always comes with own class loader. then enforce that you can not get to SocketUDT except via its own class loader.
less crazy idea: take a look on how other popular native libraries like sigar http://support.hyperic.com/display/SIGAR/Home are getting loaded and re-loaded in jboss.
@CCob take a look at this http://developer.android.com/training/articles/perf-jni.html#jclass_jmethodID_and_jfieldID