Java High compilation low Run error (Concurrenthashmap.keyset)

Source: Internet
Author: User
Tags maven compiler plugin

Encountered the same problem, tangled for several hours. The background is resolved by switching the Java environment to 1.7. hereby reproduced, recorded under: http://www.jianshu.com/p/f4996b1ccf2f

Problem

The local use of Maven compiles and runs everything is fine, but through CI, compiling, packaging, and publishing to the deployment environment, the runtime throws a one-glance error about the JDK version.

The error is like this:

java.lang.NoSuchMethodError: java.util.concurrent.ConcurrentHashMap.keySet() Ljava/util/concurrent/ConcurrentHashMap$KeySetView;

The report is a NoSuchMethodError:java.util.concurrent.ConcurrentHashMap mistake. So it is not difficult to isolate the reason is that CI was compiled with JDK 8来, resulting in bytecode containing the new method of JDK 8 change keyset (). Its return value is Concurrenthashmap$keysetview this JDK8 new inner class.

To further verify that the class files on the deployment server are compiled with JDK 8, I use the JAVAP this JDK's own tool to verify the following:

javap -v a.class |grep major

The returned result is

51

The problem initially revealed that the JDK version number 51 corresponds to 1.7 (or 7), and 52 is the major version of JDK 8. There are two doubts:

    • Why is the class compiled by CI using JDK 8 The result of the compilation of JDK 7?
    • Since JDK 7 compiles the class file, why does the internal class of JDK 8 appear?

Look first at the doubts. Previously, the CI was also compiled through Maven compiler plugin, and the language level could be configured in Pom.xml as follows:

<Plugin><Groupid>org.apache.maven.plugins</groupid> < artifactid>maven-compiler-plugin</ artifactid> <version>3.5.1< Span class= "Hljs-tag" ></version> < configuration> <source>1.7< Span class= "Hljs-tag" ></source> < target>1.7</target> </configuration></ PLUGIN>             

This actually corresponds to the-source and-target parameters of the Javac, what exactly do these two parameters represent?

$ javac -help-source <release>          Provide source compatibility with specified release-target <release> Generate class files for specific VM version

The source parameter refers to the code-level syntax compatibility, and the target parameter refers to the class file that produces the release version of compatibility, but only ensures that the target VM can load the class file, but it does not guarantee runtime correctness . Next, we try to compile the source code using JAVAC plus these parameters.

First we write a program, as follows:

// App.javapackage com.lambeta;import java.util.concurrent.ConcurrentHashMap;public class App { public static void main(String[] args) { ConcurrentHashMap map = new ConcurrentHashMap(); map.keySet(); }}

My native Java version is 1.8, directly using Javac to compile App.java, the results are as follows

$ javac App.java$ javap -v App.class |grep major major version: 52

If you specify the source and target parameters, then compile with Javac App.java

"1.8.0_45"...$ javac -source 7 -target 7 App.javawarning: [options] bootstrap class path not set in conjunction with -source 1.71 warning$ lsApp.class App.java

Here is a warning, we do not look at the moment. Use JAVAP to decompile the App.class and observe the return value of major version and the keyset () method.

$ javap -v App.class...major version: 51...9: invokevirtual #4 // Method java/util/concurrent/ConcurrentHashMap.keySet:()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;...

In this way, the second puzzle is solved. A preliminary conclusion can be drawn.

Summary

When javac specifies these parameters and reduces the version number to compile, the resulting class file is identified as a lower version for the specified JVM to load. however , the keyset () method, which is compiled from the bootstrap class of JDK 8, is still the new inner class of concurrenthashmap\ $KeySetView in JDK 8. At run time, 1.7 of the JVM tries to load this class file, it must not find Keysetview as the return value of the keyset () method, error.

How to Solve

Now that you know the wrong place, it's easier to find a solution.

    • During compilation, replace the Bootstrap class
    • Replace the subclass with the parent class/interface, that is, Concurrentmap replace the Concurrenthashmap declaration

During compilation, replace Bootstrap Clas

Javac compile, you can specify Bootclasspath to replace the default load path, as follows:

javac -bootclasspath /Library/Java/JavaVirtualMachines/jdk1.7.0_60.jdk/Contents/Home/jre/lib/rt.jar -source 7 -target 7 App.java// orjavac -Xbootclasspath:/Library/Java/JavaVirtualMachines/jdk1.7.0_60.jdk/Contents/Home/jre/lib/rt.jar -source 7 -target 7 App.java

At this point, look at the results of the anti-compilation, it will be:

51...9: invokevirtual #4                  // Method java/util/concurrent/ConcurrentHashMap.keySet:()Ljava/util/Set;

At this point the major is (JDK 7), and the return value of keyset () is the Java.util.Set type in JDK 7.

Replace the subclass with the parent class/interface, that is, Concurrentmap replace the Concurrenthashmap declaration

The previous scenario, though feasible, is impractical-because it cannot be required to have two different versions of the JDK on the CI server, nor does it require that the value of the Bootclasspath be passed as the parameter value of the installation path so tightly coupled at Maven build time. So you can take a way to replace the declaration of a concrete implementation class with its interface, as follows:

package com.lambeta;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentMap;public class App { public static void main(String[] args) { ConcurrentMap map = new ConcurrentHashMap(); map.keySet(); }}

In this way, there will be no return value type of Concurrenthashmap$keysetview in the compiled bytecode. When running on JDK 7, the JVM must dynamically invoke the Concurrenthashmap keyset (): Java.util.Set method.

Conclusion
    • Ensure consistency of JDK versions for compilation, packaging, and final deployment environments
    • If there is no guarantee, try to interface programming, especially the classes provided in the JDK. The reason is that the interface is not easy to change, and the implementation class follows the principle of "wide receiving and strict hair", and the method's entry and exit parameters are variable.
Wen/lambeta (author of Jane's book)
Original link: http://www.jianshu.com/p/f4996b1ccf2f
Copyright belongs to the author, please contact the author to obtain authorization, and Mark "book author".

Java High compilation low Run error (Concurrenthashmap.keyset)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.