开发问题总结(一)

下面是在利用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

你可能感兴趣的:(java基础)