2016-10-25 3 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 

벤치 마크 코드 : 윈도우 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

불행히도 Throwable.getStackTrace()은 순수 자바 8에서 호출자 프레임을 얻는 유일한 실행 가능한 옵션 인 것 같습니다.

그러나 선택한 스택 프레임 하나에 액세스하는 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이 있다는 것입니다. 여기에 자바 9.

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

자바의 모두 이전 및 최신 버전 잘 작동 대안 옵션에 동일한 작업을 수행하는 방법은, JVMTI GetStackTrace 기능입니다. 그것은 네이티브 코드를 링크해야합니다.

+0

이 답변에 대한 많은 감사드립니다. JMH를 통한 제안에 대한 몇 가지 테스트를 실행했으며 Throwable.getStackTrace()는 약 13us가 걸리는 반면 SharedSecrets API는 액세스 된 항목에 따라 2 ~ 3us가 걸리는 것으로 보입니다. 완성을 위해 자바 9 API도 테스트 할 예정 이었지만, Eclipse를 작동 시키려면 툴링을 변경해야하고,이를 깨뜨리는 것은 불안합니다. 그러나 https://github.com/pingtimeout/stack-walker-benchmark의 벤치 마크는 느린 것을 제안합니다. – Ian

+0

SharedSecrets API가 내 초기 테스트보다 지속 시간이 약간 더 가변적 인 것처럼 보입니다. 난 내 응용 프로그램에 넣어 한번 두 통화의 합계에 대한 20us (그리고 이상 600us 이상) 값을보고 있어요. – Ian

+0

AspectJ를 기반으로 훨씬 더 나은 솔루션을 발견했다고 생각합니다. – kriegaex

1

당신은 AspectJ에 대해 이야기하고 있습니다. 그래서 당신은 어떤 반사를 필요는 없지만 단지 call() 포인트 컷과 AspectJ를 조합하여 같은 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 배 빠릅니다.당신이 측면에서 인쇄 제표의 주석을 경우

은 BTW, 당신은 출력의 세 가지 유형을 참조하십시오

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 내장 기능에 대한 좋은 지적! 불행히도 심각한 제한이 있습니다. 직접 실행 호출자의 정적 위치 만 제공됩니다. 때로는 두 프레임을 더 깊게 볼 필요가 있습니다 (예 : 관찰 된 메소드가 유틸리티 메소드에서 호출 될 때). 반사 참조와 메소드 참조를 통한 호출도 고려하지 않습니다. – apangin

+0

글쎄, 당신은 이러한 제한 사항을 언급하지 않았다. 정상적인 경우 내 솔루션은 단지 blazingly 빠릅니다. 어쨌든, 그것은 나를위한 손가락 운동이었습니다. 왜 실제로 발신자 정보가 필요한지 궁금합니다. 비즈니스 가치는 어디에 있습니까? – kriegaex

+0

나는 원래 질문의 묻는 사람이 아니다 :) 어쩌면, OP는 당신의 해결책에 절대적으로 좋을 것이다. 방금 전에 비슷한 문제가 있었는데 흥미로운 호출자는 약 4 프레임 아래였습니다. JVMTI는 그것을 얻는 가장 빠른 방법 인 것처럼 보였다. – apangin

관련 문제