J2V8 icon indicating copy to clipboard operation
J2V8 copied to clipboard

java.lang.IllegalStateException: When two different JVMs use J2V8

Open angelozerr opened this issue 9 years ago • 13 comments

Perhaps this issue looks like https://github.com/eclipsesource/J2V8/issues/33, but I have a strange problem.

I'm on Windows OS 64 bits. I can start the Hello sample http://eclipsesource.com/blogs/2015/02/25/announcing-j2v8-2-0/

Now take the same sample add a loop at the end of the main (in order to that the main doesn't finish) :

import com.eclipsesource.v8.V8;

public class Hello {

    public void print(String s) {
        System.out.println(s);
    }
    public void start() {
        V8 v8 = V8.createV8Runtime();
        v8.registerJavaMethod(this, "print", "print", 
                                      new Class<?>[] { String.class });
        v8.executeVoidScript("print('Hello, World!');");

                // HERE THE MAIN DOESN'T STOP
        while(true) {

        }
    }
    public static void main(String[] args) {
        new Hello().start();
    }
}
  • Run the Hello with Eclipse (it works)
  • Run a second Hello with Eclipse, I have the foolowing problem :
Exception in thread "main" java.lang.IllegalStateException: J2V8 native library not loaded.
    at com.eclipsesource.v8.V8.checkNativeLibraryLoaded(V8.java:86)
    at com.eclipsesource.v8.V8.createV8Runtime(V8.java:74)
    at com.eclipsesource.v8.V8.createV8Runtime(V8.java:63)
    at Hello.start(Hello.java:10)
    at Hello.main(Hello.java:20)
Caused by: java.lang.UnsatisfiedLinkError: Could not load J2V8 library. Reasons: 
    no j2v8_win32_x86_64 in java.library.path

    at com.eclipsesource.v8.LibraryLoader.loadLibrary(LibraryLoader.java:71)
    at com.eclipsesource.v8.V8.load(V8.java:49)
    at com.eclipsesource.v8.V8.createV8Runtime(V8.java:72)
    ... 3 more

angelozerr avatar Apr 16 '15 15:04 angelozerr

The problem is that we blindly copy the .so file out of the jar file and use that with the library loader. If another JVM is currently running, this file is locked and we failed. The reality is, we should not blindly copy it, but rather check if it exists first.

However, to do that we first need to properly version the .so files. I will work on this. Thanks!

irbull avatar Apr 16 '15 15:04 irbull

I'm hitting this problem when running JUnit tests on android too. Something about the way JUnit runs different test classes makes it fail on the second and subsequent classes. Would be nice to find a fix for this specific case if the general fix is going to be a while, because I can't do CI as it is.

djMax avatar May 21 '15 15:05 djMax

18:29:36.036 [DEBUG] [TestEventLogger]              no j2v8_linux_x86_64 in java.library.path
18:29:36.037 [DEBUG] [TestEventLogger]              Native Library /home/stack/libj2v8_linux_x86_64.so already loaded in another classloader
18:29:36.038 [DEBUG] [TestEventLogger]                 at com.eclipsesource.v8.LibraryLoader.loadLibrary(LibraryLoader.java:71)
18:29:36.038 [DEBUG] [TestEventLogger]                 at com.eclipsesource.v8.V8.load(V8.java:51)
18:29:36.039 [DEBUG] [TestEventLogger]                 at com.eclipsesource.v8.V8.createV8Runtime(V8.java:76)
18:29:36.040 [DEBUG] [TestEventLogger]                 ... 8 more

Might there be some args to junit that will stop this?

djMax avatar May 26 '15 01:05 djMax

Yes, this is only because it tries to unpack the .so file from the .jar file. If you unpack the .so file yourself, put it in a directory (/this/that/foo) and set -Djava.library.path=/this/that/foo it should not try to unpack the file. Let me know if that works as a temporary solution.

irbull avatar May 26 '15 02:05 irbull

19:32:50.327 [DEBUG] [TestEventLogger]             java.lang.UnsatisfiedLinkError: Could not load J2V8 library. Reasons:
19:32:50.328 [DEBUG] [TestEventLogger]              no j2v8_linux_x86_64 in java.library.path
19:32:50.329 [DEBUG] [TestEventLogger]              Native Library /home/stack/libj2v8_linux_x86_64.so already loaded in another classloader

That .so exists in that place, so I think it's actually unable to load it. Setting forkEvery=1 in the GUI seems to make it work, but I haven't been able to do that in gradle.

djMax avatar May 26 '15 02:05 djMax

I got this to work by putting the .so in a directory called "jni" in what appears to be the working dir of the android Junit tests, which was not the location of gradlew, but the project (in my case "sdk") under that.

djMax avatar May 26 '15 02:05 djMax

unfortunately doesn't seem very deterministic... sometimes it works sometimes it doesn't. I don't think the .so's I have put there matter. (I certainly may not be putting them in the right place)

djMax avatar May 26 '15 03:05 djMax

I did some searching and these are two different problems. Let's leave this one for the windows issue (two different JVMs). I've filed #62 for the classloader issue.

irbull avatar May 26 '15 05:05 irbull

Hi all,

I have this same issue when running unit test... `java.lang.IllegalStateException: J2V8 native library not loaded.

at com.eclipsesource.v8.V8.checkNativeLibraryLoaded(V8.java:149)
at com.eclipsesource.v8.V8.createV8Runtime(V8.java:135)
at com.eclipsesource.v8.V8.createV8Runtime(V8.java:96)
at com.hypodiabetic.happ.integration.openaps.dev.DetermineBasalAdapterJS.<init>(DetermineBasalAdapterJS.java:55)
at com.hypodiabetic.happ.integration.openaps.dev.DetermineBasalAdapterJS_dev_Test.setUp(DetermineBasalAdapterJS_dev_Test.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Caused by: java.lang.UnsatisfiedLinkError: Could not load J2V8 library. Reasons: no j2v8_win32_x86_64 in java.library.path`

Reading this thread I am unsure what I should unpack and where to potentially fix the issue?

fyi im using the following dependency: compile 'com.eclipsesource.j2v8:j2v8:3.1.6@aar'

timomer avatar Jun 27 '16 18:06 timomer

Hi Ian,

I ran into the same issue, but in a bit different circumstances. I started using J2V8 library in a Spring MVC application, which gets deployed to a WebSphere container. The problem that I ran into is that when we restart just the module with J2V8, or uninstall the module and install it back (re-deploy), the JVM of WebSphere container keeps in JVM native memory the native DLL that got loaded by previous web application start.

To be able to have several deployments to the web application container, without having to restart it (the JVM), I was able to go with a workaround: create a new temporary directory for J2V8 each time I need it to load the DLL (calling V8.createV8Runtime(String, String) instead of plain V8.createV8Runtime()). Let me know if you want an example of how it's done.

However, this causes me the issue that the JVM of WebSphere application server gets multiple copies of V8 native library in JVM native memory, unless I restart the web server. Eventually, the native memory of JVM gets full and I start getting Native Out Of Memory exceptions when trying to deploy my application. There is no real way to un-load a native library from JVM memory without restarting the JVM: done only when class loader that loaded the lib is shred in garbage collector.

Hence, looks like we need to teach J2V8 to find and re-use a reference to an instance of loaded V8 native library.

Ian, is there any chance we could have this functionality, or we should stick to restarting the JVM of application server on each cluster node?

I'm using com.eclipsesource.j2v8:j2v8_win32_x86_64:4.6.0.

Best regards, Egor.

ebujinov avatar Oct 10 '17 13:10 ebujinov

Hi @irbull ,

Please disregard my previous comment. I was able to create a shared library with an isolated class loader in WebSphere, which made it possible to load a single copy of V8 native library into memory and avoid loading it again with re-deployments. You can consider my case closed.

Best regards, Egor

ebujinov avatar Oct 12 '17 15:10 ebujinov

It seems that I found an easier solution. If you check the exception stack trace, you see that createV8Runtime method is called twice:

at com.eclipsesource.v8.V8.checkNativeLibraryLoaded(V8.java:149)
at com.eclipsesource.v8.V8.createV8Runtime(V8.java:135)
at com.eclipsesource.v8.V8.createV8Runtime(V8.java:96)

The first call does not have parameters, and the second has 2 parameters with null actual values. The second parameter is the library path. So instead of calling the parameter-less createV8Runtime() in your code, call createV8Runtime(null, "/path/to/your/library/directory"). Be sure that the path specified by the second parameter exists and it is a directory. (The J2V8 engine does not create it.) Any other tasks - extracting the library file and loading it - are automatically done by the engine, you do not need to do anything about them.

With this solution, you can use different library paths for different JVMs. You can use even temp directories with randomly generated names. The only problem is that you will not be able to delete those temp directories from the JVM (because they will contain the native library files, and those files are used while the JVM is running) - File.deleteOnExit() will not work on them.

kokog78 avatar Jan 24 '18 12:01 kokog78

hi @irbull I I ran into the same issue, I started using J2V8-android 3.0.5 library in a android application, and start a activity in another process and then create v8 Runtime,then get error: exception java.lang.IllegalStateException: J2V8 native library not loaded

java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/pa.com.genie-iTyVuH4ffiTHUgQup1e2LA==/base.apk"],nativeLibraryDirectories=[/data/app/pa.com.genie-iTyVuH4ffiTHUgQup1e2LA==/lib/arm64, /system/lib64, /product/lib64]]] couldn't find "libj2v8.so"

is any way to resolve this problem?

carl1990 avatar Mar 18 '20 01:03 carl1990