我制作了一个Java代理程序,它在运行时附加到JVM上,并检测所有已加载的项目类并插入一些日志语句。总共有11k个类。我测量了我的的方法所需的总时间,大约为3秒。但整个检测流程的持续时间约为30秒。 这是如何重新转换我的类:
instrumentation.retransformClasses(myClassesArray);
我假设大部分时间都被JVM用来重新加载更改的类,这是正确的吗?我该如何加快仪器化过程?
更新: 当我的代理程序被附加时,
instrumentation.addTransformer(new MyTransfomer(), true);
instrumentation.retransformClasses(retransformClassArray);
这被称为只会被一次调用。
然后MyTransfomer
类对类进行仪器化,并测量仪器化的总持续时间:
public class MyTransfomer implements ClassFileTransformer {
private long total = 0;
private long min = ..., max = ...;
public final byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classFileBuffer) {
long s = System.currentTimeMillis();
if(s < min) min = s;
if(s > max) max = s;
byte[] transformed = this.transformInner(loader, className, classFileBuffer);
this.total += System.currentTimeMillis() - s;
return transformed;
}
}
当所有类都被从初始数组中插装后(全局缓存跟踪插装的类),会打印出total
,大约需要3秒钟。但max-min
需要大约30秒钟。
更新2:
查看堆栈跟踪后发现以下情况:我调用
instrumentation.retransformClasses(retransformClassArray);
这段代码调用本地方法retransformClasses0()
,稍等片刻后,JVM会调用sun.instrument.InstrumentationImpl
类的transform()
方法(但该方法一次只能处理一个类,因此JVM需要连续多次调用该方法),该方法又会调用sun.instrument.TransformerManager
对象的transform()
方法,该对象具有已注册的所有ClassTransformers
列表,并依次调用每个转换器来转换类(我只注册了一个转换器!)。
因此,在JVM中花费的大部分时间(在调用retransformClasses0()
之后,在每次调用sun.instrument.InstrumentationImpl.transform()
之前)是最耗时的。是否有办法减少JVM执行此任务所需的时间?