JVM何时开始省略堆栈跟踪信息?

16

我在这个主题上做了很多搜索,但没有具体的解决方案或指南。

根据文档

某些虚拟机在某些情况下会从堆栈跟踪中省略一个或多个堆栈帧。在极端情况下,一个没有关于此throwable的堆栈跟踪信息的虚拟机允许从此方法返回长度为零的数组。

有人可以说明可能发生这种情况的条件吗?

我知道如果多次产生相同的堆栈跟踪,它会发生(在数量上没有确定的硬性规定,据我所知-请指正)。但这不应该有一个定义好的行为吗?

而且,根据这个,传递-XX:-OmitStackTraceInFastThrow将确保我的StackTrace不会丢失。在生产环境中总是这样做不是明智之举吗?


1
性能。如果一直生成相同的堆栈跟踪,那么性能将会受到影响。 - Michael
关于极端情况。我认为如果JVM生成了内存不足错误,例如,它甚至可能没有足够的内存来构建堆栈跟踪,因此必须为空。 - Michael
如果内存消耗如此关键,最好抛出OOM而不是“默默地”抑制堆栈跟踪,对吧? - Mohamed Anees A
2
“总是在生产机器上这样做不是很明智吗?” 不是的。我们的生产机器已启用此功能。我们更关心性能提升,而不是每次都获取完整的堆栈跟踪。如果被省略了,那就意味着它已经生成了。要获取完整的堆栈跟踪,只需转到日志中的第一个出现位置即可。 - Michael
我不知道你的意思。OOM异常总是被抛出。只是抛出的OOM是否包含堆栈跟踪。如果没有足够的内存生成它,那么只能在没有堆栈跟踪的情况下抛出它。 - Michael
经典案例通常是这样的 for (int i = 0; i < 100_000; i++) {try {throwsNPE();} catch (NullPointerException e) {}}; throwsNPE();。如果你遇到了类似的情况,那么你应该已经有足够多的堆栈跟踪了。 - Johannes Kuhn
1个回答

27
  1. OmitStackTraceInFastThrow 是在 C2 编译的 代码中的优化,可以抛出某些隐式异常而不包含堆栈跟踪。
  2. 此优化仅适用于 隐式 异常,即 JVM 自身抛出的异常,而非用户代码。这些隐式异常包括:
    • NullPointerException(空指针异常)
    • ArithmeticException(算术异常)
    • ArrayIndexOutOfBoundsException(数组越界异常)
    • ArrayStoreException(数组存储异常)
    • ClassCastException(类转换异常)
  3. 只有当 JVM 知道异常已经在特定位置发生过时,才会省略堆栈跟踪信息。

因此,在 HotSpot JVM 中,只有满足以下所有条件时,异常才不会有堆栈跟踪:1)抛出方法是热点方法,即由 C2 编译;2)它是隐式异常,例如通过obj.method() 抛出的 NPE,而不是通过 throw new NullPointerException() 抛出的;3)该异常至少被抛出了两次。

优化的目的是消除那些(可以说是罕见的)情况下在快速路径上重复抛出隐式异常的性能影响。在我看来,这并不是一个正常的情况。异常,尤其是隐式异常,通常表示需要修复的错误条件。从这个意义上讲,禁用-XX:-OmitStackTraceInFastThrow是可以的。例如,在我们的生产环境中,我们总是禁用它,并且它为我们节省了大量的调试时间。然而,我承认,如果存在这样的优化,就有一些情况可以帮助解决性能问题。
简而言之,添加-XX:-OmitStackTraceInFastThrow选项确实是一个好主意,除非您的应用程序中有许多热点路径上的隐式异常。

4
除了快速抛出功能,还有其他情况,例如内存不足,可能会导致没有堆栈跟踪的可抛出异常。 - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接