Java也会内存泄露吗? 读书的时候,都说Java自动管理内存,JVM内涵GC机制,只管用不管释放,JVM自动会回收内存,不会存在内存泄露.
不过经过多年C&C++编程,一直尚未接触Java,也不知道Java是否真的永远不会内存泄露.....,抱着怀疑的精神,我一直疑问不敢实战求证,因为我不会Java,为什么怀疑呢?因为JVM无非还是用C&C++实现的,咋就不会内存泄露呢?C++都要内存泄露,Java这种字节码还需要翻译的运行在JVM内的语言就不会内存泄露?当然我承认,我当时的理解还停留在原生态....
多年之后,再来接触Java,发现我被欺骗了....
Java真的不会内存泄露吗?
面试过很多人,问他们曾经是否写有过Java内存泄露的代码,无一回答曾有.而真正实战代码的时候,我却发现,他们都写着一堆疑似内存泄露的代码,或许我被欺骗,或许他们的代码没有经过日夜煎熬奋战在最前线,每天享受着500以上的并发处理吧.
从不少同学,同事口里得知,其实Java会"内存泄露",曾经有个同事说,X组做的J2EE应用服务,就像定时炸 弹,一天重启七八次....为什么重启呢?因为内存泄露.
自己也做了J2EE应用服务,虽然没有达到一天重启七八次,也有两三天一次,高峰的时候一天两次.为什么呢?因为内存泄露...
此处,我阐述一下我对内存泄露的理解,内存泄露一般大家都认为是C和C++中new出一块内存,然后没有释放delete,这块内存就造成了内存泄露.其实我也是这么理解的,不过我更觉得,一块内存如果被申请,存放了一堆废弃的数据,而且内存一直这样占据下去,我觉得也是一种内存泄露.当然更恐怖的就是这样占据的内存在一个程序的关键点使用造成不停的增大,就等待crash吧...
所以在我眼中,Java的内存泄露与C++的内存泄露没什么区别,一个是GC可达却无法去释放,一个是人为没有delete,这两种情况都是一块内存没有在可delete的范围之内,当然有人说,Java的任何对象是可达的连通图,C++的内存是没有任何对象handle了此内存.Java更安全一些,可是我想说,Java就像一张被银行印有伪钞二字的RMB,C++就像一个分钱没有的穷光蛋,其实大家都是一无所有.大家都是内存泄露...
从理论上讲,Java内存泄露的范围更小但是我觉得她更危险!为什么?有这么一个习惯,大多时候,让错误ERROR尽量的暴露出来而不是隐藏下去继续执行...
GC机制没有一个标准,那是一个算法,各个JVM的GC机制不一样,算法不一样,只要算法有漏洞,你的程序可能存在内存泄露,金无足赤,所以不要期待有完美的算法,同样的代码使用不同的JVM,极端情况下,有一个JVM就可能内存泄露,而另外一个JVM可能不会内存泄露.越说越玄乎....其实不要那么担心,编写JVM的都是世界达人,我们要相信比我们聪明的达人.
废话不多说,说说最近两次解决的系统内存泄露问题.
上面说过,Java内存泄露与JVM的GC算法机制有关系,网上经常有人举Java内存泄露的代码,如:
List
for(int i=0;i<10;i++){
Objet a = new Object();
list.add(a);
}
网上对这句话的解释太多了,new Object发生了内存泄露,其实我脑袋被铁打了,我根本看不出new Object怎么内存泄露了,因为new Object被list对象hold住了,list没有释放,尽管a释放了,但是还是内存泄露了.........这个我怎么说好呢,这个叫断章取义吧,那list释放了Object也就被释放了嘛,那也就是说list永远不释放的时候才会造成Object的内存泄露啊,我想知道我们在什么时候不会对list做释放?
大家都很聪明,我们声明list为static变量的时候,list就永远不会释放啦,但我很想知道大家是在写程序还是在做辩论会?既然你声明list为全局static变量,那么必定是想让Object被hold住,被全局用起来,做一个缓存作用.如果你不是此意,你为什么要用static变量?如果是这样还是回家看看ABC吧.
所以如果你书写上面的如此代码是不会造成内存泄露的,除非你声明为static,并且将此段代码在程序中反复调用.除非是故意,否则我想中国程序员还没有达到故意制造内存泄露的境界.
而我第一次解决内存泄露,应该归纳为"内存碎片",因为零星的内存碎片(其实是很小的Java对象)存在于JVM的内存池中[不清楚JVM的内存池是对象内存池还是原生内存池],无论哪种情况,都是因为很小的内存对象被new出来,存在于JVM内存池的各个角落,造成JVM不断GC,GC的过程中内存碎片又总是被hold住无法释放,随着时间的推移,当接近heap峰值的时候,JVM因为GC而直接crash...
一般内存碎片是因为不当的编码,将零星无用对象秒存在了某个长生命周期对象当中被持有必须等待父对象释放才能释放"内存碎片",我遇见的内存碎片是在action当中对List成员直接进行new空数组,然后再将List对象指向来自hibernate的对象数组.造成new出来的数组对象成为内存碎片.如果下面的代码:
List list = new ArrayList();
list = listOther; // listOther是来自hibernate返回的数据库PO对象列表
不是new ArrayList不会释放,而是他释放的时机与JVM的GC机制形成了对抗,服务器采用的是Weblogic & SUN JDK1.6至于其他JVM就不太清楚是否也会这样.
第二次内存泄露更疯狂,一天重启两次服务器,有了第一次内存泄露的经验,此次内存泄露解决时间大大缩短到了一个星期以内.第二次是CXF的webservice与quartz定时器相结合的时候,在设置webservice的HttpClientPolicy权限时,声明了一个静态常量static HttpClientPolicy作为webservice的公共权限,因为webservice内部编码机制,让HttpClientPolicy反引用了WebService的Client,造成内存无法释放.并且在WebService中加入了listener,没有对listener进行清理.加上quartz定时器破坏了Sprin的引用规则,多种原因错综复杂搅在一起造成大量hold内存对象.所以提个醒,Java引用复杂,不要轻易声明static变量,不要认为是别人hold住你,而不是你hold住别人,因为在你没有明确编码内部算法的时候,大家都有有可能互相hold
本文转自:http://hi.baidu.com/crazyonline/item/cc01a883201ebccdee083d76