I have a native application that launches a JVM and interacts with it through the JNI API.
The native application constructs a (complex) JVM object and passes it as parameter in a method call.
The problem is that in some cases, on determinate inputs, the JVM crashes during the execution of the method with:
Exception in thread "Thread-1" java.lang.IncompatibleClassChangeError
I learned that this exception is thrown by the JVM when some incompatible binary changes has been made to a class used by a client that is not aware of the changes. However, I don't see how this could happen: in my case the JVM has only one fat jar in the classpath. I can't find duplicate classes in it, and the client code is always compiled to produce the fat jar.
On "What causes java.lang.IncompatibleClassChangeError?" I found that there is another potential reason: a method called via JNI with objects of the wrong type, e.g. in the wrong order. So, I added dynamic checks with IsInstanceOf to check the type of any object passed to the JVM. Unfortunately, all the checks succeed.
How can I debug this? The exception has no message (e.g. no message like "Expected non-static field ChildClass.message"). This may hint that it's a problem caused by the JNI, and not by the "more common" case of an incompatible binary change in a library.
I tried -verbose:class but I don't see anything strange in the log. The last loaded class seems a common Scala lambda function, nothing strange:
[Loaded a.b.c.FastPrettyPrinter$$$Lambda$457/868352876 from a.b.c.FastPrettyPrinter$]
Is there an exhaustive list, or explanation, of what might cause an IncompatibleClassChangeError when calling JVM methods via the JNI?
Related
I need to take input at runtime and create a new class with it, and load it into the JVM to run.
Although this can quickly become an abstract question with minimal concrete answers, and therefore be marked "Not appropriate for stackoverflow", I really am looking for some concrete answers on this.
I have read a ton of resources regarding this. Answers I've uncovered range from manipulating the byte code (not even sure how I would begin that), to recursively loading all classes in the JVM which doesn't actually seem possible because this seems like it would cause an infinite loop type scenario.
Currently I'm writing a .java file dynamically and then getting a refference to the System compiler and using that to compile to a class file. Then I use the defineClass() method on the ClassLoader class to attempt to load it into the JVM.
After performing these steps I'm getting a NoClassDefError thrown which makes sense, I just need some input on how to correctly get this new, dynamically written class loaded into the JVM?
In response to a concern brought forth by someones comment, this is 100% experimentation and for learning purposes only. This is not something that would go into production.
While im trying to run TexturePacker gui jar file from command line, its showing this error. Please see the image below. Thank you for your help in advance.
I don't think we can give you a specific solution, but the general diagnosis is that you have called a native method for which the the JVM hasn't been given an implementation. Possible causes are:
missing native libraries (DLLs, whatever)
the JVM can't find the native libraries
the application hasn't told the JVM to load the library (e.g. by calling System.loadLibrary(...)
the library doesn't define the required native method with specific method signature that you are looking for.
In the example you have shown us, the JVM is failing to find the native code implementation for a method with this signature:
void setWindowProc(java.lang.reflect.Method)
that is declared in the class org.lwjgl.opengl.WindowsDisplay. The problem could be a missing or incorrectly configured library, or it could be due to a version mismatch between the library's DLL and Java code.
Based on a 2 minute read of the "getting started" example for LWJGL, it doesn't look like there is an explicit initialization call that your application needs to make. (But I may have missed something, or you may be using a version of the library that doesn't match the "getting started" docs.)
For Java compiler to resolve reference to another class in different file needs to be on classpath.
I know that if the class is not found on the classpath or the method signatures do not match some Exceptions are thrown.
However, is there any validation that compares compile time classpath with runtime classpath when it comes to method implementations?
What If I provide classpath with method signatures matching the ones during compile time but with different implementation.
Will there be no Validation error or any Exception thrown? That seems very insecure.
Generally speaking, no, as long as the signatures match some JVMs can even replace methods at runtime (modulo some additional constraints).
Of course a custom classloader implementation could perform extended validations, such as checking hashes/signatures.
What the JVM does validate is the standards-conformance of the bytecode. I.e. bytecode cannot reference nonexistent stack slots, do arbitrary jumps or similar things that would lead to illegal behavior inside the JVM.
According this topic:
Reasons of getting a java.lang.VerifyError
java.lang.VerifyError obtains if version of execution jvm newer than jvm that was used for compilation.
Always we can fix this problem using following jvm option: -XX:-UseSplitVerifier.
According this:
https://stackoverflow.com/a/16467026/2674303
to use this option is 'perfectly safe'.
Thus I don't understand why does java.lang.VerifyError is problem that prevents succesful compilation. Please clarify. Maybe it is not safe for libraries which instrument bytecode?
The Q&A's that you link to refer to a specific kind of verification error that it is safe to work around by using an alternative verifier. However, there are other kinds of verify error that you should not ignore ... and that you cannot deal with that way.
In short, the advice in the linked question does not apply in general. In general:
Switching to the alternative verifier probably won't help.
If you disable the verifier entirely, you risk running code that may violate the JVM's runtime type-safety (etc) constraints. That can lead to security issues and / or heap corruption and hard JVM crashes.
If you have a specific VerifyError that you need advice on, please include the full exception message and stacktrace, and describe the situation in which it occurs. Note that Andrey's answer is correct that a common cause of verification errors is bugs in code that is doing "bytecode engineering" for various purposes. Often, the fix is to change to a different version of the corresponding dependency.
VerifyError happens when your byte code is incorrect in some way. For example, when it tries to read from an uninitialized variable or assigns a primitive value to a field of type Object. An instrumentation library may have bugs leading to generation of such malformed bytecode. If you can share the exact details of the error, we could probably be more specific about the exact cause.
The goal is to force tools and libraries generate correct StackMapTable attribute whenever they manipulate Java bytecode, so that JVM does not have to do slow and complicated type inferencing phase of verification, but rather do only fast and simple type checking phase.
-XX:-UseSplitVerifier has been deprecated in Java 8, it won't help any more.
VerifyError is thrown during class loading by JVM, so it appears on runtime level, not compile level.
Not always we can fix probem by using old verifier. There is a particular case when this helps. It only helps when class is propher for older verifier and is missing StackMapTable which was introduced in JVM 1.6 and are obligatory in JVM 1.7
If you want to get rid VerifyError and UseSplitVerifier does not help that mean that your class is incorrect from JVM point of view. You can still turn off whole verification, but this can cause problems.
Bytecode manipulation is always danger when you don't know what are your'e doing. Now main bytecode manipulation libraries support StackMapFrames calculations so they could generate bytecode which is propher for every VM. But still, user can generate bytecode which is incorrect on class file level, in that case JVM will still thrown VerifyError on load, SplitVerifier won't help. Only disabling verification will force VM to load that class, but could be some error when it will be executing.
A quick question regarding the java.lang.VerifyError exception. Suppose I get an error that looks like this:
Java call terminated by uncaught Java exception: java.lang.VerifyError:(class: com/.../MyClassName, method: <init> signature: (Ljava/io/Reader;)V) Incompatible argument to function
Could you help me with understanding what the "init" and what the "(Ljava/io/Reader;)V)" parts pertain to? They don't look like method names or signatures to me, but I'm not too familiar with java. Thanks!
This error means that somewhere in your code, you tried to call a constructor (the <init> method) passing in the wrong set of arguments. The expected argument was a Reader object.
This probably meant that you previously compiled a class file, then changed the class definition in some way without recompiling the class file. Consequently, your code tries to call a function that no longer exists. Try recompiling the code and see if that fixes it.
Hope this helps!
If you are running your application on an application server, it could be a class loading problem.
You compiled your code against a library and when you try to run your code it is running against a different (older?) version of the library.
The older library probably doesn't have that method or constructor.
Just to leave track of a different cause.
Always on an application server (in my case WildFly 10), you might be loading the same library on a modules and on the EAR lib. If this library contains an interface that needs to be implemented by the module, this might cause a conflict since the same class / interface loaded by two different class loaders are considered to be two different types.