I ran into a very unexpected error today and while I was able to find a way to fix the problem as a whole I'm not sure I completely understand why it did what it did.
The code I'm working with was originally written with a JDK 7 environment of course targeting JRE 7. In the code I was using a ConcurrentHashMap
and needed to iterate over the keys in the map. For this I was using the map.keySet()
which according to the JavaDocs should return a Set<K>
. This worked fine until our build environment switched to JDK8.
When we moved to JDK8 I ensured that I was calling a target/source for 1.7 when calling the javac. So I was pretty surprised when the code started failing right when it wanted to iterate through the keys of the map. No error was thrown, no exception, the thread just simply stopped. After doing some research I found that Java8's implementation for ConcurrentHashMap
the .keySet()
method returns a KeySetView<K,V>
.
I fixed the problem by switching from using the map.keySet()
to getting an Enumeration<K>
using map.keys()
.
Now my guess as to the problem is that although the project was compiled targeting Java7 since the JDK8 was used the Java8 libraries were included, but why didn't it thrown an error or an exception when it hit the mismatch?
As asked here is a code snippet:
class MapProcessing
{
private ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<String, Object>();
public MapProcessing()
{
map.put("First",new Object());
map.put("Second",new Object());
map.put("Third",new Object());
}
public void processing()
{
// when calling this type of loop causes a freeze on our system.
for(String key : map.keySet())
{
System.out.println(key);
}
}
public void working()
{
// This is what I had to do to fix the problem.
Enumeration<String> keys = map.keys();
while(keys.hasMoreElements())
{
String key = keys.nextElement();
System.out.println(key);
}
}
}
We are compiling using Oracle JDK 8 build 40 using a target for 1.7 and source 1.7 in the javac on a Windows 2012 server.
The code is running using Oracle JVM 7 build 25 running on Windows 2012 server.
Whenever you build a project using a newer JDK using the
-source
argument targeting an older version, you'll get this compiler warning:warning: [options] bootstrap class path not set in conjunction with -source 1.7
This blog entry talks about what it means.
Basically, you get this warning because Java is compiling it using older language rules but against the newer class library... and there are some compatibility issues with the Java 8 versions as Oracle moved some of the internal classes around.
The fix is to use the
-bootclasspath
argument to point it at thert.jar
from the older version while compiling.If i compile your code with Java 8 and javac -source 1.7 -target 1.8 and then run it with Java 7 i get an
This is because the the byte code looks like
and referring explicitly to ConcurrentHashMap$KeySetView which is not present in Java 7. I am on Mac with Java 1.7.0_79 and 1.8.0_45
If you change the code to (only use the Map Interface):
then it work's for me. Bytecode then looks like