內存泄漏java
⑴ 如何防止java中的內存泄漏
盡管java虛
擬機和垃圾回收機制治理著大部分的內存事務,但是在java軟體中還是可能存在內存泄漏的情況。的確,在大型工程中,內存泄漏是一個普遍問題。避免內存泄
漏的第一步,就是要了解他們發生的原因。這篇文章就是要介紹一些常見的缺陷,然後提供一些非常好的實踐例子來指導你寫出沒有內存泄漏的代碼。一旦你的程序
存在內存泄漏,要查明代碼中引起泄漏的原因是很困難的。同時這篇文章也要介紹一個新的工具來查找內存泄漏,然後指明發生的根本原因。這個工具輕易上手,可
以讓你找到產品級系統中的內存泄漏。
垃圾回收(GC)的角色
雖然垃圾回收關心著大部分的
問題,包括內存治理,使得程序員的任務顯得更加輕松,但是程序員還是可能犯些錯誤導致內存泄漏問題。GC(垃圾回收)通過遞歸對所有從「根」對象(堆棧中
的對象,靜態數據成員,JNI句柄等等)繼續下來的引用進行工作,然後標記所有可以訪問的活動著的對象。而這些對象變成了程序唯一能夠操縱的對象,其他的
對象都被釋放了。因為GC使得程序不能夠訪問那些被釋放的對象,所以這樣做是安全的。
內存治理可以說是自動的,但是這並沒有讓程
序員脫離內存治理問題。比方說,對於內存的分配(還有釋放)總是存在一定的開銷,盡管這些開銷對程序員來說是隱含的。一個程序假如創建了很多對象,那麼它
就要比完成相同任務而創建了較少對象的程序執行的速度慢(假如其他的條件都相同)。
文章更多想說的,導致內存泄漏主要的原因是,
先前申請了內存空間而忘記了釋放。假如程序中存在對無用對象的引用,那麼這些對象就會駐留內存,消耗內存,因為無法讓垃圾回收器驗證這些對象是否不再需
要。正如我們前面看到的,假如存在對象的引用,這個對象就被定義為「活動的」,同時不會被釋放。要確定對象所佔內存將被回收,程序員就要務必確認該對象不
再會被使用。典型的做法就是把對象數據成員設為null或者從集合中移除該對象。注重,當局部變數不需要時,不需明顯的設為null,因為一個方法執行完
畢時,這些引用會自動被清理。
從更高一個層次看,這就是所有存在內存管的語言對內存泄漏所考慮的事情,剩餘的對象引用將不再會被使用。
典型的泄漏
既然我們知道了在java中確實會存在內存泄漏,那麼就讓我們看一些典型的泄漏,並找出他們發生的原因。
全局集合
在大型應用程序中存在各種各樣的全局數據倉庫是很普遍的,比如一個JNDI-tree或者一個session table。在這些情況下,注重力就被放在了治理數據倉庫的大小上。當然是有一些適當的機制可以將倉庫中的無用數據移除。
可以有很多不同的解決形式,其中最常用的是一種周期運行的清除作業。這個作業會驗證倉庫中的數據然後清除一切不需要的數據。
另一個辦法是計算引用的數量。集合負責跟蹤集合中每個元素的引用者數量。這要求引用者通知集合什麼時候已經對元素處理完畢。當引用者的數目為零時,就可以移除集合中的相關元素。
高速緩存
高速緩存是一種用來快速查找已經執行過的操作結果的數據結構。因此,假如一個操作執行很慢的話,你可以先把普通輸入的數據放入高速緩存,然後過些時間再調用高速緩存中的數據。
高速緩存多少還有一點動態實現的意思,當數據操作完畢,又被送入高速緩存。一個典型的演算法如下所示:
1. 檢查結果是否在高速緩存中,存在則返回結果;
2. 假如結果不在,那麼計算結果;
3. 將結果放入高速緩存,以備將來的操作調用。
這個演算法的問題(或者說潛在的內存泄漏)在最後一步。假如操作是分別多次輸入,那麼存入高速緩存的內容將會非常大。很明顯這個方法不可取。
為了避免這種潛在的致命錯誤設計,程序就必須確定高速緩存在他所使用的內存中有一個上界。因此,更好的演算法是:
1. 檢查結果是否在高速緩存中,存在則返回結果;
2. 假如結果不在,那麼計算結果;
3. 假如高速緩存所佔空間過大,移除緩存中舊的結果;
4. 將結果放入高速緩存,以備將來的操作調用。
通過不斷的從緩存中移除舊的結果,我們可以假設,將來,最新輸入的數據可能被重用的幾率要遠遠大於舊的結果。這通常是一個不錯的設想。
這個新的演算法會確保高速緩存的容量在預先確定的范圍內。精確的范圍是很難計算的,因為緩存中的對象存在引用時將繼續有效。正確的劃分高速緩存的大小是一個復雜的任務,你必須權衡可使用內存大小和數據快速存取之間的矛盾。
另一個解決這個問題的途徑是使用java.lang.ref.SoftReference類來將對象放入高速緩存。這個方法可以保證當虛擬機用完內存或者需要更多堆的時候,可以釋放這些對象的引用。
類裝載器
Java類裝載器創建就存在很多導致內存泄漏的漏洞。由於類裝載器的復雜結構,使得很難得到內存泄漏的透視圖。這些困難不僅僅是由於類裝載器只與「普通
的」對象引用有關,同時也和對象內部的引用有關,比如數據變數,方法和各種類。這意味著只要存在對數據變數,方法,各種類和對象的類裝載器,那麼類裝載器
將駐留在JVM中。既然類裝載器可以同很多的類關聯,同時也可以和靜態數據變數關聯,那麼相當多的內存就可能發生泄漏。
定位內存泄漏
經常地,程序內存泄漏的最初跡象發生在出錯之後,在你的程序中得到一個OutOfMemoryError。這種典型的情況發生在產品環境中,而在那裡,
你希望內存泄漏盡可能的少,調試的可能性也達到最小。也許你的測試環境和產品的系統環境不盡相同,導致泄露的只會在產品中暴露。這種情況下,你需要一個低
負荷的工具來監聽和尋找內存泄漏。同時,你還需要把這個工具同你的系統聯系起來,而不需要重新啟動他或者機械化你的代碼。也許更重要的是,當你做分析的時
候,你需要能夠同工具分離而使得系統不會受到干擾。
一個OutOfMemoryError經常是內存泄漏的一個標志,有可能應用
程序的確用了太多的內存;這個時候,你既不能增加JVM的堆的數量,也不能改變你的程序而使得他減少內存使用。但是,在大多數情況下,一個
OutOfMemoryError是內存泄漏的標志。一個解決辦法就是繼續監聽GC的活動,看看隨時間的流逝,內存使用量是否會增加,假如有,程序中一定
存在內存泄漏。
具體輸出
有很多辦法來監聽垃圾回收器的活動。也許運用最廣泛的就是以:-Xverbose:gc選項運行JVM,然後觀察輸出結果一段時間。
[memory] 10.109-10.235: GC 65536K->16788K (65536K), 126.000 ms
箭頭後的值(在這個例子中 16788K)是垃圾回收後堆的使用量。
控制台
觀察這些無盡的GC具體統計輸出是一件非常單調乏味的事情。好在有一些工具來代替我們做這些事情。The JRockit Management Console可以用圖形的方式輸出堆的使用量。通過觀察圖像,我們可以很方便的觀察堆的使用量是否伴隨時間增長。
Figure 1. The JRockit Management Console
治理控制台甚至可以配置成在堆使用量出現問題(或者其他的事件發生)時向你發送郵件。這個顯然使得監控內存泄漏更加輕易。
內存泄漏探測工具
有很多專門的內存泄漏探測工具。其中The JRockit Memory Leak
Detector可以供來觀察內存泄漏也可以針對性地找到泄漏的原因。這個強大的工具被緊密地集成在JRockit
JVM中,可以提供最低可能的內存事務也可以輕松的訪問虛擬機的堆。
專門工具的優勢
一旦
你知道程序中存在內存泄漏,你需要更專業的工具來查明為什麼這里會有泄漏。而JVM是不可能告訴你的。現在有很多工具可以利用了。這些工具本質上主要通過
兩種方法來得到JVM的存儲系統信息的:JVMTI和位元組碼儀器。Java虛擬機工具介面(JVMTI)和他的原有形式JVMPI(壓型介面,PRofiling Interface)都是標准介面,作為外部工具同JVM進行通信,搜集JVM的信息。位元組碼儀器則是引用通過探針獲得工具所需的位元組信息的預處理技術。
通過這些技術來偵測內存泄漏存在兩個缺點,而這使得他們在產品級環境中的運用不夠理想。首先,根據兩者對內存的使用量和內存事務性能的降級是不可以忽略
的。從JVM獲得的堆的使用量信息需要在工具中導出,收集和處理。這意味著要分配內存。按照JVM的性能導出信息是需要開銷的,垃圾回收器在搜集信息的時
候是運行的非常緩慢的。另一個缺點就是,這些工具所需要的信息是關繫到JVM的。讓工具在JVM開始運行的時候和它關聯,而在分析的時候,分離工具而保持
JVM運行,這顯然是不可能的。
既然JRockit Memory Leak
Detector是被集成到JVM中的,那麼以上兩種缺點就不再存在。首先,大部分的處理和分析都是在JVM中完成的,所以就不再需要傳送或重建任何數
據。處理也可以建立在垃圾回收器的基礎上,即提高速度。再有,內存泄漏偵測器可以同一個運行的JVM關聯和分離,只要JVM在開始的時候伴隨著
–Xmanagement選項(通過遠程JMX介面答應監聽和治理JVM)。當工具分離以後,工具不會遺留任何東西在JVM中;JVM就可以全速運行代碼
就似乎工具關聯之前一樣。
⑵ 如何識別Java中的內存泄漏
1、內存泄漏的現象:
常常地,程序內存泄漏的最初跡象發生在出錯之後,在程序中得到一個OutOfMemoryError。這種典型的情況發生在產品環境中,而在那裡,希望內存泄漏盡可能的少,調試的可能性也達到最小。也許測試環境和產品的系統環境不盡相同,導致泄露的只會在產品中暴露。這種情況下,需要一個低負荷的工具來監聽和尋找內存泄漏。同時,還需要把這個工具同系統聯系起來,而不需要重新啟動他或者機械化代碼。也許更重要的是,當做分析的時候,需要能夠同工具分離而使得系統不會受到干擾。
一個OutOfMemoryError常常是內存泄漏的一個標志,有可能應用程序的確用了太多的內存;這個時候,既不能增加JVM的堆的數量,也不能改變程序而使得減少內存使用。但是,在大多數情況下,一個OutOfMemoryError是內存泄漏的標志。一個解決辦法就是繼續監聽GC的活動,看看隨時間的流逝,內存使用量是否會增加,如果有,程序中一定存在內存泄漏。
2. 發現內存泄漏
1. jstat -gc pid
可以顯示gc的信息,查看gc的次數,及時間。
其中最後五項,分別是young gc的次數,young gc的時間,full gc的次數,full gc的時間,gc的總時間。
2.jstat -gccapacity pid
可以顯示,VM內存中三代(young,old,perm)對象的使用和佔用大小,
如:PGCMN顯示的是最小perm的內存使用量,PGCMX顯示的是perm的內存最大使用量,
PGC是當前新生成的perm內存佔用量,PC是但前perm內存佔用量。
其他的可以根據這個類推, OC是old內純的佔用量。
3.jstat -gcutil pid
統計gc信息統計。
4.jstat -gcnew pid
年輕代對象的信息。
5.jstat -gcnewcapacity pid
年輕代對象的信息及其佔用量。
6.jstat -gcold pid
old代對象的信息。
7.stat -gcoldcapacity pid
old代對象的信息及其佔用量。
8.jstat -gcpermcapacity pid
perm對象的信息及其佔用量。
9.jstat -class pid
顯示載入class的數量,及所佔空間等信息。
10.jstat -compiler pid
顯示VM實時編譯的數量等信息。
11.stat -printcompilation pid
當前VM執行的信息。
一些術語的中文解釋:
S0C:年輕代中第一個survivor(倖存區)的容量 (位元組)
S1C:年輕代中第二個survivor(倖存區)的容量 (位元組)
S0U:年輕代中第一個survivor(倖存區)目前已使用空間 (位元組)
S1U:年輕代中第二個survivor(倖存區)目前已使用空間 (位元組)
EC:年輕代中Eden(伊甸園)的容量 (位元組)
EU:年輕代中Eden(伊甸園)目前已使用空間 (位元組)
OC:Old代的容量 (位元組)
OU:Old代目前已使用空間 (位元組)
PC:Perm(持久代)的容量 (位元組)
PU:Perm(持久代)目前已使用空間 (位元組)
YGC:從應用程序啟動到采樣時年輕代中gc次數
YGCT:從應用程序啟動到采樣時年輕代中gc所用時間(s)
FGC:從應用程序啟動到采樣時old代(全gc)gc次數
FGCT:從應用程序啟動到采樣時old代(全gc)gc所用時間(s)
GCT:從應用程序啟動到采樣時gc用的總時間(s)
NGCMN:年輕代(young)中初始化(最小)的大小 (位元組)
NGCMX:年輕代(young)的最大容量 (位元組)
NGC:年輕代(young)中當前的容量 (位元組)
OGCMN:old代中初始化(最小)的大小 (位元組)
OGCMX:old代的最大容量 (位元組)
OGC:old代當前新生成的容量 (位元組)
PGCMN:perm代中初始化(最小)的大小 (位元組)
PGCMX:perm代的最大容量 (位元組)
PGC:perm代當前新生成的容量 (位元組)
S0:年輕代中第一個survivor(倖存區)已使用的占當前容量百分比
S1:年輕代中第二個survivor(倖存區)已使用的占當前容量百分比
E:年輕代中Eden(伊甸園)已使用的占當前容量百分比
O:old代已使用的占當前容量百分比
P:perm代已使用的占當前容量百分比
S0CMX:年輕代中第一個survivor(倖存區)的最大容量 (位元組)
S1CMX :年輕代中第二個survivor(倖存區)的最大容量 (位元組)
ECMX:年輕代中Eden(伊甸園)的最大容量 (位元組)
DSS:當前需要survivor(倖存區)的容量 (位元組)(Eden區已滿)
TT:持有次數限制
MTT :最大持有次數限制
⑶ java內存泄漏怎麼處理
一、Java內存回收機制
不論哪種語言的內存分配方式,都需要返回所分配內存的真實地址,也就是返回一個指針到內存塊的首地址。Java中對象是採用new或者反射的方法創建的,這些對象的創建都是在堆(Heap)中分配的,所有對象的回收都是由Java虛擬機通過垃圾回收機制完成的。GC為了能夠正確釋放對象,會監控每個對象的運行狀況,對他們的申請、引用、被引用、賦值等狀況進行監控,Java會使用有向圖的方法進行管理內存,實時監控對象是否可以達到,如果不可到達,則就將其回收,這樣也可以消除引用循環的問題。在Java語言中,判斷一個內存空間是否符合垃圾收集標准有兩個:一個是給對象賦予了空值null,以下再沒有調用過,另一個是給對象賦予了新值,這樣重新分配了內存空間。
二、Java內存泄露引起原因
首先,什麼是內存泄露看經常聽人談起內存泄露,但要問什麼是內存泄露,沒幾個說得清楚。內存泄露是指無用對象(不再使用的對象)持續佔有內存或無用對象的內存得不到及時釋放,從而造成的內存空間的浪費稱為內存泄露。內存泄露有時不嚴重且不易察覺,這樣開發者就不知道存在內存泄露,但有時也會很嚴重,會提示你Out of memory。
那麼,Java內存泄露根本原因是什麼呢看長生命周期的對象持有短生命周期對象的引用就很可能發生內存泄露,盡管短生命周期對象已經不再需要,但是因為長生命周期對象持有它的引用而導致不能被回收,這就是java中內存泄露的發生場景。具體主要有如下幾大類:
1、靜態集合類引起內存泄露:
像HashMap、Vector等的使用最容易出現內存泄露,這些靜態變數的生命周期和應用程序一致,他們所引用的所有的對象Object也不能被釋放,因為他們也將一直被Vector等引用著。
例:
Static Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{
Object o = new Object();
v.add(o);
o = null;
}//
在這個例子中,循環申請Object 對象,並將所申請的對象放入一個Vector 中,如果僅僅釋放引用本身(o=null),那麼Vector 仍然引用該對象,所以這個對象對GC 來說是不可回收的。因此,如果對象加入到Vector 後,還必須從Vector 中刪除,最簡單的方法就是將Vector對象設置為null。
2、當集合裡面的對象屬性被修改後,再調用remove()方法時不起作用。
例:
public static void main(String[] args)
{
Set<Person> set = new HashSet<Person>();
Person p1 = new Person("唐僧","pwd1",25);
Person p2 = new Person("孫悟空","pwd2",26);
Person p3 = new Person("豬八戒","pwd3",27);
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println("總共有:"+set.size()+" 個元素!"); //結果:總共有:3 個元素!
p3.setAge(2); //修改p3的年齡,此時p3元素對應的hashcode值發生改變
set.remove(p3); //此時remove不掉,造成內存泄漏
set.add(p3); //重新添加,居然添加成功
System.out.println("總共有:"+set.size()+" 個元素!"); //結果:總共有:4 個元素!
for (Person person : set)
{
System.out.println(person);
}
}
3、監聽器
在java 編程中,我們都需要和監聽器打交道,通常一個應用當中會用到很多監聽器,我們會調用一個控制項的諸如addXXXListener()等方法來增加監聽器,但往往在釋放對象的時候卻沒有記住去刪除這些監聽器,從而增加了內存泄漏的機會。
4、各種連接
比如資料庫連接(dataSourse.getConnection()),網路連接(socket)和io連接,除非其顯式的調用了其close()方法將其連接關閉,否則是不會自動被GC 回收的。對於Resultset 和Statement 對象可以不進行顯式回收,但Connection 一定要顯式回收,因為Connection 在任何時候都無法自動回收,而Connection一旦回收,Resultset 和Statement 對象就會立即為NULL。但是如果使用連接池,情況就不一樣了,除了要顯式地關閉連接,還必須顯式地關閉Resultset Statement 對象(關閉其中一個,另外一個也會關閉),否則就會造成大量的Statement 對象無法釋放,從而引起內存泄漏。這種情況下一般都會在try裡面去的連接,在finally裡面釋放連接。
5、內部類和外部模塊等的引用
內部類的引用是比較容易遺忘的一種,而且一旦沒釋放可能導致一系列的後繼類對象沒有釋放。此外程序員還要小心外部模塊不經意的引用,例如程序員A 負責A 模塊,調用了B 模塊的一個方法如:
public void registerMsg(Object b);
這種調用就要非常小心了,傳入了一個對象,很可能模塊B就保持了對該對象的引用,這時候就需要注意模塊B 是否提供相應的操作去除引用。
6、單例模式
不正確使用單例模式是引起內存泄露的一個常見問題,單例對象在被初始化後將在JVM的整個生命周期中存在(以靜態變數的方式),如果單例對象持有外部對象的引用,那麼這個外部對象將不能被jvm正常回收,導致內存泄露,考慮下面的例子:
class A{
public A(){
B.getInstance().setA(this);
}
....
}
//B類採用單例模式
class B{
private A a;
private static B instance=new B();
public B(){}
public static B getInstance(){
return instance;
}
public void setA(A a){
this.a=a;
}
//getter...
}
顯然B採用singleton模式,它持有一個A對象的引用,而這個A類的對象將不能被回收。想像下如果A是個比較復雜的對象或者集合類型會發生什麼情況
⑷ java內存泄露是什麼意思
Java內存泄露
一般來說內存泄漏有兩種情況。一種情況如在C/C++語言中的,在堆中的分配的內存,在沒有將其釋放掉的時候,就將所有能訪問這塊內存的方式都刪掉(如指針重新賦值);另一種情況則是在內存對象明明已經不需要的時候,還仍然保留著這塊內存和它的訪問方式(引用)。第一種情況,在Java中已經由於垃圾回收機制的引入,得到了很好的解決。所以,Java中的內存泄漏,主要指的是第二種情況。
可能光說概念太抽象了,大家可以看一下這樣的例子:
1 Vector v=new Vector(10);
2 for (int i=1;i<100; i++){
3 Object o=new Object();
4 v.add(o);
5 o=null;
6 }
在這個例子中,代碼棧中存在Vector對象的引用v和Object對象的引用o。在For循環中,不斷的生成新的對象,然後將其添加到Vector對象中,之後將o引用置空。問題是當o引用被置空後,如果發生GC,創建的Object對象是否能夠被GC回收呢?答案是否定的。因為,GC在跟蹤代碼棧中的引用時,會發現v引用,而繼續往下跟蹤,就會發現v引用指向的內存空間中又存在指向Object對象的引用。也就是說盡管o引用已經被置空,但是Object對象仍然存在其他的引用,是可以被訪問到的,所以GC無法將其釋放掉。如果在此循環之後,Object對象對程序已經沒有任何作用,那麼就認為此Java程序發生了內存泄漏。
盡管對於C/C++中的內存泄露情況來說,Java內存泄露導致的破壞性小,除了少數情況會出現程序崩潰的情況外,大多數情況下程序仍然能正常運行。但是,在移動設備對於內存和CPU都有較嚴格的限制的情況下,Java的內存溢出會導致程序效率低下、佔用大量不需要的內存等問題。這將導致整個機器性能變差,嚴重的也會引起拋出OutOfMemoryError,導致程序崩潰。
一般情況下內存泄漏的避免
在不涉及復雜數據結構的一般情況下,Java的內存泄露表現為一個內存對象的生命周期超出了程序需要它的時間長度。有時也將其稱為「對象游離」。
例如:
1 public class FileSearch{
2
3 private byte[] content;
4 private File mFile;
5
6 public FileSearch(File file){
7 mFile = file;
8 }
9
10 public boolean hasString(String str){
11 int size = getFileSize(mFile);
12 content = new byte[size];
13 loadFile(mFile, content);
14
15 String s = new String(content);
16 return s.contains(str);
17 }
18 }
在這段代碼中,FileSearch類中有一個函數hasString,用來判斷文檔中是否含有指定的字元串。流程是先將mFile載入到內存中,然後進行判斷。但是,這里的問題是,將content聲明為了實例變數,而不是本地變數。於是,在此函數返回之後,內存中仍然存在整個文件的數據。而很明顯,這些數據後續是不再需要的,這就造成了內存的無故浪費。
要避免這種情況下的內存泄露,要求以C/C++的內存管理思維來管理自己分配的內存。第一,是在聲明對象引用之前,明確內存對象的有效作用域。在一個函數內有效的內存對象,應該聲明為local變數,與類實例生命周期相同的要聲明為實例變數……以此類推。第二,在內存對象不再需要時,記得手動將其引用置空。
復雜數據結構中的內存泄露問題
在實際的項目中,經常用到一些較為復雜的數據結構用於緩存程序運行過程中需要的數據信息。有時,由於數據結構過於復雜,或者存在一些特殊的需求(例如,在內存允許的情況下,盡可能多的緩存信息來提高程序的運行速度等情況),很難對數據結構中數據的生命周期作出明確的界定。這個時候,可以使用Java中一種特殊的機制來達到防止內存泄露的目的。
之前介紹過,Java的GC機制是建立在跟蹤內存的引用機制上的。而在此之前,所使用的引用都只是定義一個「Object o;」這樣形式的。事實上,這只是Java引用機制中的一種默認情況,除此之外,還有其他的一些引用方式。通過使用這些特殊的引用機制,配合GC機制,就可以達到一些需要的效果。
⑸ java常見內存泄漏原因
Java內存泄露
一般來說內存泄漏有兩種情況。一種情況如在C/C++語言中的,在堆中的分配的內存,在沒有將其釋放掉的時候,就將所有能訪問這塊內存的方式都刪掉(如指針重新賦值);另一種情況則是在內存對象明明已經不需要的時候,還仍然保留著這塊內存和它的訪問方式(引用)。第一種情況,在Java中已經由於垃圾回收機制的引入,得到了很好的解決。所以,Java中的內存泄漏,主要指的是第二種情況。
可能光說概念太抽象了,大家可以看一下這樣的例子:
1 Vector v=new Vector(10);
2 for (int i=1;i<100; i++){
3 Object o=new Object();
4 v.add(o);
5 o=null;
6 }
⑹ java在什麼情況下會出現內存泄露
內存泄露就是指一個不再被程序使用的對象或變數一直被占據在內存中。java中的內存泄露的情況:
1.長生命周期的對象持有短生命周期對象的引用就很可能發生內存泄露,例如,緩存系統,我們載入了一個對象放在緩存中(例如放在一個全局map對象中),然後一直不再使用它,這個對象一直被緩存引用,但卻不再被使用。
2.集合類,而如果集合類是全局性的變數(比如類中的靜態屬性,全局性的map等即有靜態引用或final一直指向它),那麼沒有相應的刪除機制,很可能導致集合所佔用的內存只增不減,因此提供這樣的刪除機制或者定期清除策略非常必要。
3.單例模式。不正確使用單例模式是引起內存泄露的一個常見問題,單例對象在被初始化後將在JVM的整個生命周期中存在(以靜態變數的方式),如果單例對象持有外部對象的引用,那麼這個外部對象將不能被jvm正常回收,導致內存泄露
⑺ java的內存泄露是錯誤還是異常
java中內存泄漏有兩種情況。
一是在堆中的分配的內存,在沒有將其釋放掉的時候,就將所有能訪問這塊內存的方式都刪掉;另一種情況則是在內存對象明明已經不需要的時候,還仍然保留著這塊內存和它的訪問方式(引用)。在Java中已經由於垃圾回收機制的引入,第一種情況得到了很好的解決。所以,Java中的內存泄漏,主要指的是第二種情況。
Java的內存泄漏會導致程序效率低下、佔用大量不需要的內存等問題。這將導致整個機器性能變差,內存泄露不是錯誤也不是異常,但是嚴重的話也會引起拋出OutOfMemoryError,導致程序崩潰。