栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

Java是否有任何机制可让VM自行跟踪方法调用,而无需使用javaagent等?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java是否有任何机制可让VM自行跟踪方法调用,而无需使用javaagent等?

JVM没有提供这样的API,即使对于以开头的代理也是如此

-javaagent
。JVM
TI是为以该
-agent
选项开头的本机代理或调试器提供的本机接口。Java代理可能会使用Instrumentation API,该API提供了类检测的底层功能,但没有直接分析功能。

有两种类型的性能分析实现,即通过采样和通过仪器。

采样通过定期记录堆栈跟踪信息(样本)来进行。这不会跟踪每个方法调用,但仍会检测到热点,因为热点在记录的堆栈跟踪中多次出现。优点是它不需要代理程序或特殊的API,并且您可以控制探查器的开销。您可以通过ThreadMXBean来实现它,该工具允许您获取所有正在运行的线程的堆栈跟踪。实际上,即使a

Thread.getAllStackTraces()
也可以,但是会
ThreadMXBean
提供有关线程的更详细的信息。

因此,主要任务是为堆栈跟踪中找到的方法实现有效的存储结构,即将同一方法的出现折叠为单个调用树项。

这是一个非常简单的采样器在自己的JVM上工作的示例:

import java.lang.Thread.State;import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;import java.util.*;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class Sampler {  private static final ThreadMXBean TMX=ManagementFactory.getThreadMXBean();  private static String CLASS, METHOD;  private static CallTree ROOT;  private static ScheduledExecutorService EXECUTOR;  public static synchronized void startSampling(String className, String method) {    if(EXECUTOR!=null) throw new IllegalStateException("sampling in progress");    System.out.println("sampling started");    CLASS=className;    METHOD=method;    EXECUTOR = Executors.newScheduledThreadPool(1);    // "fixed delay" reduces overhead, "fixed rate" raises precision    EXECUTOR.scheduleWithFixedDelay(new Runnable() {      public void run() {        newSample();      }    }, 150, 75, TimeUnit.MILLISECONDS);  }  public static synchronized CallTree stopSampling() throws InterruptedException {    if(EXECUTOR==null) throw new IllegalStateException("no sampling in progress");    EXECUTOR.shutdown();    EXECUTOR.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);    EXECUTOR=null;    final CallTree root = ROOT;    ROOT=null;    return root;  }  public static void printCallTree(CallTree t) {    if(t==null) System.out.println("method not seen");    else printCallTree(t, 0, 100);  }  private static void printCallTree(CallTree t, int ind, long percent) {    long num=0;    for(CallTree ch:t.values()) num+=ch.count;    if(num==0) return;    for(Map.Entry<List<String>,CallTree> ch:t.entrySet()) {      CallTree cht=ch.getValue();      StringBuilder sb = new StringBuilder();      for(int p=0; p<ind; p++) sb.append(' ');      final long chPercent = cht.count*percent/num;      sb.append(chPercent).append("% (").append(cht.cpu*percent/num)        .append("% cpu) ").append(ch.getKey()).append(" ");      System.out.println(sb.toString());      printCallTree(cht, ind+2, chPercent);    }  }  static class CallTree extends HashMap<List<String>, CallTree> {    long count=1, cpu;    CallTree(boolean cpu) { if(cpu) this.cpu++; }    CallTree getOrAdd(String cl, String m, boolean cpu) {      List<String> key=Arrays.asList(cl, m);      CallTree t=get(key);      if(t!=null) { t.count++; if(cpu) t.cpu++; }      else put(key, t=new CallTree(cpu));      return t;    }  }  static void newSample() {    for(ThreadInfo ti:TMX.dumpAllThreads(false, false)) {      final boolean cpu = ti.getThreadState()==State.RUNNABLE;      StackTraceElement[] stack=ti.getStackTrace();      for(int ix = stack.length-1; ix>=0; ix--) {        StackTraceElement ste = stack[ix];        if(!ste.getClassName().equals(CLASS)||!ste.getMethodName().equals(METHOD))          continue;        CallTree t=ROOT;        if(t==null) ROOT=t=new CallTree(cpu);        for(ix--; ix>=0; ix--) {          ste = stack[ix];          t=t.getOrAdd(ste.getClassName(), ste.getMethodName(), cpu);        }      }    }  }}

探查器在不通过调试API的情况下搜寻每个方法调用,使用工具将通知代码添加到他们感兴趣的每个方法中。优点是它们永远不会错过方法调用,但另一方面,它们却在执行中增加了大量开销搜索热点时可能会影响结果。而且实施起来更加复杂。我无法为您提供此类字节代码转换的代码示例。

Instrumentation
API仅提供给Java代理,但是如果您想进入Instrumentation方向,下面的程序演示了如何连接到其自己的JVM并作为Java代理加载自身:

import java.io.*;import java.lang.instrument.Instrumentation;import java.lang.management.ManagementFactory;import java.nio.ByteBuffer;import java.nio.charset.Charset;import java.nio.charset.StandardCharsets;import java.util.UUID;import java.util.zip.ZipEntry;import java.util.zip.ZipOutputStream;// this API comes from the tools.jar of your JDKimport com.sun.tools.attach.*;public class SelfAttacher {  public static Instrumentation BACK_link;  public static void main(String[] args) throws Exception { // create a special property to verify our JVM connection    String magic=UUID.randomUUID().toString()+'/'+System.nanoTime();    System.setProperty("magic", magic); // the easiest way uses the non-standardized runtime name string    String name=ManagementFactory.getRuntimeMXBean().getName();    int ix=name.indexOf('@');    if(ix>=0) name=name.substring(0, ix);    VirtualMachine vm;    getVM: {      try {      vm = VirtualMachine.attach(name);      if(magic.equals(vm.getSystemProperties().getProperty("magic")))        break getVM;      } catch(Exception ex){} //   if the easy way failed, try iterating over all local JVMs      for(VirtualMachineDescriptor vd:VirtualMachine.list()) try {        vm=VirtualMachine.attach(vd);        if(magic.equals(vm.getSystemProperties().getProperty("magic")))          break getVM;        vm.detach();      } catch(Exception ex){} //   could not find our own JVM or could not attach to it      return;    }    System.out.println("attached to: "+vm.id()+'/'+vm.provider().type());    vm.loadAgent(createJar().getAbsolutePath());    synchronized(SelfAttacher.class) {      while(BACK_link==null) SelfAttacher.class.wait();    }    System.out.println("Now I have hands on instrumentation: "+BACK_link);    System.out.println(BACK_link.isModifiableClass(SelfAttacher.class));    vm.detach();  } // create a JAR file for the agent; since our class is already in class path // our jar consisting of a MANIFEST declaring our class as agent only  private static File createJar() throws IOException {    File f=File.createTempFile("agent", ".jar");    f.deleteonExit();    Charset cs=StandardCharsets.ISO_8859_1;    try(FileOutputStream fos=new FileOutputStream(f);        ZipOutputStream os=new ZipOutputStream(fos)) {      os.putNextEntry(new ZipEntry("meta-INF/MANIFEST.MF"));      ByteBuffer bb = cs.enpre("Agent-Class: "+SelfAttacher.class.getName());      os.write(bb.array(), bb.arrayOffset()+bb.position(), bb.remaining());      os.write(10);      os.closeEntry();    }    return f;  } // invoked when the agent is loaded into the JVM, pass inst back to the caller  public static void agentmain(String agentArgs, Instrumentation inst) {    synchronized(SelfAttacher.class) {      BACK_link=inst;      SelfAttacher.class.notifyAll();    }  }}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/569193.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号