欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识

具体阐明稀有的ClassCircu 图纸加密 larityError异常导致的StackOverflowError

点击: 次  来源:宝鼎软件 时间:2017-06-01

原文出处: hengyunabc的专栏

先打一个告白。

greys是一个很不错的Java诊断东西:https://github.com/oldmanpushcart/greys-anatomy

最近实验用greys来及时统计jvm里的异常生成数量,在加强Throwable时,发明应用会抛出StackOverflowError。下面记录具体的阐明进程。

在真正阐明之前,先先容JVM对反射要领挪用的优化greys的事情道理

JVM对反射要领挪用的优化

在JVM里对付反射要领挪用Method.invoke,默认环境下,是通过NativeMethodAccessorImpl来挪用到的。

挪用栈如下:

NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]  
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62  
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43  
Method.invoke(Object, Object...) line: 497

当颠末16次要领挪用之后,NativeMethodAccessorImpl 会用MethodAccessorGenerator 动态生成一个MethodAccessorImpl(即下面的GeneratedMethodAccessor1) ,然后再配置到 DelegatingMethodAccessorImpl 里。然后挪用栈就酿成这个样子:

GeneratedMethodAccessor1.invoke(Object, Object[]) line: not available   
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43  
Method.invoke(Object, Object...) line: 497

这个动态生成的GeneratedMethodAccessor1是如何加载到ClassLoader里的?实际上是通过 Unsafe.defineClass 来define,然后再挪用 ClassLoader.loadClass(String) 来加载到的。

AgentLauncher$1(ClassLoader).loadClass(String) line: 357   
Unsafe.defineClass(String, byte[], int, int, ClassLoader, ProtectionDomain) line: not available [native method] 
ClassDefiner.defineClass(String, byte[], int, int, ClassLoader) line: 63

更多反射挪用优化的细节参考:http://rednaxelafx.iteye.com/blog/548536

