2016-10-25 8 views
2

私は、それがどこから呼び出されたかを知る必要があるaspectj面に取り組んでいます。現時点では、私はこの情報にアクセスするには数百マイクロ秒かかるが、この情報にアクセスするにはすぐに発信者情報にアクセスする

new Throwable().getStackTrace(); 

を使用しています。

私はSecurityManagerを見てきましたが、それは私にクラス名を得ることができるようです。

私が見逃した他の選択肢はありますか? @ apanginの答えに私のコメントに言及

更新

JMHベンチマーク結果:

Benchmark      Mode Cnt  Score Error Units 
MyBenchmark.javalangaccess13i avgt 100 2025.865 ± 8.133 ns/op 
MyBenchmark.javalangaccess2i avgt 100 2648.598 ± 24.369 ns/op 
MyBenchmark.throwable1   avgt 100 12706.978 ± 84.651 ns/op 

ベンチマークコード:Windowsの10、JDK 1.8の下で実行さ

@Benchmark 
public StackTraceElement[] throwable1() { 
    return new Throwable().getStackTrace(); 
} 

@SuppressWarnings("restriction") 
@Benchmark 
public static StackTraceElement javalangaccess2i() { 
    Exception e = new Exception(); 
    return sun.misc.SharedSecrets.getJavaLangAccess().getStackTraceElement(e, 2); 
} 

@SuppressWarnings("restriction") 
@Benchmark 
public static StackTraceElement javalangaccess13i() { 
    Exception e = new Exception(); 
    return sun.misc.SharedSecrets.getJavaLangAccess().getStackTraceElement(e, 13); 
} 

をテストします。 0_112、Dell XPS13 9343(i5-5200U @ 2.2GHz)

+1

明白な疑問はなぜこれが必要なのかです。スタックトレースを取得するには、さまざまな理由からかなりの時間がかかります。そのため、スタックトレースを読み込むことなく、元の問題の代替ソリューションを見つけることをお勧めします。 – biziclop

+2

発信者情報の取得が遅いです。すべてのロギングフレームワークには、たとえばその問題があります。また、パフォーマンスが必要な場合は、そのような情報をオンにしないようにアドバイスします。 – zapl

+0

他のコメントは次のように述べています。欠けていることは、何とか解決する必要のある矛盾する要件があることです。合理的で堅牢な方法ですべてを満たすことができないので、 – GhostCat

答えて

5

残念ながら、純粋なJava 8で呼び出し元フレームを取得するには、Throwable.getStackTrace()しか実行可能な選択肢には見えません。

ただし、選択した1つのスタックフレームにアクセスするためのJDK固有のトリックがあります。
これは非標準のsun.misc.SharedSecrets APIを使用しています。

public static StackTraceElement getCaller() { 
    Exception e = new Exception(); 
    return sun.misc.SharedSecrets.getJavaLangAccess().getStackTraceElement(e, 2); 
} 

ここで、2は必要なフレームのインデックスです。

これは最新のJDK 8までは正常に動作しますが、JDK 9ではプライベートAPIにアクセスできません。良いニュースは、Java 9に新しい標準Stack-Walking APIがあることです。ここではJavaの9

public static StackWalker.StackFrame getCaller() { 
    return StackWalker.getInstance(Collections.emptySet(), 3) 
      .walk(s -> s.skip(2).findFirst()) 
      .orElse(null); 
} 

のJavaの両方古いと新しいバージョンに適しています代替オプションで同じことを行う方法で、JVMTI GetStackTrace関数です。しかし、ネイティブコードをリンクする必要があります。

+0

この非常に完全な答えに感謝します。私はあなたの提案をJMHでテストしましたが、Throwable.getStackTrace()は約13us、SharedSecrets APIは2〜3usのアクセスをしています。私はJava 9 APIを完全なものとしてテストしようとしていましたが、Eclipseを機能させるためにツールを変更する必要があり、それを壊すことに心配しています。しかし、https://github.com/pingtimeout/stack-walker-benchmarkのベンチマークは、その速度が遅いことを示しています。 – Ian

+0

