2017-03-07 52 views
0

我試圖調用一個靜態方法,每當一個對象被分配,但遇到問題。我已經將我的代碼縮減爲小型工作示例。NoClassDefFoundError通過檢測字節代碼調用類的靜態方法

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 /回撥」, 「onAllocation」 「()V」,false);似乎是問題所在。雖然我無法找到問題所在。

道歉,如果這是一個非常基本的問題。我是字節碼操作的新手。

錯誤,我得到:

java.lang.NoClassDefFoundError - 克拉斯: '爪哇/郎/ NoClassDefFoundError的'

回答

2

我認爲這個問題是關係到不同ClassLoader S公司的隔離。特別是代理商的代碼和「主要」代碼由不同的Classloader加載,因此您不能從「主要」代碼呼叫Callback。解決這個問題的方法是將Callback類移動到另一個jar中,並使用-Xbootclasspath/a選項將其添加到引導類路徑中。

也見類似的問題在 Java NoClassDefFoundError when calling own class from instrumented method

更新(過濾)

如果不是使用引導程序要過濾掉無法訪問您的Callback類,你應該這樣做這而不是隻是檢查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; 
} 
+0

你是對的!看起來premain是由 [email protected]調用的,它看起來不錯,但是通過引導加載程序調用變換。我添加了下面的代碼片段以進行轉換並且工作正常。 (loader == null){ System.out.println 返回null; } else System.out.println(「Transformed by」+ loader.toString()+「loader」); 在AppClassLoader之後爲什麼要調用bootstap加載器的任何想法? – KodeWarrior

+0

我不確定我是否收到您的問題。是否回答「因爲所有課程都是懶惰地(按需求)加載」滿足你?我也想提醒你,在一個複雜的應用程序,做自己的'基於ClassLoader'魔法(如網絡/應用程序容器)只檢查'裝載機== null'可能是不夠的 – SergGr

+0

我知道,類懶惰地加載,但我不確定引導程序被調用。我認爲它在第一次調用AppClassLoader之前只調用一次。 – KodeWarrior