GraalVM support
junixsocket supports running in GraalVM, both in Hotspot/OpenJDK-mode as well as in Native Image mode with Substrate VM (ahead-of-time compilation).
Some optional features, such as junixsocket-rmi
, are currently unavailable in Native Image mode.
Support has been tested with GraalVM 22.1.0.
Hotspot VM mode
In this mode, GraalVM behaves mostly like OpenJDK.
Native Image/Substrate VM mode
In this mode, GraalVM attempts ahead-of-time compilation of our code, resulting in an executable binary of your entire Java application.
To achieve this with junixsocket, GraalVM needs some specific “Reachability
Metadata”. Since junixsocket
2.6.0, this metadata is included with junixsocket-common
and junixsocket-selftest
artifacts.
junixsocket selftest Native Image
You can try junixsocket's Native Image integration by running its selftests natively.
Prerequisites
Make sure GraalVM is enabled
For example:
# export JAVA_HOME=/Library/Java/JavaVirtualMachines/graalvm-ce-java17-22.2.0/Contents/Home
# export PATH=$JAVA_HOME/bin:$PATH
Make sure your build system works
## Add dependencies if necessary, e.g.:
sudo apt-get install gcc zlib1g-dev
Build the native image
# Build the platform-native executable:
cd junixsocket/junixsocket-selftest-native-image
# (Also specify -Dmysql to include junixsocket-mysql tests)
mvn -Dnative clean package
# Run the platform-native executable:
./target/junixsocket-selftest-native-image-X.Y.Z
NOTE: (Replace X.Y.Z with the actual version)
musl / Alpine Linux compatibility
The above binary, even though it currently cannot be built on Alpine Linux (musl), will run after
adding gcompat
:
sudo apk add gcompat
Known issues
native-image needs a lot of RAM
When you see an error message Error: Image build request failed with exit status 137
coming from the Maven build of junixsocket-selftest-native-image
, there was not enough RAM, and the kernel terminated the process. Either add some swap space or more RAM.
NoSuchMethodException: …DatagramSocketImpl.peekData
When you see errors like this, try building with a Java 19 GraalVM instead of Java 11 GraalVM:
Fatal error: org.graalvm.compiler.debug.GraalError: com.oracle.svm.util.ReflectionUtil$ReflectionUtilError: java.lang.NoSuchMethodException: org.newsclub.net.unix.vsock.AFVSOCKDatagramSocketImpl.peekData(java.net.DatagramPacket)
at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.util.AnalysisFuture.setException(AnalysisFuture.java:49)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:269)
at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.util.AnalysisFuture.ensureDone(AnalysisFuture.java:63)
at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.meta.AnalysisElement.lambda$execute$2(AnalysisElement.java:170)
at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.util.CompletionExecutor.executeCommand(CompletionExecutor.java:193)
at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.util.CompletionExecutor.lambda$executeService$0(CompletionExecutor.java:177)
at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Caused by: com.oracle.svm.util.ReflectionUtil$ReflectionUtilError: java.lang.NoSuchMethodException: org.newsclub.net.unix.vsock.AFVSOCKDatagramSocketImpl.peekData(java.net.DatagramPacket)
at org.graalvm.nativeimage.base/com.oracle.svm.util.ReflectionUtil.lookupMethod(ReflectionUtil.java:82)
at org.graalvm.nativeimage.base/com.oracle.svm.util.ReflectionUtil.lookupMethod(ReflectionUtil.java:69)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.JNIRegistrationUtil.method(JNIRegistrationUtil.java:91)
at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.jdk.JNIRegistrationJavaNet.lambda$registerDatagramSocketCheckOldImpl$0(JNIRegistrationJavaNet.java:230)
at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.meta.AnalysisElement$SubtypeReachableNotification.lambda$notifyCallback$0(AnalysisElement.java:129)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
... 10 more
Caused by: java.lang.NoSuchMethodException: org.newsclub.net.unix.vsock.AFVSOCKDatagramSocketImpl.peekData(java.net.DatagramPacket)
at java.base/java.lang.Class.getDeclaredMethod(Class.java:2475)
at org.graalvm.nativeimage.base/com.oracle.svm.util.ReflectionUtil.lookupMethod(ReflectionUtil.java:74)
... 15 more
MassiveParallelTest.testAcceptConnect()… java.lang.ArrayIndexOutOfBoundsException: Index 7 out of bounds for length 4
When you see this or a similar message, you're hitting GraalVM bug 7599:
Testing "junixsocket-common"... MassiveParallelTest.testAcceptConnect()... java.lang.ArrayIndexOutOfBoundsException: Index 7 out of bounds for length 4
at org.graalvm.nativeimage.builder/com.oracle.svm.core.handles.ThreadLocalHandles.popFramesIncluding(ThreadLocalHandles.java:136)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.JNIObjectHandles.popLocalFramesIncluding(JNIObjectHandles.java:229)
at org.graalvm.nativeimage.builder/com.oracle.svm.core.jni.JNIGeneratedMethodSupport.nativeCallEpilogue(JNIGeneratedMethodSupport.java:62)
at org.newsclub.net.unix.NativeUnixSocket.configureBlocking(Native Method)
at org.newsclub.net.unix.AFCore.configureVirtualBlocking(AFCore.java:410)
at org.newsclub.net.unix.AFSocketImpl.accept0(AFSocketImpl.java:284)
at org.newsclub.net.unix.AFServerSocket.accept1(AFServerSocket.java:311)
at org.newsclub.net.unix.AFServerSocket.accept(AFServerSocket.java:305)
at org.newsclub.net.unix.AFUNIXServerSocket.accept(AFUNIXServerSocket.java:162)
at org.newsclub.net.unix.domain.MassiveParallelTest$Server.acceptJob(MassiveParallelTest.java:237)
at java.base@21.0.1/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base@21.0.1/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base@21.0.1/java.lang.Thread.runWith(Thread.java:1596)
at java.base@21.0.1/java.lang.VirtualThread.run(VirtualThread.java:309)
at java.base@21.0.1/java.lang.VirtualThread$VThreadContinuation$1.run(VirtualThread.java:190)
Restarting failed server job
Building and Maintaining junixsocket's Reachability Metadata
The required GraalVM metadata is currently obtained by running junixsocket's selftest with GraalVM's native-image-agent, and then manually separated into configurations that are relevant for junixsocket-common
, and those specifically for junixsocket-selftest
.
A shell script that exercises this path is provided (tested on macOS):
# Make sure GraalVM is enabled, e.g.:
export JAVA_HOME=/Library/Java/JavaVirtualMachines/graalvm-ce-java17-22.2.0/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH
# Run selftest with native-image-gent, build and run native-image version of selftest
cd junixsocket/junixsocket-native-graalvm
bin/build-selftest
Before a new release is drafted, this script needs to be run.
If there are metadata changes, they will be reported using the above script.
The new metadata files are stored under
junixsocket/junixsocket-native-graalvm/output/META-INF/native-image/
.
Their content needs to be
analyzed and distributed among junixsocket-common
, junixsocket-selftest
, etc., by storing the
relevant metadata in the corresponding files under src/main/resources/META-INF/native-image
(subdirectories artifactId/groupId
, e.g.,
com.kohlschutter.junixsocket/junixsocket-native-graalvm
).