通过检测字节码调用 class 的静态方法时出现 NoClassDefFoundError
NoClassDefFoundError when invoking static method of a class by instrumenting byte code
我试图在分配对象但 运行 出现问题时调用静态方法。我已将我的代码缩减为更小的工作示例。
MemorySnifferAgent.java
public class MemorySnifferAgent {
public static void premain(String agentArguments, Instrumentation instrumentation) {
Callback.main(); //Make sure the class is loaded ?
instrumentation.addTransformer(new MemoryAllocationTransformer());
}
}
MemoryAllocationTransformer.java
public class MemoryAllocationTransformer implements ClassFileTransformer {
public byte[] instrumentByteCode(byte[] bytecode) {
ClassReader reader = new ClassReader(bytecode);
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
CustomClassReader customVisitor = new CustomClassReader(Opcodes.ASM4, writer);
reader.accept(customVisitor, 0);
return writer.toByteArray();
}
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] bytes = instrumentByteCode(classfileBuffer);
return bytes;
}
}
CustomClassReader.java
public class CustomClassReader extends ClassVisitor {
public CustomClassReader(int api, ClassWriter classWriter) {
super(api, classWriter);
}
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
MethodVisitor methodWritter = super.visitMethod(access, name, desc, signature, exceptions);
return new CustomMethodWritter(api, name, methodWritter);
}
}
CustomMethodWritter.java
class CustomMethodWritter extends MethodVisitor {
String name;
public CustomMethodWritter(int i, String name, MethodVisitor methodWriter) {
super(i, methodWriter);
this.name = name;
}
@Override
public void visitTypeInsn(int opcode, String type) {
super.visitTypeInsn(opcode, type);
if (opcode == Opcodes.NEW) {
super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/chasingnanos/Callback", "onAllocation", "()V", false);
}
}
}
注:
- 我意识到利用所有分配需要利用所有新*操作码和反射 API
- super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/chasingnanos/Callback", "onAllocation", "()V", false);似乎是问题所在。虽然我找不到问题所在。
如果这是一个非常基本的问题,我们深表歉意。我是字节码操作的新手。
我得到的错误:
java.lang.NoClassDefFoundError
- 类:'java/lang/NoClassDefFoundError'
我认为这个问题与不同 ClassLoader
提供的隔离有关。特别是代理代码和 "main" 代码由不同的 Classloader
加载,因此您不能从 "main" 代码调用您的 Callback
。解决此问题的方法是将 Callback
class 移动到另一个 jar 并使用 -Xbootclasspath/a
选项将其添加到 bootstrap class 路径。
另请参阅类似问题
Java NoClassDefFoundError when calling own class from instrumented method
更新(过滤)
如果您想过滤掉无法访问您的 Callback
的 class 而不是使用 bootstrap,您可能应该这样做,而不是仅仅检查 null
:
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
// check if the class inside the loader can access Callback
// by analyzing its hierarchy of parent class loaders
for (ClassLoader curLoader = loader; ; curLoader = curLoader.getParent()) {
if (curLoader == null) {
System.out.println("Skip '" + className + "' for " + loader);
return null;
} else if (curLoader == Callback.class.getClassLoader())
break;
}
byte[] bytes = instrumentByteCode(classfileBuffer);
return bytes;
}
我试图在分配对象但 运行 出现问题时调用静态方法。我已将我的代码缩减为更小的工作示例。
MemorySnifferAgent.java
public class MemorySnifferAgent {
public static void premain(String agentArguments, Instrumentation instrumentation) {
Callback.main(); //Make sure the class is loaded ?
instrumentation.addTransformer(new MemoryAllocationTransformer());
}
}
MemoryAllocationTransformer.java
public class MemoryAllocationTransformer implements ClassFileTransformer {
public byte[] instrumentByteCode(byte[] bytecode) {
ClassReader reader = new ClassReader(bytecode);
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
CustomClassReader customVisitor = new CustomClassReader(Opcodes.ASM4, writer);
reader.accept(customVisitor, 0);
return writer.toByteArray();
}
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] bytes = instrumentByteCode(classfileBuffer);
return bytes;
}
}
CustomClassReader.java
public class CustomClassReader extends ClassVisitor {
public CustomClassReader(int api, ClassWriter classWriter) {
super(api, classWriter);
}
public MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {
MethodVisitor methodWritter = super.visitMethod(access, name, desc, signature, exceptions);
return new CustomMethodWritter(api, name, methodWritter);
}
}
CustomMethodWritter.java
class CustomMethodWritter extends MethodVisitor {
String name;
public CustomMethodWritter(int i, String name, MethodVisitor methodWriter) {
super(i, methodWriter);
this.name = name;
}
@Override
public void visitTypeInsn(int opcode, String type) {
super.visitTypeInsn(opcode, type);
if (opcode == Opcodes.NEW) {
super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/chasingnanos/Callback", "onAllocation", "()V", false);
}
}
}
注:
- 我意识到利用所有分配需要利用所有新*操作码和反射 API
- super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/chasingnanos/Callback", "onAllocation", "()V", false);似乎是问题所在。虽然我找不到问题所在。
如果这是一个非常基本的问题,我们深表歉意。我是字节码操作的新手。
我得到的错误:
java.lang.NoClassDefFoundError - 类:'java/lang/NoClassDefFoundError'
我认为这个问题与不同 ClassLoader
提供的隔离有关。特别是代理代码和 "main" 代码由不同的 Classloader
加载,因此您不能从 "main" 代码调用您的 Callback
。解决此问题的方法是将 Callback
class 移动到另一个 jar 并使用 -Xbootclasspath/a
选项将其添加到 bootstrap class 路径。
另请参阅类似问题 Java NoClassDefFoundError when calling own class from instrumented method
更新(过滤)
如果您想过滤掉无法访问您的 Callback
的 class 而不是使用 bootstrap,您可能应该这样做,而不是仅仅检查 null
:
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
// check if the class inside the loader can access Callback
// by analyzing its hierarchy of parent class loaders
for (ClassLoader curLoader = loader; ; curLoader = curLoader.getParent()) {
if (curLoader == null) {
System.out.println("Skip '" + className + "' for " + loader);
return null;
} else if (curLoader == Callback.class.getClassLoader())
break;
}
byte[] bytes = instrumentByteCode(classfileBuffer);
return bytes;
}