使用 JDK 8 编译但针对 JRE 7 的 ConcurrentHashMap 崩溃应用程序
我今天遇到了一个非常意外的错误,虽然我能够找到解决整个问题的方法,但我不确定我是否完全理解它为什么会这样做.
我正在使用的代码最初是使用 JDK 7 环境编写的,当然目标是 JRE 7.在代码中,我使用的是 ConcurrentHashMap
并且需要遍历映射中的键.为此,我使用了 map.keySet()
,根据 JavaDocs 应该返回一个 Set<K>
.这一直很好,直到我们的构建环境切换到 JDK8.
当我们迁移到 JDK8 时,我确保在调用 javac 时调用的是 1.7 的目标/源.因此,当代码在想要遍历地图的键时开始失败时,我感到非常惊讶.没有抛出错误,没有异常,线程只是简单地停止了.在做了一些研究之后,我发现 Java8 对 ConcurrentHashMap
的实现 .keySet()
方法返回一个 KeySetView<K,V>
.
我通过从使用 map.keySet()
切换到使用 map.keys()
获取 Enumeration<K>
解决了这个问题>.
现在我对这个问题的猜测是,虽然该项目是针对Java7编译的,因为使用的是JDK8,但包含了Java8库,但是为什么它在遇到不匹配时没有抛出错误或异常?
这里问的是一个代码片段:
类 MapProcessing{私有 ConcurrentHashMap<字符串,对象>map = new ConcurrentHashMap<String, Object>();公共地图处理(){map.put("First",new Object());map.put("第二个",new Object());map.put("第三个",new Object());}公共无效处理(){//当调用这种类型的循环会导致我们的系统冻结.for(字符串键:map.keySet()){System.out.println(key);}}公共无效工作(){//这是我必须做的来解决这个问题.枚举<字符串>键 = map.keys();同时(keys.hasMoreElements()){字符串键 = keys.nextElement();System.out.println(key);}}}
我们在 Windows 2012 服务器的 javac 中使用 Oracle JDK 8 build 40 进行编译,目标为 1.7,源为 1.7.
代码使用在 Windows 2012 服务器上运行的 Oracle JVM 7 build 25 运行.
解决方案如果我用 Java 8 和 javac -source 1.7 -target 1.8 编译你的代码,然后用 Java 7 运行它,我会得到一个
<上一页>线程主"java.lang.NoSuchMethodError 中的异常:java.util.concurrent.ConcurrentHashMap.keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;在 stackoverflowt.Test.processing(Test.java:20)在 stackoverflowt.Test.main(Test.java:27)这是因为字节码看起来像
<上一页>公共无效处理();代码:0:aload_01: getfield #4//字段映射:Ljava/util/concurrent/ConcurrentHashMap;4: invokevirtual #10//方法 java/util/concurrent/ConcurrentHashMap.keySet:()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;7: invokevirtual #11//方法 java/util/concurrent/ConcurrentHashMap$KeySetView.iterator:()Ljava/util/Iterator;10:astore_1并明确引用 Java 7 中不存在的 ConcurrentHashMap$KeySetView.我在 Mac 上使用 Java 1.7.0_79 和 1.8.0_45
如果您将代码更改为(仅使用地图接口):
私有映射map = new ConcurrentHashMap<String, Object>();
那么它对我有用.字节码看起来像
<上一页>公共无效处理();代码:0:aload_01: getfield #4//字段映射:Ljava/util/Map;4: invokeinterface #10, 1//InterfaceMethod java/util/Map.keySet:()Ljava/util/Set;9: invokeinterface #11, 1//接口方法 java/util/Set.iterator:()Ljava/util/Iterator;14:astore_1I 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.
解决方案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
Exception in thread "main" java.lang.NoSuchMethodError: java.util.concurrent.ConcurrentHashMap.keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; at stackoverflowt.Test.processing(Test.java:20) at stackoverflowt.Test.main(Test.java:27)
This is because the the byte code looks like
public void processing(); Code: 0: aload_0 1: getfield #4 // Field map:Ljava/util/concurrent/ConcurrentHashMap; 4: invokevirtual #10 // Method java/util/concurrent/ConcurrentHashMap.keySet:()Ljava/util/concurrent/ConcurrentHashMap$KeySetView; 7: invokevirtual #11 // Method java/util/concurrent/ConcurrentHashMap$KeySetView.iterator:()Ljava/util/Iterator; 10: astore_1
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):
private Map<String, Object> map = new ConcurrentHashMap<String, Object>();
then it work's for me. Bytecode then looks like
public void processing(); Code: 0: aload_0 1: getfield #4 // Field map:Ljava/util/Map; 4: invokeinterface #10, 1 // InterfaceMethod java/util/Map.keySet:()Ljava/util/Set; 9: invokeinterface #11, 1 // InterfaceMethod java/util/Set.iterator:()Ljava/util/Iterator; 14: astore_1
相关文章