I'm trying to pass Mat
object to Java from C++ using JNI. I've looked inside the source code of OpenCV Java dll, and I'm using exact same code that OpenCV uses. When I want to release a Mat
object from Java, which is created by OpenCV Java API it works fine, but when I want to release a Mat
object that I've created using the same code my applications crashes.
I'm posting the codes that I'm using. I would be very appreciated if anybody has an idea. Thanks in advance.
[EDIT]
Also I would like to add one more remark, if you look at code blocks 3 and 4 function definitons in Java and C++ sides have same function names, readImage0, but in 1 and 2 in C++ side function name has an extra 1 on it's name, imread_11 and imread_1, how does those two get linked? Maybe there is an extra layer that I'm not aware of.
[/EDIT]
[EDIT2]
Running on Windows 7, VC11, tried both with OpenCV 2.4.5 and 2.4.8
[/EDIT2]
1. This is the C++ code to pass Mat
object in OpenCV source:
JNIEXPORT jlong JNICALL Java_org_opencv_highgui_Highgui_imread_11 (JNIEnv*, jclass, jstring);
JNIEXPORT jlong JNICALL Java_org_opencv_highgui_Highgui_imread_11
(JNIEnv* env, jclass , jstring filename)
{
static const char method_name[] = "highgui::imread_11()";
try {
LOGD("%s", method_name);
const char* utf_filename = env->GetStringUTFChars(filename, 0); std::string n_filename( utf_filename ? utf_filename : "" ); env->ReleaseStringUTFChars(filename, utf_filename);
Mat _retval_ = cv::imread( n_filename );
return (jlong) new Mat(_retval_);
} catch(const std::exception &e) {
throwJavaException(env, &e, method_name);
} catch (...) {
throwJavaException(env, 0, method_name);
}
return 0;
}
2. This is the Java code in OpenCV source:
public static Mat imread(String filename)
{
Mat retVal = new Mat(imread_1(filename));
return retVal;
}
private static native long imread_1(String filename);
3. This is my code:
JNIEXPORT jlong JNICALL Java_tr_com_guney_opencvcpp2java_OpenCVCpp2Java_readImage0(JNIEnv *env, jclass, jstring imagePath)
JNIEXPORT jlong JNICALL Java_tr_com_guney_opencvcpp2java_OpenCVCpp2Java_readImage0(JNIEnv *env, jclass, jstring imagePath)
{
static const char method_name[] = "OpenCVCpp2Java::readImage0()";
try {
LOGD("%s", method_name);
const char* utf_filename = env->GetStringUTFChars(imagePath, 0); std::string n_filename( utf_filename ? utf_filename : "" ); env->ReleaseStringUTFChars(imagePath, utf_filename);
cv::Mat _retval_ = cv::imread( n_filename );
return (jlong) new cv::Mat(_retval_);
} catch(const std::exception &e) {
throwJavaException(env, &e, method_name);
} catch (...) {
throwJavaException(env, 0, method_name);
}
return 0;
}
4. This is my Java code:
public static Mat readImage(String imagePath)
{
Mat retVal = new Mat(readImage0(imagePath));
return retVal;
}
private static native long readImage0(String imagePath);
5. This is how I run the code:
Mat image = Highgui.imread("e:/image.png"); //this works fine
Highgui.imwrite("e:/imageJava.png", image); // this is to check if image is read correctly and works fine
image.release(); // this works fine
//system.gc(); // this also works fine
Mat image2 = OpenCVCpp2Java.readImage("e:/image.png"); //this works fine
Highgui.imwrite("e:/imageJava2.png", image2); // this is to check if image is read correctly and works fine
image2.release(); // this throws exception!
//system.gc(); // this causes application to crash!
6. This is what is written in Exception:
Exception in thread "main" java.lang.Exception: Unknown exception in JNI code {Mat::n_1release()}
at org.opencv.core.Mat.n_release(Native Method)
at org.opencv.core.Mat.release(Mat.java:1875)
7. This is what is written in crash report:
Stack: [0x28880000,0x288d0000], sp=0x288cf620, free space=317k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
C [ntdll.dll+0x2e3be] RtlInitUnicodeString+0x196
C [ntdll.dll+0x2e023] RtlFreeHeap+0x7e
C [kernel32.dll+0x114ad] HeapFree+0x14
C [opencv_java245.dll+0x48e877] Java_org_opencv_contrib_Contrib_chamerMatching_11+0x429a47
C [opencv_java245.dll+0x904f3] Java_org_opencv_contrib_Contrib_chamerMatching_11+0x2b6c3
C [opencv_java245.dll+0x6516a] Java_org_opencv_contrib_Contrib_chamerMatching_11+0x33a
j org.opencv.core.Mat.n_delete(J)V+0
j org.opencv.core.Mat.finalize()V+4
v ~StubRoutines::call_stub
V [jvm.dll+0xfb88b]
V [jvm.dll+0x18d551]
V [jvm.dll+0xfb90d]
V [jvm.dll+0x96301]
V [jvm.dll+0x990c9]
C [java.dll+0x2100] Java_java_lang_ref_Finalizer_invokeFinalizeMethod+0x39
j java.lang.ref.Finalizer.runFinalizer()V+45
j java.lang.ref.Finalizer.access$100(Ljava/lang/ref/Finalizer;)V+1
j java.lang.ref.Finalizer$FinalizerThread.run()V+24
v ~StubRoutines::call_stub
V [jvm.dll+0xfb88b]
V [jvm.dll+0x18d551]
V [jvm.dll+0xfba31]
V [jvm.dll+0xfba8b]
V [jvm.dll+0xb5e89]
V [jvm.dll+0x119b74]
V [jvm.dll+0x14217c]
C [msvcr71.dll+0x9565] endthreadex+0xa0
C [kernel32.dll+0x1336a] BaseThreadInitThunk+0x12
C [ntdll.dll+0x39f72] RtlInitializeExceptionChain+0x63
C [ntdll.dll+0x39f45] RtlInitializeExceptionChain+0x36
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j org.opencv.core.Mat.n_delete(J)V+0
j org.opencv.core.Mat.finalize()V+4
v ~StubRoutines::call_stub
j java.lang.ref.Finalizer.invokeFinalizeMethod(Ljava/lang/Object;)V+0
j java.lang.ref.Finalizer.runFinalizer()V+45
j java.lang.ref.Finalizer.access$100(Ljava/lang/ref/Finalizer;)V+1
j java.lang.ref.Finalizer$FinalizerThread.run()V+24
v ~StubRoutines::call_stub
I've figured out what the problem is. I think it is kind of complicated, at least it was for me.
The problem is I was using opencv_java248.dll which is distributed with OpenCV and already compiled. opencv_java248.dll is built statically, which means it does not need any of the OpenCV dll's (opencv_core248.dll, etc) after it is compiled. And I've created my own dll opencvcpp2java.dll which is also built statically so that it won't need any of the dll's during runtime.
After I got the crashes, I started to think what might be causing this problem. Then I realized that memory allocation and deallocation is done on completely different dll's. I was allocating memory using opencvcpp2java.dll and was expecting opencv_java248.dll to release it. And when it was trying to release it, program crashes. On the other hand when I do both the allocation and deallocation using opencv_java248.dll there was no problem. So, as an intuition, I thought that allocation and deallocation should be done on the same dll.
After this intuition I've compiled opencv_java248.dll myself (btw for this you need to compile all of the OpenCV dll's, it is piece of cake when you use CMake) and make it dependent to other OpenCV dll's and recompiled opencvcpp2java.dll and also make it dependent to OpenCV dll's. So when I want to allocate memory using opencvcpp2java.dll it allocates memory using OpenCV dll's (opencv_core248.dll to be more specific), and when I want to deallocate memory using opencv_java248.dll it deallocates the memory using same dll that it is created.
I'm not 100% sure that this is the correct solution, but it worked for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With