2016-10-04 5 views
3

finalizeOperationは(大規模アプリケーションでの生産に)実行されている:クラス名に数字があるスタックトレース - なぜですか?

public interface OperationFinalizerHook { 
    void onOperationFinalize(Operation operation, Object context); 
} 
private final List<OperationFinalizerHook> operationFinalizeHooks = new ArrayList<>(); 
... 
public void finalizeOperation(Object context) { 
    final Operation operation = getOperation(); 
    operationFinalizeHooks.forEach(hook -> hook.onOperationFinalize(operation, context)); 
} 

コールツリー/スタックトレースが構築され、次の

11 at com.company.SomeClass.lambda$finalizeOperation$0 (SomeClass.java:51) 
12 at com.company.SomeClass$$Lambda$135/2085968933.accept (Unknown source) 
13 at java.util.ArrayList.forEach (ArrayList.java:1249) 
14 at com.company.SomeClass.finalizeOperation (SomeClass.java:51) 

私は12行に興味 - この名前が来ませんから?なぜ私はクラスの名前を期待する乱数がありますか?

編集: はここblog post mentioned byニクラスPからのコードです:

public class Test { 
    public static void main(String... args) { 
     List<String> names = Arrays.asList("adam", ""); 
     Stream lengths = names.stream().map(name -> check(name)); 
     lengths.count(); 
    } 
    public static int check(String s) { 
     if (s.equals("")) 
      throw new IllegalArgumentException(); 
     return s.length(); 
    } 
} 

しかし、結果は、この数値の名前が含まれていない、スタックトレースは、(jdk8u102)です:jdk8u25数に

Exception in thread "main" java.lang.IllegalArgumentException 
    at Test.check(Test.java:19) 
    at Test.lambda$main$0(Test.java:12) 
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) 
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) 
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) 
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) 
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) 
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) 
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) 
    at Test.main(Test.java:14) 

そこにいます:

Exception in thread "main" java.lang.IllegalArgumentException 
    at Test.check(Test.java:18) 
    at Test.lambda$main$0(Test.java:11) 
    at Test$$Lambda$1/1554547125.apply(Unknown Source) 
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) 
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) 
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512) 
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502) 
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) 
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) 
    at java.util.stream.LongPipeline.reduce(LongPipeline.java:438) 
    at java.util.stream.LongPipeline.sum(LongPipeline.java:396) 
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526) 
    at Test.main(Test.java:13) 

答えて

5

ここでは重複する問題が2つあります。まず、ラムダ式をオブジェクト型に変換するときに、機能インタフェースを実装するものがなければなりません。詳細はあまり重要ではありません。インターフェイスを実装し、ラムダ式のコードまたはメソッド参照のターゲットを呼び出す何かがあることを理解する必要があります。

現在のJRE実装では、匿名クラスが生成されます。匿名クラスは、名前が示すように、一意であるという名前に依存しません。クラス名の後に表示される数字は、このプロパティのアーティファクトです。どちらの方法でも、番号を付けても使わなくても、ClassLoaderでこれらのクラスをルックアップすることはできません。

スタックトレースに合成アーチファクトがあることは、Javaにとって新しいものではありません。内部クラスを使用するときにアクセッサメソッドが生成されます。

import java.util.*; 
import java.util.function.Function; 
import java.util.stream.Stream; 

public class Test { 
    public static void main(String... args) { 
     List<String> names = Arrays.asList("adam", ""); 
     Stream lengths = names.stream().map(new Function<String, Integer>() { 
      public Integer apply(String name) { 
       return check(name); 
      } 
     }); 
     lengths.count(); 
    } 
    private static int check(String s) { 
     if (s.equals("")) 
      throw new IllegalArgumentException(); 
     return s.length(); 
    } 
} 
Exception in thread "main" java.lang.IllegalArgumentException 
    at Test.check(Test.java:17) 
    at Test.access$000(Test.java:5) 
    at Test$1.apply(Test.java:10) 
    at Test$1.apply(Test.java:8) 
    at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) 
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) 
    … (shortened it a bit) 
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526) 
    at Test.main(Test.java:13) 

注ソースコードに表示されていないスタックトレース、内access$000の存在;それは関連付けられた行番号は無意味です、それは外部クラス定義のちょうど始まりです。

最近、JREバージョンの匿名クラスの合成メンバーを省略するために、スタックトレースの生成に変更があったようです。これは、反射呼び出しのスタックトレースにも影響します。 MethodHandleインスタンスを使用します。これは、ほとんどの使用例では便利だと思われるかもしれませんが、呼び出し元がインターフェイスメソッドを呼び出すとスタックトレースが報告するため、呼び出し元と呼び出し先の間に不一致がある可能性があります。

import java.util.*; 
import java.util.stream.Stream; 

public class Test { 
    public static void main(String... args) { 
     Stream.of("adam", "", null).filter("foo"::contains).count(); 
    } 
} 

ReferencePipeline.java:174Predicateインターフェースのacceptメソッドの呼び出しを含むが、クラスString方法containsで終わる

Exception in thread "main" java.lang.NullPointerException 
    at java.lang.String.contains(String.java:2133) 
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174) 
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) 
… 

を印刷します。私たちは、最大それはアウトすることができます:

import java.util.*; 
import java.util.stream.Stream; 

public class Test { 
    public static void main(String... args) { 
     Stream.of("adam", "", null).filter(String::isEmpty).count(); 
    } 
} 

は、最新のJRE上で、次の生成されます。

Exception in thread "main" java.lang.NullPointerException 
    at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:174) 
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) 
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) 
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) 
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) 
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) 
    at java.util.stream.LongPipeline.reduce(LongPipeline.java:438) 
    at java.util.stream.LongPipeline.sum(LongPipeline.java:396) 
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526) 
    at Test.main(Test.java:6) 

はさらに混乱することができ、最終的Stringインスタンス上isEmptyを起動する合成コードを省いReferencePipeline.java:174interfaceメソッドの呼び出しのみを含み、interfaceインスタンスはnullではありません(これはそのコードの前半部分で確認済みです)。

これは動きのある開発であることに注意してください。 Java 9では、隠し/反射スタックフレームの設定された処理でアプリケーションが独自のスナップショットを生成できるようになるStackWalker APIがあります。アプリケーションがこのAPIを使用して予測可能なスタックトレースを作成すると、すなわちThrowable.getStackTrace()の特定の動作にもう依存しない場合、スローテーブルの動作はJVMオプションまたはシステムプロパティを介して設定可能になります。

1

番号は来ています匿名クラスから、ラムダ操作用に作成されたJVM - ここをクリックしてください:the-dark-side-of-lambda-expressions-in-java-8

+0

リンクは、特定の内部クラスに?この番号は実際何を表していますか? –

+0

実際にはありません - 私はここで何かを見つけました:(http://www.retrologic.com/innerclasses.doc7.html) –

+0

クイックスキャンから、その番号を生成するものに何も表示されませんでした手元に)。あなたの投稿は本当にその質問に答えるべきです。 –

関連する問題