这是一个简单的Java应用程序:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Main {
interface MyInterface {
void myMethod();
}
public static void main(String[] args) {
MyInterface myInterface = (MyInterface) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[] {MyInterface.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(proxy, args);
}
});
myInterface.myMethod();
}
}
我希望StackOverflowError
这里变得简单,因为我递归地在代理实例上调用了相同的方法。
但是,异常产生的堆栈跟踪包含数百万行和数百MB的大小。
堆栈跟踪的第一部分开始如下:
java.lang.reflect.UndeclaredThrowableException
at $Proxy0.myMethod(Unknown Source)
at Main.main(Main.java:22)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at Main$1.invoke(Main.java:18)
... 2 more
Caused by: java.lang.reflect.UndeclaredThrowableException
at $Proxy0.myMethod(Unknown Source)
... 7 more
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at Main$1.invoke(Main.java:18)
... 8 more
并扩展到:
Caused by: java.lang.reflect.UndeclaredThrowableException
at $Proxy0.myMethod(Unknown Source)
... 1022 more
然后遵循数百万行,例如:
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at Main$1.invoke(Main.java:18)
at $Proxy0.myMethod(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at Main$1.invoke(Main.java:18)
.......
和:
Caused by: java.lang.reflect.UndeclaredThrowableException
at $Proxy0.myMethod(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at Main$1.invoke(Main.java:18)
at $Proxy0.myMethod(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at Main$1.invoke(Main.java:18)
.......
最后打印的行是(在上述重复序列之后):
Exception: java.lang.StackOverflowError thrown from the UncaughtExceptionHandler in thread "main"
我怀疑堆栈跟踪生成机制正在代理实例上调用某些方法(可能toString
是打印它,或进行其他操作),从而一遍又一遍地重复递归(因为代理上的每个方法调用都会导致无限递归),但是代理方法执行的总数为1919(通过增加InvocationHandler.invoke
方法中的计数器来衡量)。
在我的实际用例中,我当然解决了无限递归问题。我只是好奇是否有人知道这是错误还是有合理的解释?
Java版本:
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)
编辑
@JiriTousek和@AndrewWilliamson友好地分析了可能的原因。我根据他们的输入进行了模拟:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
public class Test {
private static int counter = 0;
public static void main(String[] args) {
proxy();
}
private static void proxy() {
try {
methodInvoke();
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
private static void methodInvoke() throws InvocationTargetException {
try {
myMethod();
} catch (Throwable e) {
throw new InvocationTargetException(e);
}
}
private static void myMethod() {
if (counter++ == 5) {
throw new StackOverflowError();
}
proxy();
}
}
这将导致以下堆栈跟踪:
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at Test.proxy(Test.java:16)
at Test.main(Test.java:9)
Caused by: java.lang.reflect.InvocationTargetException
at Test.methodInvoke(Test.java:24)
at Test.proxy(Test.java:14)
... 1 more
Caused by: java.lang.reflect.UndeclaredThrowableException
at Test.proxy(Test.java:16)
at Test.myMethod(Test.java:32)
at Test.methodInvoke(Test.java:22)
... 2 more
Caused by: java.lang.reflect.InvocationTargetException
at Test.methodInvoke(Test.java:24)
at Test.proxy(Test.java:14)
... 4 more
Caused by: java.lang.reflect.UndeclaredThrowableException
at Test.proxy(Test.java:16)
at Test.myMethod(Test.java:32)
at Test.methodInvoke(Test.java:22)
... 5 more
Caused by: java.lang.reflect.InvocationTargetException
at Test.methodInvoke(Test.java:24)
at Test.proxy(Test.java:14)
... 7 more
Caused by: java.lang.reflect.UndeclaredThrowableException
at Test.proxy(Test.java:16)
at Test.myMethod(Test.java:32)
at Test.methodInvoke(Test.java:22)
... 8 more
Caused by: java.lang.reflect.InvocationTargetException
at Test.methodInvoke(Test.java:24)
at Test.proxy(Test.java:14)
... 10 more
Caused by: java.lang.reflect.UndeclaredThrowableException
at Test.proxy(Test.java:16)
at Test.myMethod(Test.java:32)
at Test.methodInvoke(Test.java:22)
... 11 more
Caused by: java.lang.reflect.InvocationTargetException
at Test.methodInvoke(Test.java:24)
at Test.proxy(Test.java:14)
... 13 more
Caused by: java.lang.reflect.UndeclaredThrowableException
at Test.proxy(Test.java:16)
at Test.myMethod(Test.java:32)
at Test.methodInvoke(Test.java:22)
... 14 more
Caused by: java.lang.reflect.InvocationTargetException
at Test.methodInvoke(Test.java:24)
at Test.proxy(Test.java:14)
... 16 more
Caused by: java.lang.StackOverflowError
at Test.myMethod(Test.java:30)
at Test.methodInvoke(Test.java:22)
... 17 more
因此,每个堆栈帧没有行号增长。
当相同迹线的深度大于1024(默认值)时,打印的堆栈迹线不会被截断。这就是为什么最后一个截断的跟踪以结尾... 1022 more
而所有后续的跟踪都被完整打印的原因。
可以通过将MaxJavaStackTraceDepth
JVM参数设置为所需值来更改默认值。当我使用Proxy
来运行时,为原始示例增加它时-XX:MaxJavaStackTraceDepth=8192
,整个打印的堆栈跟踪下降到大约12500行,结尾为:
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at Main1$1.invoke(Main1.java:16)
... 7003 more
Caused by: java.lang.reflect.UndeclaredThrowableException
at $Proxy0.myMethod(Unknown Source)
... 7007 more
Exception: java.lang.StackOverflowError thrown from the UncaughtExceptionHandler in thread "main"
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句