我最近在一些代码中遇到了这个 - 基本上有人试图创建一个大对象,当没有足够的堆来创建它时应对:
try {
// try to perform an operation using a huge in-memory array
byte[] massiveArray = new byte[BIG_NUMBER];
} catch (OutOfMemoryError oome) {
// perform the operation in some slower but less
// memory intensive way...
}
这似乎不对,因为 Sun 自己建议您不要 try catch Error
或其子类。我们对此进行了讨论,提出的另一个想法是显式检查空闲堆:
if (Runtime.getRuntime().freeMemory() > SOME_MEMORY) {
// quick memory-intensive approach
} else {
// slower, less demanding approach
}
同样,这似乎并不令人满意 - 特别是为 SOME_MEMORY
选择一个值很难轻易地与所讨论的工作相关联:对于一些任意的大对象,我如何估计它的实例化有多少内存可能需要?
有更好的方法吗?在 Java 中甚至有可能吗,或者是否有任何在语言本身的抽象级别下管理内存的想法?
编辑 1: 在第一个示例中,估计给定长度的 byte[]
可能占用的内存量实际上是可行的,但是是否存在扩展到任意大对象的更通用的方法?
编辑 2: 正如 @erickson 指出的那样,有一些方法可以在对象创建后估计其大小,但是(忽略基于先前对象大小的统计方法)是否有一种方法对尚未创建的对象这样做?
关于捕获 OutOfMemoryError
是否合理似乎也存在一些争论 - 有人知道任何结论吗?
最佳答案
freeMemory 不太正确。您还必须添加 maxMemory()-totalMemory()。例如假设您使用 max-memory=100M 启动 VM,则 JVM 在您的方法调用时可能仅使用(来自操作系统)50M。其中,假设 JVM 实际使用了 30M。这意味着您将免费显示 20M(粗略地说,因为我们在这里只讨论堆),但是如果您尝试制作更大的对象,它将尝试获取其他 50M,其契约(Contract)允许它从放弃和错误之前的操作系统。所以你实际上(理论上)有 70M 可用。
为了使事情变得更复杂,它在上面的示例中报告的正在使用的 30M 包括可能符合垃圾收集条件的内容。因此,您实际上可能有更多可用内存,如果它达到上限,它将尝试运行 GC 以释放更多内存。
您可以尝试通过手动触发 System.GC 来解决这个问题,但这样做并不是一件好事,因为
-不保证立即运行
-它会在运行时停止所有在其轨道上的东西
你最好的选择(假设你不能轻易地重写你的算法来处理更小的内存块,或者写入内存映射文件,或者内存密集度较低的东西)可能是对所需的内存做一个安全的粗略估计并确保它在运行函数之前可用。
关于Java:足够的空闲堆来创建一个对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/331953/