【发布时间】:2020-10-10 11:34:42
【问题描述】:
我想做的是监控对象的创建并为该对象记录一个唯一的 ID。所以我使用 ASM 来监控“NEW”指令。在我的方法访问者适配器中:
public void visitTypeInsn(int opcode, String desc){
mv.visitTypeInsn(opcode, desc);
if (opcode == NEW){
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESTATIC, "org/.../.../MyRecorder", "object_new",
"(Ljava/lang/Object;)V", false);
}
}
在MyRecorder.java:
public static void object_new(Object ref){
log("object_new !");
log("MyRecorder: " + ref);
log("ref.getClass().getName(): " + ref.getClass().getName());
}
但是,这些代码导致java.lang.VerifyError: (...) Expecting to find object/array on stack。我不知道为什么这行不通。如果这种方式不对,如何监控对象的创建?
其实我也试过监控object.<init>,而不是监控NEW指令。但是,使用以下代码,它会抛出 java.lang.VerifyError: (...) Unable to pop operand off an empty stack:
public void visitMethodInsn(int opc, String owner, String name, String desc, boolean isInterface) {
...
mv.visitMethodInsn(opc, owner, name, desc, isInterface);
if (opc == INVOKESPECIAL && name.equals("<init>")) {
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESTATIC, "org/myekstazi/agent/PurityRecorder", "object_new",
"(Ljava/lang/Object;)V", false);
}
}
【问题讨论】:
-
生成的字节码是什么样的?您可以在已编译的类上使用
javap来解决这个问题。我猜你可能在NEW操作码之后干预得太早了,需要等到INVOKESPECIAL被调用。 -
@nrainer 我不知道生成的字节码是什么样的,因为我在加载类时使用 java 代理来转换类,所以我没有将它们转储到磁盘。我认为您的意思是在对象初始化后插入这些指令?我也尝试过,但遗憾的是它也无法顺利运行。
-
问题是你不能用一个未初始化的对象做很多事情。生成的字节码看起来像
new C, dup, invokestatic ...PurityRecorder.object_new, dup, invokespecial C.<init> -
@JohannesKuhn 感谢您的评论!你能解释更多关于“不能对未初始化的对象做很多事情”吗?我认为字节码与您描述的完全一样。但我不明白为什么它会抛出 VerifyError,因为它看起来是有效的。
标签: java jvm instrumentation java-bytecode-asm javaagents