SharedSecrets APIのように見えるのは、私の最初のテストよりも、持続時間がかなり変わる可能性があります。私は一度私は私のアプリケーションに入れて2つの呼び出しの合計のための値を20us(と600us以上の外れ値)を見ている。 – Ian

+0

私はAspectJに基づいたはるかに優れたソリューションを見つけたと思います。 – kriegaex

1

あなたはAspectJについて話しています。だから、任意の反射を必要としませんが、ちょうどAspectJのは、このようなcall()ポイントカットとの組み合わせでthisEnclosingJoinPointStaticPart.getSignature()手段としてオンボードで使用することができます。

ドライバアプリケーション:

package de.scrum_master.app; 

public class Application { 
    private static final long NUM_LOOPS = 1000 * 1000; 

    public static void main(String[] args) { 
     Application application = new Application(); 

     long startTime = System.nanoTime(); 
     for (long i = 0; i < NUM_LOOPS; i++) 
      application.doSomething(); 
     System.out.printf(
      "%-40s | %8.3f ms%n", 
      "AspectJ thisEnclosingJoinPointStaticPart", 
      (System.nanoTime() - startTime)/1.0e6 
     ); 

     startTime = System.nanoTime(); 
     for (long i = 0; i < NUM_LOOPS; i++) 
      application.doSomething2(); 
     System.out.printf(
      "%-40s | %8.3f ms%n", 
      "Throwable.getStackTrace", 
      (System.nanoTime() - startTime)/1.0e6 
     ); 

     startTime = System.nanoTime(); 
     for (long i = 0; i < NUM_LOOPS; i++) 
      application.doSomething3(); 
     System.out.printf(
      "%-40s | %8.3f ms%n", 
      "SharedSecrets.getJavaLangAccess", 
      (System.nanoTime() - startTime)/1.0e6 
     ); 
    } 

    public void doSomething() {} 
    public void doSomething2() {} 
    public void doSomething3() {} 
} 

は、アスペクト比:

package de.scrum_master.aspect; 

import de.scrum_master.app.Application; 
import sun.misc.SharedSecrets; 

public aspect MyAspect { 
    before() : call(* Application.doSomething()) { 
     Object o = thisEnclosingJoinPointStaticPart.getSignature(); 
     //System.out.println(o); 
    } 

    before() : call(* Application.doSomething2()) { 
     Object o = new Throwable().getStackTrace()[1]; 
     //System.out.println(o); 
    } 

    before() : call(* Application.doSomething3()) { 
     Object o = SharedSecrets.getJavaLangAccess().getStackTraceElement(new Throwable(), 1); 
     //System.out.println(o); 
    } 
} 

コンソールログ:

AspectJ thisEnclosingJoinPointStaticPart |  7,246 ms 
Throwable.getStackTrace     | 1852,895 ms 
SharedSecrets.getJavaLangAccess   | 1043,050 ms 

ご覧のとおり、AspectJは次善のリフレクションベースの方法より約140倍高速です。あなたが縦横にprint文のコメントを解除する場合

はところで、あなたは、出力のこれらの3種類を参照してください。

void de.scrum_master.app.Application.main(String[]) 
de.scrum_master.app.Application.main(Application.java:16) 
de.scrum_master.app.Application.main(Application.java:21) 

をお楽しみください!

+0

+1 AspectJの組み込み機能についての良い点!残念ながら、それには厳しい制限があります。即時発呼者の静的な位置だけが提供される。場合によっては、2つのフレームをより深く見なければならない場合もあります(たとえば、観測されたメソッドがユーティリティメソッドから呼び出されたとき)。反射参照やメソッド参照による呼び出しも考慮しません。 – apangin

+0

まあ、あなたはこれらの制限について言及していませんでした。通常の場合、私のソリューションは驚くほど高速です。とにかく、それは私のための指の運動だった。私はあなたがなぜ発信者情報を実際に必要としているのだろうと思います。ビジネス上の価値はどこにありますか? – kriegaex

+0

私は元の質問の質問者ではありません:)おそらく、OPはあなたの解決策ではまったく問題ありません。私は以前も同様の問題を抱えていましたが、私の場合、興味深い呼び出し元は約4フレーム下でした。 JVMTIはこれを入手する最も速い方法でした。 – apangin

関連する問題