Java Programming: JNA vs. JNI on Windows

Some developers used JNA to call native C/C++ interfaces of Dynamsoft Barcode Reader in Java program. The app ran slowly and sometimes crashed. Based on the use case, I created a simple project for building JNA and JNI on Windows. The sample does not only show how to invoke native code but also share how to check the Java thread stack size and solve the stack overflow issue on Windows.

Download and Installation

Accessing Native Code via JNA and JNI

Open Eclipse to create a new project. The structures of JNA and JNI folders are almost the same.

Java code

Create the Java function decodefile() for JNA and JNI:

C/C++ code

For JNA, you just need to create a standard function and export it. For JNI, you have to comply with the JNI naming convention.

Building shared libraries with CMake

Copy shared libraries to <Project Root>/jna(jni)/platforms/win:

  • DBRx64.lib

Create CMakeLists.txt:

To build the shared library for JNI, replace ‘jnadbr’ with ‘jnidbr’.

Build the project for 64-bit Java runtime:

cd jnamkdir buildcd buildcmake -G"Visual Studio 15 2017 Win64" ..cmake --build . --config Release --target install

Create Demo.java for test:

Run the app:

javac -cp libs\jna-3.0.9.jar src\com\dynamsoft\*.javajava -cp libs\jna-3.0.9.jar;src\ com.dynamsoft.Demo

Why is running JNA much slower than running JNI?

The native code is the same, why the performance is different? The only possible reason is when calling the native method the first time it takes a long time to initialize the JNA environment.

We can call the native method to initialize the JNA environment before measuring the performance.

Build and rerun the app. The JNA performance of invoking native method looks no different to JNI.

Now we get the conclusion that using JNA takes extra loading time.

Java VM Stack Overflow Exception

The stack overflow exception showed up when runtime environment switched from 64-bit to 32-bit.

According to https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/crashes001.html, the issue is caused by thread stack size. To solve the issue, we’d better know the default thread stack size. Let’s try the following command:

Linux

java -XX:+PrintFlagsFinal -version | grep ThreadStackSize

Windows

java -XX:+PrintFlagsFinal -version | findstr ThreadStackSize

The value of stack size is 0 on Windows. Maybe the answer is in the source code.

Download and install TortoiseHg.

Get JVM source code:

hg clone http://hg.openjdk.java.net/jdk8/jdk8/hotspot/

Search for ‘stack size’ to find the following informative paragraph.

To verify whether the default stack size is 320K on Windows, I tried the code snippet located in os_windows.cpp:

To avoid stack overflow exception, we should allocate big chunks of memory on the heap instead of the stack.

Re-compile the native code in x86 mode:

cd jnamkdir buildcd buildcmake ..cmake --build . --config Release --target install

Compile and run the Java code with 32-bit JDK:

"E:\Program Files (x86)\Java\jdk1.8.0_191\bin\javac.exe" -cp libs\jna-3.0.9.jar src\com\dynamsoft\*.java"E:\Program Files (x86)\Java\jdk1.8.0_191\bin\java.exe" -cp libs\jna-3.0.9.jar;src\ com.dynamsoft.Demo

We can see the default stack size is 320K by default on Windows. To enlarge the stack size, append -Xss option when running Java program:

"E:\Program Files (x86)\Java\jdk1.8.0_191\bin\java.exe" -Xss1024 -cp libs\jna-3.0.9.jar;src\ com.dynamsoft.Demo

References

Source Code

https://github.com/yushulx/jna-jni-windows

Originally published at www.codepool.biz on October 21, 2018.

Manager of Dynamsoft Open Source Projects | Tech Lover