56

我正在开发一个需要大量内存的程序,并且我想在发生内存不足异常时进行捕捉。我听说这是不可能的,但好奇这方面是否有任何发展。

4

9 回答 9

90

这也不例外。这是一个错误:java.lang.OutOfMemoryError

可以在它从 Throwable 下降时捕获它:

try {
    // create lots of objects here and stash them somewhere
} catch (OutOfMemoryError E) {
    // release some (all) of the above objects
}

但是,除非您正在做一些相当具体的事情(例如,在特定代码部分中分配大量内容),否则您可能无法捕捉到它,因为您不知道它会从哪里抛出。

于 2009-11-07T06:42:22.963 回答
57

这是可能的:

try {
   // tragic logic created OOME, but we can blame it on lack of memory
} catch(OutOfMemoryError e) {
   // but what the hell will you do here :)
} finally {
   // get ready to be fired by your boss
}
于 2009-11-07T06:49:14.067 回答
28

您可以捕获并尝试从 OutOfMemoryError (OOM) 异常中恢复,但这可能是一个坏主意……尤其是如果您的目标是让应用程序“继续运行”。

有许多的原因:

  1. 正如其他人所指出的,管理内存资源有比显式释放更好的方法。即对内存不足时可以释放的对象使用SoftReference 和WeakReference。

  2. 如果你等到真正耗尽内存后再释放东西,你的应用程序可能会花费更多时间运行垃圾收集器。根据您的 JVM 版本和您的 GC 调整参数,当 JVM 接近将引发 OOM 的点时,它最终可能会越来越频繁地运行 GC。减速(就应用程序做有用的工作而言)可能是显着的。你可能想避免这种情况。

  3. 如果问题的根本原因是内存泄漏,那么从 OOM 中捕获和恢复可能不会回收泄漏的内存。您的应用程序将继续运行一段时间,然后 OOM 一次又一次,一次又一次,间隔越来越小。

所以我的建议是不要试图从 OOM 中继续前进……除非知道

  • OOM 发生的地点和原因,
  • 不会有任何“附带损害”,并且
  • 您的恢复将释放足够的内存以继续。
于 2009-11-07T08:26:26.810 回答
14

只是把这个扔给那些思考为什么有人可能会耗尽内存的人:我正在做一个经常耗尽内存的项目,我不得不为此实施一个解决方案。

该项目是取证和调查应用程序的一个组成部分。在现场收集数据后(使用非常低的内存占用,顺便说一句)数据在我们的调查应用程序中打开。其中一项功能是对现场捕获的任意二进制图像(来自物理内存的应用程序)执行 CFG 遍历。这些遍历可能需要很长时间,但会产生非常有用的被遍历二进制文件的可视化表示。

为了加快遍历过程,我们尝试在物理内存中保留尽可能多的数据,但是数据结构会随着二进制文件的增长而增长,我们不能将其全部保留在内存中(目标是使用小于 256m 的 java 堆)。那我该怎么办?

我创建了 LinkedLists、Hashtables 等的磁盘支持版本。这些是它们对应物的直接替代品,并实现了所有相同的接口,因此它们从外部世界看起来是相同的。

区别?这些替换结构相互协作,捕捉到内存错误并请求从最近最少使用的集合中释放最近最少使用的元素。释放元素会将其转储到临时文件中的磁盘(在系统提供的临时目录中),并将占位符对象标记为正确集合中的“分页”。

有很多原因您可能会在 java 应用程序中耗尽内存 - 大多数这些原因的根源是以下之一或两者: 1. 应用程序在资源受限的机器上运行(或尝试通过限制堆大小来限制资源使用) 2. 应用程序只需要大量内存(建议进行图像编辑,但音频和视频怎么样?像我这样的编译器怎么样?没有非易失性存储的长期数据收集器怎么样?)

-少量

于 2010-06-17T14:52:45.410 回答
8

可以捕获一个OutOfMemoryError(它是一个Error,而不是一个Exception),但您应该知道,没有办法获得一个定义的行为。
您甚至可能在尝试捕获它时得到另一个 OutOfMemoryError。

所以更好的方法是创建/使用内存感知缓存。那里有一些框架(例如:JCS ),但您可以使用SoftReference轻松构建自己的框架。这里有一篇关于如何使用的小文章。按照文章中的链接获取更多信息。

于 2010-06-17T15:22:37.160 回答
5

当您专门分配可能太大的东西时,可能至少有一个好时机来捕获 OutOfMemoryError:

public static int[] decode(InputStream in, int len) throws IOException {
  int result[];
  try {
    result = new int[len];
  } catch (OutOfMemoryError e) {
    throw new IOException("Result too long to read into memory: " + len);
  } catch (NegativeArraySizeException e) {
    throw new IOException("Cannot read negative length: " + len);
  }
  ...
}
于 2010-06-17T15:28:50.960 回答
4

这是可能的,但如果你用完了堆,它就不是很有用。如果有可以释放的资源,最好对这些资源使用 SoftReference 或 WeakReference,它们的清理将是自动的。

我发现如果您在由于某种原因不会自动触发 GC 之前用完直接内存,这很有用。因此,如果我未能分配直接缓冲区,我就有理由强制执行 gc。

于 2009-11-07T07:57:05.793 回答
2

可以捕获任何异常。写吧

try{
   // code which you think might throw exception
}catch(java.lang.Throwable t){
   // you got the exception. Now what??
}

理想情况下,您不应该捕获java.lang.Error异常。不捕获此类异常,并让应用程序在它们发生时终止可能是最好的解决方案。如果您认为您可以很好地处理此类错误,请继续。

于 2009-11-07T06:57:08.763 回答
1

当然,捕获 OutOfMemoryError 是允许的。确保你有一个计划,当它发生时要做什么。在分配更多对象之前,您需要释放一些内存(通过删除对对象的引用),否则您将再次耗尽内存。有时仅仅将堆栈展开几帧就可以为您做到这一点,有时您需要做一些更明确的事情。

于 2009-11-07T06:45:26.403 回答