使用 JDK 8 编译但针对 JRE 7 的 ConcurrentHashMap 崩溃应用程序 [英] ConcurrentHashMap crashing application compiled with JDK 8 but targeting JRE 7

查看:29
本文介绍了使用 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_1

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.

解决方案

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      

这篇关于使用 JDK 8 编译但针对 JRE 7 的 ConcurrentHashMap 崩溃应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