先打一个告白。
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
简朴总结下:
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的代码