開發問題總結(一)

下面是在利用JDK的Instrument來編寫調試工具的時候出現的一些問題總結

 

1、java.io.Console 類的讀取操作將會阻塞掉寫入操作,造成寫入操作不能異步進行。

原因是該類中加入了讀寫鎖。代碼如下:

    public String readLine(String fmt, Object ... args) {
        String line = null;
        synchronized (writeLock) {  
            synchronized(readLock) {
                if (fmt.length() != 0)
                    pw.format(fmt, args);
                try {
                    char[] ca = readline(false);
                    if (ca != null)
                        line = new String(ca);
                } catch (IOException x) {
                    throw new IOError(x);
                }
            }
        }
        return line;
    }

 

2、Class.getSimpleName 方法在scala 下有可能拋出異常

例如:

Exception in thread "agent thread" java.lang.InternalError: Malformed class name
    at java.lang.Class.getSimpleName(Class.java:1133)
    at cn.zhxing.trace.agent.instrument.ClassFilter.match(ClassFilter.java:41)
    at cn.zhxing.trace.util.InstrumentUtil.findMatchClassAndMethods(InstrumentUtil.java:111)
    at cn.zhxing.trace.agent.command.LoaderCommand.run(LoaderCommand.java:40)
    at cn.zhxing.trace.agent.Client.listen(Client.java:43)
    at cn.zhxing.trace.agent.Main$1.run(Main.java:62)
    at java.lang.Thread.run(Thread.java:662)

 經過查詢發現該類爲:scala.collection.SeqLike$$anonfun$occCounts$1,這類有個特徵是連續有兩個$符號,正是這個情況導致。網上也有類似的錯誤:http://www.scala-lang.org/node/7691

 

3、asm.jar使用中有可能出現ClassNotFoundException 的異常(在使用instrument的時候容易出現)

例如下面的方法,調用accept的時候拋出

 

ClassReader reader = new ClassReader(classfileBuffer);
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
reader.accept(vistor, ClassReader.SKIP_FRAMES);//error

 異常堆棧部分如下:

TraceTransformer:reader.acceptjava.lang.RuntimeException: java.lang.ClassNotFoundException: exceptions/ServiceException
    at org.objectweb.asm.ClassWriter.getCommonSuperClass(Unknown Source)
    at org.objectweb.asm.ClassWriter.a(Unknown Source)
    at org.objectweb.asm.Frame.a(Unknown Source)
    at org.objectweb.asm.Frame.a(Unknown Source)
    at org.objectweb.asm.MethodWriter.visitMaxs(Unknown Source)
    at org.objectweb.asm.commons.LocalVariablesSorter.visitMaxs(Unknown Source)
    at cn.zhxing.trace.agent.instrument.MethodInstrument.visitMaxs(MethodInstrument.java:137)
    at org.objectweb.asm.ClassReader.accept(Unknown Source)
    at org.objectweb.asm.ClassReader.accept(Unknown Source)

 

仔細分析了asm的代碼發現,代碼如下:

    protected String getCommonSuperClass(final String type1, final String type2)
    {
        Class<?> c, d;
        ClassLoader classLoader = getClass().getClassLoader();
        try {
//這裏可以看出classloader是直接在getClass().getClassLoader();中獲取的本地classloader,如果該class不在該classloader中時就會出現異常
            c = Class.forName(type1.replace('/', '.'), false, classLoader);
            d = Class.forName(type2.replace('/', '.'), false, classLoader);
        } catch (Exception e) {
            throw new RuntimeException(e.toString());
        }
        if (c.isAssignableFrom(d)) {
            return type1;
        }
        if (d.isAssignableFrom(c)) {
            return type2;
        }
        if (c.isInterface() || d.isInterface()) {
            return "java/lang/Object";
        } else {
            do {
                c = c.getSuperclass();
            } while (!c.isAssignableFrom(d));
            return c.getName().replace('.', '/');
        }
    }

 

修改如下:

新建一個新的Class 繼承ClassWriter,重寫getCommonSuperClass 方法,如下:

public class TraceClassWriter extends ClassWriter {
 //省略其他代碼

    public TraceClassWriter(ClassReader classReader, int flags, ClassLoader loader) {
        super(classReader, flags);
        this.loader = loader;
    }

    protected String getCommonSuperClass(final String type1, final String type2) {
        Class c, d;
        try {
            c = Class.forName(type1.replace('/', '.'), true, loader);
            d = Class.forName(type2.replace('/', '.'), true, loader);
        } catch (Exception e) {
            logger.error(e, "type1=%s,type2=%s,loader=%s", type1, type2, loader);
            throw new RuntimeException(e.toString());
        }
        if (c.isAssignableFrom(d)) {
            return type1;
        }
        if (d.isAssignableFrom(c)) {
            return type2;
        }
        if (c.isInterface() || d.isInterface()) {
            return "java/lang/Object";
        } else {
            do {
                c = c.getSuperclass();
            } while (!c.isAssignableFrom(d));
            return c.getName().replace('.', '/');
        }
    }
}

 使用的時候是這樣:

ClassReader reader = new ClassReader(classfileBuffer);
//由外部傳入classloader
ClassWriter writer = new TraceClassWriter(reader, ClassWriter.COMPUTE_FRAMES,loader);
reader.accept(vistor, ClassReader.SKIP_FRAMES);

 

類似錯誤也可看:http://www.avaje.org/topic-180.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章