先打一个告白。
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的代码