如何提前了解OutOfMemory或StackOverflow错误

在Java中,有没有办法知道StackOverflow错误或OutOfMemoryexception可能很快发生?

如果一个能够以编程方式获取内存使用情况统计信息,并且如果提前知道在OutOfMemoryexception之前需要使用多少内存,则OutOfMemoryexception可能更容易捕获。 但这些价值是否可知?

对于StackOverflow错误,有没有办法获得递归深度,以及如何知道递归深度的哪个值会导致错误发生?

通过提前知道这些错误是否会发生,我觉得我可以更优雅地恢复应用程序,而不是看着它崩溃。

预计内存不足错误

我很惊讶我在其他post中没有看到这个,但你可以使用Java 5/6中的ManagementFactory来获取大量的内存使用信息。

查看平台mbean服务器页面 ,了解有关检测Java中的低内存条件的更多信息。 我相信你可以设置通知程序,以便在内存使用率达到某个阈值时调用代码。

您可以使用Runtime.freeMemory()Runtime.maxMemory()预测内存不足的情况。 大多数时候,优雅地恢复很难,但我把它留给你。

大多数StackOverflow错误来自错误的递归。 不幸的是,确定递归是否会停止的问题通常是不可判定的(这是CS中的一个核心概念)。 但是,有些情况下,您可能会收到警告,例如,如果您在没有参数的情况下递归调用函数,某些IDE会通知您。

如果您的应用程序设计和正确实现,您永远不应该看到StackOverflowexception!

通常,如果您收到StackOverflowexception,则表示您的递归代码中存在错误。

通常内存使用很难预测。

无限回归的堆栈溢出通常出现在写入/调试周期中。

更难捕获的是诸如大型藏品,缓存等等的内存问题。 正如已经指出的那样,你可以检查运行时空闲和最大内存,但要小心,因为它们的含义不明显 – 这里的“免费”意味着“现在免费”,例如意味着如果你运行一个完整的gc,你可能会得到更多。 不幸的是,如果没有运行System.gc(),就没有办法获得“总可能免费,包括垃圾收集”,这对于生产应用程序来说不是一件好事(你可能有足够大的数据集到首先导致问题)因为整个JVM将暂停几秒钟(或更多,在大型应用程序中)。 请注意,即使System.gc()也不能保证“现在”运行,但我的经验是,无论什么时候我都玩过它。

您可以通过使用-verbose:gc,-XX:+ PrintGCTimeStamps和-XX:+ PrintGCDetails( 此处更详细)启动java来从正在运行的jvm中打印gc活动,并且通常如果收集器开始更频繁地运行,它可能是你内存不足的迹象。

您可以做的一件有用的事情是使用SoftReference进行缓存。 当你的内存不足时,这会给你一个逐渐的性能下滑。 只是不要在WeakHashMap使用WeakReference ,因为当你的应用程序在我身上时,它会让我烦恼。

如果内存达到某个阈值, MemoryMXBean可以发出通知。

我不知道在运行时如何解决这个问题,或者一旦你预测它会发生什么,你可以做些什么来避免它。 最好尽量避免它首先发生。

1)您可以使用Findbugs ,它可能表示由于无意中从自身调用相同的方法而发生的一些StackOverFlow错误。

2)您可以使用SoftReference存储可能导致内存不足的数据,并在访问它时进行空检查,以便在垃圾回收时可以重新加载。

如果这些事情中的任何一个实际上都是您的问题,那么解决方案可能不会检测到它发生,而是以不同的方式构建您的解决方案,以避免它们尽可能发生。

您可以通过创建Throwable对象并查询其getStackTrace()方法来发现很多关于递归深度的信息。 但这样做很昂贵。

如果你真的有一个抛出StackOverflowError或OutOfMemoryError的可能性很小的方法,为什么不只是插入try-catch块并捕获这些错误? 它们可以像检查exception一样被捕获和处理。

对于StackOverflowError:

要知道当前的深度,通常是:

  1. 使用有状态函数(在函数外部存储深度)
  2. 使用累加器(将深度作为参数传递给函数)

知道它将发生的深度是困难的。 有几个因素:

  1. 分配给JVM的堆栈空间(可以使用-Xss选项更改此值)
  2. 已使用的堆栈空间量
  3. 当前函数使用的数量。

为什么不尝试使用这样的东西呢?

 public static void main(String[] args) { try { recurs(); } catch (Throwable t) { // not a good idea in production code.... } } static int depth = 0; static void recurs() { System.out.println(depth++); recurs(); } 

运行几次。 还尝试添加虚拟变量。 可以看出,即使相同的代码也可能在不同的深度处停止,并且添加更多变量会导致它更早结束。 所以,是的,几乎是不可预测的。

我想除了重写算法之外,唯一的选择是使用-Xss选项增加堆栈空间。

对于OutOfMemoryError,有-Xmx选项

我不知道如何防止这些错误情况,但如果你向线程添加一个未处理的exception处理程序至少你可以记录并可能在另一个线程中进行某种恢复。