內存泄露
?
???? ? 所謂內存泄露就是指一個不再被程序使用的對象或變量一直被占據在內存中。 ? java中有垃圾回收機制,它可以保證一對象不再被引用的時候,即對象編程了孤兒的時候,對象將自動被垃圾回收器從內存中清除掉。由于Java 使用有向圖的方式進行垃圾回收管理,可以消除引用循環的問題,例如有兩個對象,相互引用,只要它們和根進程不可達的,那么GC也是可以回收它們的,例如下面的代碼可以看到這種情況的內存回收:
- public ? class ?GarbageTest?{??
- ??
- ???? public ? static ? void ?main(String[]?args)? throws ?IOException?{??
- ???????? try ?{??
- ????????????gcTest();??
- ????????}? catch ?(IOException?e)?{??
- ????????????e.printStackTrace();??
- ????????}??
- ????????System.out.println( "has?exited?gcTest!" );??
- ????????System.in.read();??
- ????????System.in.read();?????????
- ????????System.out.println( "out?begin?gc!" );??????????
- ???????? for ( int ?i= 0 ;i< 100 ;i++)??
- ????????{??
- ????????????System.gc();??
- ????????????System.in.read();?????
- ????????????System.in.read();?????
- ????????}??
- ????}??
- ??
- ???? private ? static ? void ?gcTest()? throws ?IOException?{??
- ????????System.in.read();??
- ????????System.in.read();?????????
- ????????Person?p1?=? new ?Person();??
- ????????System.in.read();??
- ????????System.in.read();?????????
- ????????Person?p2?=? new ?Person();??
- ????????p1.setMate(p2);? //p1中包含p2的引用 ??
- ????????p2.setMate(p1);? //p2中包含p1的引用 ??
- ????????System.out.println( "before?exit?gctest!" );??
- ????????System.in.read();??
- ????????System.in.read();?????????
- ????????System.gc();??
- ????????System.out.println( "exit?gctest!" );??
- ????}??
- ??
- ???? private ? static ? class ?Person??
- ????{??
- ???????? byte []?data?=? new ? byte [ 20000000 ];??
- ????????Person?mate?=? null ;??
- ???????? public ? void ?setMate(Person?other)??
- ????????{??
- ????????????mate?=?other;??
- ????????}??
- ????}??
- }??
?????? 當gcTest()方法運行完畢以后,p1和p2對象都變成了垃圾,他們都不會被根對象所找到。關于根對象請參見《 JVM垃圾回收機制總結(2) :基本算法概述 ? 》中的關于“垃圾回收從哪里開始”的討論。
?
Java的內存泄露
?
雖然Java的垃圾回收機制較大程度的降低了內存泄露的可能性,但Java程序員仍然可能會寫出發生內存泄露的代碼。其原因就是一個已經不被使用的短壽命對象被一個長壽命對象(如類的靜態成員對象)引用,這就使得本來要被回收的短壽命對象永遠無法被回收,造成內存泄露。
?
?
內存泄露的典型情況
?
(1) 數據結構造成的短暫內存泄露問題,看下面的代碼
- public ? class ?Stack{??
- ?????? private ?Object[]?element= new ?Object[ 10 ];??
- ?????? private ? int ?size= 0 ;??
- ????????
- ?????? public ? void ?push(Object?ele){??
- ?????????????ensureCapacity();??
- ?????????????element[size++]=ele;??
- ??????}??
- ??
- ?????? public ?Object?pop(){??
- ????????????? if (size== 0 )? throw ? new ?EmptyStackException();??
- ????????????? return ?element[--size];? //短暫造成內存泄露 ??
- ??????}??
- ??
- ?????? private ? void ?ensureCapacity(){??
- ????????????? if (element.length==size){??
- ?????????????????????Object[]?oldElement=element;??
- ?????????????????????element= new ?Object[size* 2 + 1 ];??
- ?????????????????????System.arraycopy(oldElement, 0 ,element, 0 ,size);??
- ?????????????}??
- ??????}??
- }??
?????? 上面的代碼每一次pop()的時候,Stack都會彈出一個元素,在沒有加入新元素之前,實際上仍然有一個引用element[x]指向了這個已經彈出的對象,因此GC是不會對其進行垃圾回收的。只有push()新元素的時候使得element[x]=newObject,才會使得以前創建的對象有可能被回收。應該把上面的pop()方法改成下面的代碼就安全多了:
- public ?Object?pop(){??
- ??????? if (element.length==size)? throws ?EmptyStackException();??
- ???????Object?o=element[--size];??
- ???????elements[size]= null ;?? //使得GC有機會回收這個對象 ??
- ??????? return ?o;??
- }??
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
