是否有可能在java中捕获内存不足?

jy.*_*jy. 52 java exception out-of-memory

我正在开发一个需要大量内存的程序,我想在发生内存不足异常时捕获.我听说这是不可能做到的,但是如果在这方面有任何发展,我很好奇.

Chs*_*y76 84

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

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

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

但是,除非你正在做一些相当具体的事情(比如在特定的代码段中分配大量的东西),否则你可能无法捕获它,因为你不知道它会从哪里被抛出.

  • 另外,如果您抓住它,可能没有简单的方法可以从中恢复. (17认同)

Sur*_*ran 53

这是可能的:

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
}
Run Code Online (Sandbox Code Playgroud)

  • **是*至少有一个合理的东西,你可以做的导致OOME并且可以恢复:加载一个非常大的图像.try块中唯一的东西是对ImageIO.read()的调用,并且您向用户显示一个对话框,告诉他们catch块中的图像太大.只是说...... (11认同)
  • @SurajChandran:试图加载一个非常大的图像引起的OOME将是由于试图分配一个非常大的`byte []`或`int []`.你得到了一个OOME,因为分配失败了,不是因为你实际上没有内存 - 因此它不会在其他线程上引起问题. (4认同)
  • @uckelman 这是错误的方法,除非您的应用程序是单线程的。问题是,即使您的图像加载线程捕获了 OOME,但其他线程也不知道这一点并且也可以获得 OOME (3认同)

Ste*_*n C 25

您可以捕获并尝试从OutOfMemoryError(OOM)异常中恢复,但这可能是一个糟糕的想法 ...特别是如果您的目标是让应用程序"继续".

有许多的原因:

  1. 正如其他人所指出的那样,有更好的方法来管理内存资源而不是明确地释放内容; 即对内存较短时可以释放的对象使用SoftReference和WeakReference.

  2. 如果你等到实际耗尽内存之前你的应用程序,你的应用程序可能会花更多的时间来运行垃圾收集器.根据您的JVM版本和GC调整参数,JVM可能会越来越频繁地运行GC,因为它接近抛出OOM的点.减速(就应用程序做有用的工作而言)可能很重要.你可能想避免这种情况.

  3. 如果您的问题的根本原因是内存泄漏,那么从OOM捕获和恢复的可能性将无法回收泄漏的内存.您的应用程序将再次持续一段时间,然后再次,并再次减少间隔.

所以我的建议是不要试图继续从OOM出发...除非你知道:

  • OOM发生的地点和原因,
  • 没有任何"附带损害",并且
  • 您的恢复将释放足够的内存以继续.


小智 14

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

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

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

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

区别?这些替换结构彼此协作,消除内存错误并请求从最近最少使用的集合中最近最少使用的元素从存储器中释放.释放元素将其转储到临时文件(在系统提供的临时目录中)中的磁盘,并在适当的集合中将占位符对象标记为"分页".

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


Har*_*ded 8

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

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

  • "没有办法获得定义的行为":因为`OutOfMemoryError`可以抛到任何地方,包括可能使程序处于不一致状态的地方.请参见http://stackoverflow.com/questions/8728866/no-throw-virtualmachineerror-guarantees (2认同)

Pet*_*rey 5

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

我发现如果您在由于某种原因无法自动触发 GC 之前耗尽直接内存,它会很有用。因此,如果我无法分配直接缓冲区,我就有理由强制执行GC。


Ada*_*ode 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);
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

  • 这种方法在C++中运行良好,但我怀疑在Java中不太好,因为`new`和`OutOfMemoryError`之间的连接是间接的.如果分配只适合内存,可能会在一段时间后出现神秘的"OutOfMemoryError". (4认同)