gpt4 book ai didi

java.lang.reflect.代理 : Huge exception stack trace

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:39:41 24 4
gpt4 key购买 nike

这是一个简单的 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

因此,每个堆栈帧的行号都没有增长。

最佳答案

我对这个堆栈跟踪的解释是:

  • 当堆栈溢出最终发生时,抛出了一个StackOverflowError
  • 此错误被 method.invoke() 捕获并转换为 InvocationTargetException(根据 it's javadoc ),以原始异常为原因
  • 由于这个异常是一个已检查的异常,Proxy 不能让它自己掉下来,所以它捕获了它并将它翻译成一个 UndeclaredThrowableException,再次将前一个作为原因

这样,对于堆栈溢出之前的每个递归级别,您都会得到另外两个“由”异常及其堆栈跟踪 - 大量输出。至于输出多少,大家猜猜看:

  • 根据您的帖子大约有 2000 次调用
  • 在每次调用中,堆栈跟踪似乎都增长了 5 行
  • 为每次调用打印 2 个错误和堆栈跟踪

所以最大的堆栈跟踪将有大约 10000 行,平均堆栈跟踪将有大约 5000 行,所有内容都打印两次(每种异常类型一次),总计约为 2000 * 5000 * 2 = 2000 万行

关于java.lang.reflect.代理 : Huge exception stack trace,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35511849/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com