简朴总结下:

  • jvm会对method反射挪用优化
  • 运行时动态生成反射挪用代码,再define到classloader里
  • define到classloader时,会挪用ClassLoader.loadClass(String)
  • greys的事情道理

    利用greys可以在运行时,对要领挪用举办一些watch, monitor等的行动。那么这个是怎么实现的呢?

    简朴来说,劳务派遣管理系统,是通过运行时修改字节码来实现的。好比下面这个函数:

    class xxx {
        public String abc(Student s) {
            return s.getName();
        }
    }

    被greys修改事后,变为

    Spy.ON_BEFORE_METHOD.invoke(null, new Integer(0), xxx2.getClass().getClassLoader(), "xxx", "abc", "(LStudent;)Ljava/lang/String;", xxx2, {student});
    try {
        void s;
        String string = s.getName();
        Spy.ON_RETURN_METHOD.invoke(null, string);
        return string;
    }
    catch (Throwable v1) {
        Spy.ON_THROWS_METHOD.invoke(null, v1);
        throw v1;
    }

    可以看到,greys在本来的method里插入许多钩子,所以greys可以获取到method被挪用的参数,返回值等信息。

    当利用greys对java.lang.Throwable来加强时,会抛出StackOverflowError

    测试代码:

    public class ExceptionTest {
        public static void main(String[] args) throws Exception {
            for (int i = 0; i < 100000; i++) {
                RuntimeException exception = new RuntimeException("");
                System.err.println(exception);
    
                Thread.sleep(1000);
            }
        }
    }

    在呼吁行里attach到测试代码历程之后,在greys console里执行

    options unsafe true
    monitor -c 1 java.lang.Throwable *

    当用greys加强java.lang.Throwable之后,颠末16秒之后,图纸加密,就会抛出StackOverflowError。

    详细的异常栈很长,这里只贴出重点部门:

    Thread [main] (Suspended (exception StackOverflowError))    
        ClassLoader.checkCreateClassLoader() line: 272  
    ...
        ClassCircularityError(Throwable).<init>(String) line: 264   
        ClassCircularityError(Error).<init>(String) line: 70    
        ClassCircularityError(LinkageError).<init>(String) line: 55 
        ClassCircularityError.<init>(String) line: 53   
        Unsafe.defineClass(String, byte[], int, int, ClassLoader, ProtectionDomain) line: not available [native method] 
        ClassDefiner.defineClass(String, byte[], int, int, ClassLoader) line: 63    
        MethodAccessorGenerator$1.run() line: 399  
        MethodAccessorGenerator$1.run() line: 394  
        AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method]  
        MethodAccessorGenerator.generate(Class<?>, String, Class<?>[], Class<?>, Class<?>[], int, boolean, boolean, Class<?>) line: 393 
        MethodAccessorGenerator.generateMethod(Class<?>, String, Class<?>[], Class<?>, Class<?>[], int) line: 75    
        NativeMethodAccessorImpl.invoke(Object, Object[]) line: 53  
        DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43  
        Method.invoke(Object, Object...) line: 497  
        ClassCircularityError(Throwable).<init>(String) line: 264   
        ClassCircularityError(Error).<init>(String) line: 70    
        ClassCircularityError(LinkageError).<init>(String) line: 55 
        ClassCircularityError.<init>(String) line: 53   
        Unsafe.defineClass(String, byte[], int, int, ClassLoader, ProtectionDomain) line: not available [native method] 
        ClassDefiner.defineClass(String, byte[], int, int, ClassLoader) line: 63    
        MethodAccessorGenerator$1.run() line: 399  
        MethodAccessorGenerator$1.run() line: 394  
        AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method]  
        MethodAccessorGenerator.generate(Class<?>, String, Class<?>[], Class<?>, Class<?>[], int, boolean, boolean, Class<?>) line: 393 
        MethodAccessorGenerator.generateMethod(Class<?>, String, Class<?>[], Class<?>, Class<?>[], int) line: 75    
        NativeMethodAccessorImpl.invoke(Object, Object[]) line: 53  
        DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43  
        Method.invoke(Object, Object...) line: 497  
        ClassNotFoundException(Throwable).<init>(String, Throwable) line: 286   
        ClassNotFoundException(Exception).<init>(String, Throwable) line: 84    
        ClassNotFoundException(ReflectiveOperationException).<init>(String, Throwable) line: 75 
        ClassNotFoundException.<init>(String) line: 82  
        AgentLauncher$1(URLClassLoader).findClass(String) line: 381    
        AgentLauncher$1.loadClass(String, boolean) line: 55    
        AgentLauncher$1(ClassLoader).loadClass(String) line: 357   
        Unsafe.defineClass(String, byte[], int, int, ClassLoader, ProtectionDomain) line: not available [native method] 
        ClassDefiner.defineClass(String, byte[], int, int, ClassLoader) line: 63    
        MethodAccessorGenerator$1.run() line: 399  
        MethodAccessorGenerator$1.run() line: 394  
        AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method]  
        MethodAccessorGenerator.generate(Class<?>, String, Class<?>[], Class<?>, Class<?>[], int, boolean, boolean, Class<?>) line: 393 
        MethodAccessorGenerator.generateMethod(Class<?>, String, Class<?>[], Class<?>, Class<?>[], int) line: 75    
        NativeMethodAccessorImpl.invoke(Object, Object[]) line: 53  
        DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43  
        Method.invoke(Object, Object...) line: 497  
        RuntimeException(Throwable).<init>(String) line: 264    
        RuntimeException(Exception).<init>(String) line: 66 
        RuntimeException.<init>(String) line: 62    
        ExceptionTest.main(String[]) line: 15

    从异常栈可以看出,先呈现了一个ClassNotFoundException,然后大量的ClassCircularityError,最终导致StackOverflowError。

    下面详细阐明原因。

    被加强事后的Throwable的代码