2015-10-05 2 views
8

今日は非常に予期しないエラーが発生しましたが、問題全体を解決する方法を見つけることができましたが、それはしました。JDK 8でコンパイルされているが、JRE 7をターゲットにしているConcurrentHashMapアプリケーションがクラッシュする

私が使っているコードはもともとJRE 7をターゲットとしたJDK 7環境で書かれていました。コードではConcurrentHashMapを使用していましたが、マップ内のキーを反復処理する必要がありました。このため私はmap.keySet()を使用していましたが、JavaDocsによればSet<K>が返されます。これは、ビルド環境がJDK8に切り替わるまでうまくいった。

JDK8に移動したとき、私はjavacを呼び出すときに1.7のターゲット/ソースを呼び出していました。だから、地図のキーを反復したいときにコードが正しく機能しなくなったとき、私はかなり驚いていました。エラーはスローされず、例外もなくスレッドが単に停止しました。いくつかの調査を行った後、ConcurrentHashMapのJava8の実装である.keySet()メソッドがKeySetView<K,V>を返すことがわかりました。

map.keySet()を使用してEnumeration<K>map.keys()に変更することで問題を解決しました。

私の推測では、JDK8が使用されて以来Java7をターゲットにしてプロジェクトがコンパイルされていましたが、Java8ライブラリが含まれていましたが、ミスマッチが発生したときにエラーまたは例外がスローされませんでした。

としては、ここで尋ねたコードスニペットです:私たちは、Windows 2012サーバ上で1.7のターゲットとjavacでソース1.7を使用してOracle JDK 8ビルド40を使用してコンパイルしている

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); 
     } 
    } 
} 

コードは、Windows 2012サーバーで実行されているOracle JVM 7ビルド25を使用して実行されています。古いバージョンをターゲット-source引数を使用して、より新しいJDKを使用してプロジェクトをビルドするたびに

+0

JDK 8を取り付けた後にクリーニングを試しましたか? –

+0

@sureshビルド環境はJenkinsです。 JDK8に移動すると、ワークスペースが削除され、新しいビルドが実行されました。これはコードがSVNから新しくチェックアウトされてビルドされたことを意味します。私はantで "クリーンな"ターゲットへの標準的な呼び出し以外の方法でそれをクリーニングすることが何を意味するのか分かりません。 – JRSofty

+0

あなたはコードスニペットと正確なJVMバージョンとベンダー+ OSを投稿できますか? KeySetView を実装していますので、これは少なくとも実際には問題ではありません。 – salyh

答えて

12

私は1.7 -target 1.8 -sourceあなたのJava 8でコードとjavacをコンパイルしたJava 7でそれを実行する場合、私はバイトコードが

 
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  
のように見えるためです

 
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) 

を取得

を明示的に参照し、Java 7には存在しないConcurrentHashMap $ KeySetViewを明示的に参照してください。私はJava 1.7.0_79および1.8のMacを使用しています。あなたは(のみ地図インターフェイスを使用)にコードを変更した場合0_45

は:

private Map<String, Object> map = new ConcurrentHashMap<String, Object>(); 

それは私のための仕事します。バイトコードは次のようになります

 
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  
+1

興味深い。私はジェネリックマップインターフェイスの使用については考えていませんでした。残念ながら、私は 'NoSuchMethodError'を見たことはありません。私は少なくともそれを期待していただろう。私は、エラー処理が実際にすべて正しくキャッチしているかどうかを確認するために、明日もcodを調べなければならないでしょう。ありがとう。 – JRSofty

+0

私は今朝自分のコードを調べましたが、すべてを蹴る呼び出しはtry catchでラップされていますが、例外を捕捉するだけで 'NoSuchMethodError'は' Throwable'ですが 'Exception'はありません。これにより、プログラムが壊れたときにエラーが表示されなかった理由がわかります。 – JRSofty

+0

@JRSofty Javaが回復できない問題が発生した場合、それは['Error'](https://docs.oracle.com/javase/8/docs/api/java/lang/Error)になります。 .html)は 'Throwable'のサブクラスであり、' Exception'ではありません。 – Powerlord

2

、あなたはこのコンパイラの警告を取得します:それは何を意味するのかについて

warning: [options] bootstrap class path not set in conjunction with -source 1.7

This blog entry会談。

Javaは古い言語の規則を使用して新しいクラス・ライブラリを使用してコンパイルするため、基本的にこの警告が表示されます。また、内部クラスのいくつかを移動したJava 8バージョンとの互換性の問題があります。

解決策は、-bootclasspath引数を使用して、コンパイル時に古いバージョンのrt.jarにポイントすることです。

+0

これで '-bootclasspath'オプションを使って正しいrt.jarを指すように追加すると、期待どおりの作業ができるはずです。私の修正はおそらくJRE 8を実行しているシステムにアプリケーションをデプロイするときに意味があると思いますが、それを壊す心配はありません。私が興味を持っているのは、なぜ私がプログラムを実行したときに例外やエラーが発生しなかったかです。私のためにスレッドはちょうど停止し、例外はスローされませんでした。 – JRSofty

+0

ビルド環境に関する情報をもう一度おねがいします。私はこれをできるだけ早く修正する予定です。 'bootclasspath'オプションを' javac' antタスクに追加したことはありません。私はすぐにそれをやっていきます。 – JRSofty

関連する問題