编程错误实例的剖析[1]内存不足与GC的错误用法

编程错误实例的剖析[1]内存不足与GC的错误用法

          这类文章没有一个主题范围,主要是将现实中遇到的比较有代表性、值得一说的错误(烂大街的错误就不说了~)进行详细的讨论。遇到了就写,也算是个人总结了。
     今天这个错误是我拜访一个朋友时,去他那里偶然遇到的,由于时间关系,当下没有跟他讨论。后来想了想,这个问题很有意思,也有点让人哭笑不得。
     这个朋友当时正在开发安卓应用的一个手机拍照模块,这片代码据他所说是网上抄的,为了解决内存不足的问题,这里给出代码片段:
Image img = null;
try{
	img = new Image(.....);//为拍照开辟堆区内存空间(Image只为说明意思,并不是准确的类名)
}
catch(OutOfMemoryError e){
	System.gc();//调用GC,处理堆碎片
	img = new Image(.....);//重新试图为拍照开辟堆区内存空间
}
        我第一次看到这个代码片时,给我的印象是:槽点太多,没法吐。
    首先我们先来说说Error这个东西,先明确一点,和Exception一样,Error也继承自Throwable,因此对其catch是没有任何语法错误的,但是我认为对它catch没有什么意义。
    在大学初学Java时,就明确了Error与Exception的区别,相比于Exception,Error往往意味着严重的问题,它的出现往往意味着程序无法执行(没有修补的余地),尤其是内存不足这种错误,我觉得catch到了你也没法解决了。


    之后的代码更为奇特,它显示地调用了gc,gc在整个JVM中占有不少分量,从名字上讲确实是垃圾处理,而且,它确实有清理堆区内存的作用,但gc绝对不是显示调用的。
    我们看一下对于gc的文档:
                                 
    这里注意两个我标注出来的词:
    suggests为建议、提醒之意,也就是说它是一个消息过程而非处理过程。
    made a best effort意为进了最大努力去做,言外之意是也有可能不会做。
    事实上,在重写finalize()的试验中,我们知道即使是显示的调用gc,对象也未必会被回收,事实上,在内存足够的时候,gc很有可能不去回收,以保证整体的执行效率。
    我个人认为,除了用于测试某些功能外,任何情况下都不应该显示地调用gc,如果你想主动地标记无用对象,赋值为null就可以了,如果觉得内存不够用,你可以修改jvm启动参数,gc就是为了将程序员从监视各种堆区内存释放的繁重工作中解放出来,完全不必要在显示地使用了。

    最后还有一个问题,它不是技术层面的,是逻辑层面的,我写一个代码片段,看看有没有问题:
try{
	访问硬盘
}
catch(硬盘错误 e){
	向硬盘中写入“硬盘出错”日志。
}
       不难发现,如果硬盘出错,那向硬盘写入日志这个操作是有潜在风险的,它极有可能执行不成功,这样,你的这个日志也就毫无意义。这和上面的代码片犯的是同一个错误,在内存不足时我们根本没有理由再去执行gc,更没有理由去企图分配更多的内存,这是一个本末倒置的错误,而它常见于IOException与close方法,将close单独写到捕获IOException的catch块中也是不合理的(起码还要加一个catch块,捕获close的异常)。
   
    最后,我觉得还是把他遇到的这个问题的解决思路写一下,我觉得对于内存分配问题可以从这几方面着手:
    对于没有缓冲区的对象,我们对其建立对应的缓冲区,也就是用各种Buffered类封装一下。
    对于频繁调用堆区的过程(例如在一个按钮的点击过程中new了对象),应从结构上出发,将其改成单例模式。
    可以更改jvm启动参数,扩大内存的分配(我觉得这是下策,如果结构有问题,再多内存也不行)。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值