我最近在一些代码中遇到过这种情况 - 基本上有人试图创建一个大对象,当没有足够的堆来创建它时应对:
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自己建议您不要尝试捕获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可能在您的方法调用时仅使用(来自OS)50M.其中,假设JVM实际上正在使用30M.这意味着你将显示20M免费(大致,因为我们只是在这里谈论堆),但如果你试图制作更大的对象,它将试图抓住其合同允许它从放弃和错误之前的操作系统.所以你实际上(理论上)有70M可用.
为了使这更复杂,它在上面的例子中报告的30M包括可能有资格进行垃圾收集的东西.所以你实际上可能有更多可用内存,如果它达到上限,它将尝试运行GC以释放更多内存.
您可以尝试通过手动触发System.GC来绕过这一点,除了那不是一件非常好的事情,因为
- 不保证立即运行
- 它将在运行时停止其轨道中的所有内容
你最好的选择(假设你不能轻易地重写你的算法来处理较小的内存块,或写入内存映射文件,或者内存不足的东西)可能是对所需内存进行安全粗略估计并确保它在你运行你的功能之前可用.