Den*_*nov 97 java try-catch out-of-memory
文档的java.lang.Error说:
Error是Throwable的子类,表示合理的应用程序不应该尝试捕获的严重问题
但是作为java.lang.Error子类java.lang.Throwable,我可以捕获这种类型的Throwable.
我明白为什么抓住这种例外不是一个好主意.据我所知,如果我们决定捕获它,catch处理程序不应该自己分配任何内存.否则OutOfMemoryError将再次抛出.
所以,我的问题是:
java.lang.OutOfMemoryError可能是一个好主意?java.lang.OutOfMemoryError,我们怎么能确定catch处理程序本身不分配任何内存(任何工具或最佳实践)?Chr*_*ris 78
我同意并不同意这里的大部分回复.
在许多情况下,您可能希望捕获OutOfMemoryError并且根据我的经验(在Windows和Solaris JVM上),只有非常罕见的是OutOfMemoryErrorJVM的死亡.
只有一个很好的理由来抓住OutOfMemoryError,即优雅地关闭,干净地释放资源并尽可能地记录失败的原因(如果仍然可以这样做).
通常,OutOfMemoryError由于块内存分配无法满足堆的剩余资源而发生.
当Error抛出时,堆包含与不成功分配之前相同数量的已分配对象,现在是时候删除对运行时对象的引用以释放更多可能需要清理的内存.在这些情况下,甚至可能继续,但这绝对是一个坏主意,因为你永远不能100%确定JVM处于可修复状态.
演示OutOfMemoryError并不意味着JVM在catch块中内存不足:
private static final int MEGABYTE = (1024*1024);
public static void runOutOfMemory() {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
for (int i=1; i <= 100; i++) {
try {
byte[] bytes = new byte[MEGABYTE*500];
} catch (Exception e) {
e.printStackTrace();
} catch (OutOfMemoryError e) {
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
long maxMemory = heapUsage.getMax() / MEGABYTE;
long usedMemory = heapUsage.getUsed() / MEGABYTE;
System.out.println(i+ " : Memory Use :" + usedMemory + "M/" + maxMemory + "M");
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出此代码:
1 : Memory Use :0M/247M
..
..
..
98 : Memory Use :0M/247M
99 : Memory Use :0M/247M
100 : Memory Use :0M/247M
Run Code Online (Sandbox Code Playgroud)
如果运行一些关键的东西,我通常会抓住Error它,将其记录到syserr,然后使用我选择的日志框架记录它,然后继续释放资源并以干净的方式关闭.可能发生的最坏情况是什么?无论如何,JVM正在死亡(或已经死亡)并且通过捕获它Error至少有可能进行清理.
需要注意的是,您必须仅在可能进行清理的地方捕获这些类型的错误.不要catch(Throwable t) {}到处都是毯子或者那样废话.
Bal*_*usC 29
你可以从它恢复:
package com.stackoverflow.q2679330;
public class Test {
public static void main(String... args) {
int size = Integer.MAX_VALUE;
int factor = 10;
while (true) {
try {
System.out.println("Trying to allocate " + size + " bytes");
byte[] bytes = new byte[size];
System.out.println("Succeed!");
break;
} catch (OutOfMemoryError e) {
System.out.println("OOME .. Trying again with 10x less");
size /= factor;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
但它有意义吗?你还想做什么?为什么你最初会分配那么多内存?内存少也行吗?你为什么不用它呢?或者,如果这不可能,为什么不从一开始就给JVM更多的内存呢?
回到你的问题:
1:捕获java.lang.OutOfMemoryError可能是个好主意吗?
没有人想到.
2:如果我们捕获java.lang.OutOfMemoryError,我们如何确定catch处理程序本身不分配任何内存(任何工具或最佳实践)?
取决于导致OOME的原因.如果它在try街区之外宣布并且一步一步地发生,那么你的机会很小.您可能希望事先保留一些内存空间:
private static byte[] reserve = new byte[1024 * 1024]; // Reserves 1MB.
Run Code Online (Sandbox Code Playgroud)
然后在OOME期间将其设置为零:
} catch (OutOfMemoryException e) {
reserve = new byte[0];
// Ha! 1MB free!
}
Run Code Online (Sandbox Code Playgroud)
当然,这一切都没有意义;)只需给予JVM足够的内存,因为您的应用需要.必要时运行探查器.
Ste*_*n C 15
一般来说,尝试从OOM中捕获并恢复是一个坏主意.
OOME也可能被抛到其他线程上,包括你的应用程序甚至不知道的线程.任何这样的线程现在都已经死了,等待通知的任何东西都可能永远停滞不前.简而言之,您的应用可能会被彻底打破.
即使您成功恢复,您的JVM仍可能遭受堆饥饿,因此您的应用程序将执行得非常糟糕.
使用OOME最好的办法就是让JVM死掉.
(这假设JVM 确实死了.例如,Tomcat servlet线程上的OOM不会杀死JVM,这会导致Tomcat进入紧张状态,它不会响应任何请求......甚至不会请求重新开始.)
编辑
我并不是说抓住OOM是个坏主意.当您尝试从OOME恢复时,无论是故意还是疏忽,都会出现问题.每当你捕获一个OOM(直接或作为Error或Throwable的子类型)时,你应该重新抛出它,或者安排应用程序/ JVM退出.
旁白:这表明,为了在面对OOM时具有最大的健壮性,应用程序应该使用Thread.setDefaultUncaughtExceptionHandler()来设置一个处理程序,该处理程序将导致应用程序在OOME的情况下退出,无论引入OOME的是什么线程.我对这个意见感兴趣......
唯一的另一种情况是,当您确定 OOM没有导致任何附带损害时; 即你知道:
有些应用程序可以了解这些内容,但对于大多数应用程序,您无法确定OOME之后的延续是否安全.即使它在尝试时凭经验"有效".
(问题在于需要一个正式的证据来证明"预期的"OOME的后果是安全的,并且"未预料到的"OOME不能在try/catch OOME的控制下发生.)
Mic*_*uhn 13
是的,有真实的场景.这是我的:我需要在每个节点的内存有限的集群上处理很多项的数据集.给定的JVM实例一个接一个地遍历许多项目,但是有些项目太大而无法在集群上处理:我可以抓住OutOfMemoryError并注意哪些项目太大了.后来,我可以在具有更多RAM的计算机上重新运行大项目.
(因为它是一个失败的数组的单千兆字节分配,JVM在捕获错误之后仍然很好,并且有足够的内存来处理其他项目.)
Yis*_*hai 10
肯定有一些情况下捕捉OOME是有道理的.IDEA捕获它们并弹出一个对话框,让您更改启动内存设置(然后在完成后退出).应用程序服务器可能会捕获并报告它们.这样做的关键是在调度的高级别上执行此操作,以便您有可能在捕获异常时释放大量资源.
除了上面的IDEA场景之外,一般来说,捕获应该是Throwable,而不仅仅是OOM,并且应该在至少线程将很快终止的上下文中完成.
当然,大多数时候记忆力都很缺乏,情况也无法恢复,但有些方法是有意义的.
我遇到了这个问题,因为我想知道在我的情况下捕获OutOfMemoryError是否是一个好主意.我在这里部分回答是为了显示另一个例子,当抓住这个错误对某人(即我)有意义,并且部分地发现在我的情况下是否确实是一个好主意(我是一个超级初级开发人员,我永远不会对我写的任何一行代码都太确定了.
无论如何,我正在开发一个可以在不同内存大小的不同设备上运行的Android应用程序.危险的部分是从文件解码位图并在ImageView实例中将其解除.我不想在解码位图的大小方面限制功能更强大的设备,也不能确定应用程序不会在某些古老设备上运行我从未遇到过非常低的内存.因此我这样做:
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = 1;
boolean imageSet = false;
while (!imageSet) {
try {
image = BitmapFactory.decodeFile(filePath, bitmapOptions);
imageView.setImageBitmap(image);
imageSet = true;
}
catch (OutOfMemoryError e) {
bitmapOptions.inSampleSize *= 2;
}
}
Run Code Online (Sandbox Code Playgroud)
通过这种方式,我可以根据用户的需求和期望来提供功能更强大的设备.
是的,真正的问题是"你将在异常处理程序中做什么?" 几乎任何有用的东西,你都会分配更多的内存.如果您想在发生OutOfMemoryError时执行某些诊断工作,则可以使用-XX:OnOutOfMemoryError=<cmd>HotSpot VM提供的挂钩.它会在发生OutOfMemoryError时执行您的命令,并且您可以在Java堆之外执行一些有用的操作.你真的想让应用程序首先保持内存不足,所以找出它发生的原因是第一步.然后,您可以根据需要增加MaxPermSize的堆大小.以下是一些其他有用的HotSpot钩子:
-XX:+PrintCommandLineFlags
-XX:+PrintConcurrentLocks
-XX:+PrintClassHistogram
Run Code Online (Sandbox Code Playgroud)
请在此处查看完整列表
小智 5
我有一个需要从OutOfMemoryError故障中恢复的应用程序,在单线程程序中它始终可以工作,但有时在多线程程序中却不行。该应用程序是一个自动化的Java测试工具,可以将生成的测试序列执行到测试类的最大深度。现在,UI必须稳定,但是测试引擎可能会在耗尽测试用例树的同时耗尽内存。我通过测试引擎中的以下类型的代码习惯来处理此问题:
boolean isOutOfMemory = false; //用于报告的标志
尝试{
SomeType largeVar;
//越来越多地分配给largeVar的主循环
//可能会终止,或者引发OutOfMemoryError
}
抓(OutOfMemoryError ex){
// largeVar现在超出范围,垃圾也是如此
System.gc(); //清理largeVar数据
isOutOfMemory = true; //可以使用的标志
}
//程序测试标志以报告恢复
每次在单线程应用程序中都可以使用。但是我最近将测试引擎与UI放在了单独的工作线程中。现在,内存不足可能在任一线程中任意发生,而且我不清楚如何捕获它。
例如,当UI中的动画GIF帧由专有线程循环时发生了OOME,该专有线程由不受控制的Swing类在幕后创建。我以为自己已经预先分配了所有需要的资源,但是很明显,动画师每次获取下一个图像时都在分配内存。如果有人对如何处理在任何线程中引发的OOME有任何想法,我很想听听。
| 归档时间: |
|
| 查看次数: |
79457 次 |
| 最近记录: |