java內存泄露和內存溢出
Ⅰ java內存泄漏和內存溢出的區別
內存溢出 out of memory,是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of
memory;比如申請了一個integer,但給它存了long才能存下的數,那就是內存溢出。
內存泄露 memory
leak,是指程序在申請內存後,無法釋放已申請的內存空間,一次內存泄露危害可以忽略,但內存泄露堆積後果很嚴重,無論多少內存,遲早會被佔光。
memory leak會最終會導致out of memory!
內存溢出就是你要求分配的內存超出了系統能給你的,系統不能滿足需求,於是產生溢出。
內存泄漏是指你向系統申請分配內存進行使用(new),可是使用完了以後卻不歸還(delete),結果你申請到的那塊內存你自己也不能再訪問(也許你把它的地址給弄丟了),而系統也不能再次將它分配給需要的程序。一個盤子用盡各種方法只能裝4個果子,你裝了5個,結果掉倒地上不能吃了。這就是溢出!比方說棧,棧滿時再做進棧必定產生空間溢出,叫上溢,棧空時再做退棧也產生空間溢出,稱為下溢。就是分配的內存不足以放下數據項序列,稱為內存溢出.
Ⅱ java中的內存溢出和c++中的內存溢出,是一個概念嗎
java中的內存溢出和內存泄漏
內存溢出:
對於整個應用程序來說,JVM內存空間,已經沒有多餘的空間分配給新的對象。所以就發生內存溢出。
內存泄露:
在應用的整個生命周期內,某個對象一直存在,且對象佔用的內存空間越來越大,最終導致JVM內存泄露,
比如:緩存的應用,如果不設置上限的話,緩存的容量可能會一直增長。
靜態集合引用,如果該集合存放了無數個對象,隨著時間的推移也有可能使容量無限制的增長,最終導致JVM內存泄露。
內存泄露,是應用程序中的某個對象長時間的存活,並且佔用空間不斷增長,最終導致內存泄露。
是對象分配後,長時間的容量增長。
內存溢出,是針對整個應用程序的所有對象的分配空間不足,會造成內存溢出。
內存泄漏
內存泄漏指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏並非指內存在物理上的消失,而是應用程序分配某段內存後,由於設
計錯誤,失去了對該段內存的控制,因而造成了內存的浪費。內存泄漏與許多其他問題有著相似的症狀,並且通常情況下只能由那些可以獲得程序源代碼的程序員才
可以分析出來。然而,有不少人習慣於把任何不需要的內存使用的增加描述為內存泄漏,即使嚴格意義上來說這是不準確的。
一般我們常說的內存泄漏
是指堆內存的泄漏。堆內存是指程序從堆中分配的,大小任意的(內存塊的大小可以在程序運行期決定),使用完後必須顯示釋放的內存。應用程序一般使用
malloc,realloc,new等函數從堆中分配到一塊內存,使用完後,程序必須負責相應的調用free或delete釋放該內存塊,否則,這塊內
存就不能被再次使用,我們就說這塊內存泄漏了。
內存泄漏可以分為4類:
1.
常發性內存泄漏。發生內存泄漏的代碼會被多次執行到,每次被執行的時候都會導致一塊內存泄漏。
2.
偶發性內存泄漏。發生內存泄漏的代碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測內存泄漏至關重要。
3.
一次性內存泄漏。發生內存泄漏的代碼只會被執行一次,或者由於演算法上的缺陷,導致總會有一塊僅且一塊內存發生泄漏。比如,在類的構造函數中分配內存,在析構函數中卻沒有釋放該內存,所以內存泄漏只會發生一次。
4.
隱式內存泄漏。程序在運行過程中不停的分配內存,但是直到結束的時候才釋放內存。嚴格的說這里並沒有發生內存泄漏,因為最終程序釋放了所有申請的內存。但
是對於一個伺服器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內存也可能導致最終耗盡系統的所有內存。所以,我們稱這類內存泄漏為隱式內存泄漏。
簡單點:
內存泄漏就是忘記釋放使用完畢的內存,讓下次使用有一定風險。
內存溢出就是一定的內存空間不能裝下所有的需要存放的數據,造成內存數據溢出。
主要從以下幾部分來說明,關於內存和內存泄露、溢出的概念,區分內存泄露和內存溢出;內存的區域劃分,了解GC回收機制;重點關注如何去監控和發現內存問題;此外分析出問題還要如何解決內存問題。
下面就開始本篇的內容:
第一部分 概念
眾所周知,java中的內存由java虛擬機自己去管理的,他不像C++需要自己去釋放。籠統地
去講,java的內存分配分為兩個部分,一個是數據堆,一個是棧。程序在運行的時候一般分配數據堆,把局部的臨時的變數都放進去,生命周期和進程有關系。
但是如果程序員聲明了static的變數,就直接在棧中運行的,進程銷毀了,不一定會銷毀static變數。
另外為了保證java內存不會溢出,java中有垃圾回收機制。
System.gc()即垃圾收集機制是指jvm用於釋放那些不再使用的對象所佔用的內存。java語言並不要求jvm有gc,也沒有規定gc如何工作。垃圾收集的目的在於清除不再使用的對象。gc通過確定對象是否被活動對象引用來確定是否收集該對象。
而其中,內存溢出就是你要求分配的java虛擬機內存超出了系統能給你的,系統不能滿足需求,於是產生溢出。
內存泄漏是指你向系統申請分配內存進行使用(new),可是使用完了以後卻不歸還(delete),結果你申請到的那塊內存你自己也不能再訪
問,該塊已分配出來的內存也無法再使用,隨著伺服器內存的不斷消耗,而無法使用的內存越來越多,系統也不能再次將它分配給需要的程序,產生泄露。一直下
去,程序也逐漸無內存使用,就會溢出。
第二部分 原理
JAVA垃圾回收及對內存區劃分
在Java虛擬機規范中,提及了如下幾種類型的內存空間:
◇ 棧內存(Stack):每個線程私有的。
◇ 堆內存(Heap):所有線程公用的。
◇ 方法區(Method Area):有點像以前常說的「進程代碼段」,這裡面存放了每個載入類的反射信息、類函數的代碼、編譯時常量等信息。
◇ 原生方法棧(Native Method Stack):主要用於JNI中的原生代碼,平時很少涉及。
而Java的使用的是堆內存,java堆是一個運行時數據區,類的實例(對象)從中分配空間。Java虛擬機(JVM)的堆中儲存著正在運行的應用程序所建立的所有對象,「垃圾回收」也是主要是和堆內存(Heap)有關。
垃圾回收的概念就是JAVA虛擬機(JVM)回收那些不再被引用的對象內存的過程。一般我們認為正在被引用的對象狀態為「alive」,而沒有
被應用或者取不到引用屬性的對象狀態為「dead」。垃圾回收是一個釋放處於」dead」狀態的對象的內存的過程。而垃圾回收的規則和演算法被動態的作用於
應用運行當中,自動回收。
JVM的垃圾回收器採用的是一種分代(generational )回收策略,用較高的頻率對年輕的對象(young
generation)進行掃描和回收,這種叫做minor collection,而對老對象(old generation)的檢查回收頻率要低很多,稱為major
collection。這樣就不需要每次GC都將內存中所有對象都檢查一遍,這種策略有利於實時觀察和回收。
(Sun JVM 1.3
有兩種最基本的內存收集方式:一種稱為ing或scavenge,將所有仍然生存的對象搬到另外一塊內存後,整塊內存就可回收。這種方法有效率,但需要有一定的空閑內存,拷貝也有開銷。這種方法用於minor
collection。另外一種稱為mark-compact,將活著的對象標記出來,然後搬遷到一起連成大塊的內存,其他內存就可以回收了。這種方法不需要佔用額外的空間,但速度相對慢一些。這種方法用於major collection.
)
一些對象被創建出來只是擁有短暫的生命周期,比如 iterators 和本地變數。另外一些對象被創建是擁有很長的生命周期,比如持久化對象等。
垃圾回收器的分代策略是把內存區劃分為幾個代,然後為每個代分配一到多個內存區塊。當其中一個代用完了分配給他的內存後,JVM會在分配的內存區內執行一個局部的GC(也可以叫minor
collection)操作,為了回收處於「dead」狀態的對象所佔用的內存。局部GC通常要比Full GC快很多。
JVM定義了兩個代,年輕代(yong generation)(有時稱為「nursery」托兒所)和老年代(old generation)。年輕代包括
「Eden space(伊甸園)」和兩個「survivor spaces」。虛擬內存初始化的時候會把所有對象都分配到 Eden
space,並且大部分對象也會在該區域被釋放。 當進行 minor GC的時候,VM會把剩下的沒有釋放的對象從Eden space移動到其中一個survivor
spaces當中。此外,VM也會把那些長期存活在survivor spaces 里的對象移動到 老生代的「tenured」 space中。當 tenured
generation 被填滿後,就會產生Full GC,Full GC會相對比較慢因為回收的內容包括了所有的 live狀態的對象。pemanet
generation這個代包括了所有java虛擬機自身使用的相對比較穩定的數據對象,比如類和對象方法等。
關於代的劃分,可以從下圖中獲得一個概況:
第三部分 總結
內存溢出主要是由於代碼編寫時對某些方法、類應用不合理,或者沒有預估到臨時對象會佔用很大內存量,或者把過多的數據放入JVM緩存,或者性能
壓力大導致消息堆積而佔用內存,以至於在性能測試時,生成龐大數量的臨時對象,GC時沒有做出有效回收甚至根本就不能回收,造成內存空間不足,內存溢出。
如果編碼之前,對內存使用量進行預估,對放在內存中的數據進行評估,保證有用的信息盡快釋放,無用的信息能夠被GC回收,這樣在一定程度上是可以避免內存溢出問題的。
Ⅲ java有內存溢出嗎如果有是什麼情況
內存溢出是指應用系統中存在無法回收的內存或使用的內存過多,最終使得程序運行要用到的內存大於虛擬機能提供的最大內存。
所以我們應該明確:存在內存溢出的因不一定導致內存溢出的果。。。
1。JAVA操作文本文件為什麼超過3萬行就內存益處啊?
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
//PrintWriter out = new PrintWriter(fileName);
//FileOutputStream out = new FileOutputStream(fileName);
while (rs.next()) {
for (int j = 1; j <= totalColumn; j++) {
out.write(rs.getObject(j).toString());
out.write("\t");
}
out.write("\n");
out.flush();
}
}
我在代碼中 寫了 out.flush()用來刷新該流的緩沖; 可是當我的記錄數超過3W時就報了內存益處的問題了,難道JAVA不能邊讀邊寫嗎?還是out這個對象隨著指向的fileName文件的邊大佔用內存也大了嗎??到底怎麼來實現用JAVA寫更多的數據而不內存益處呢
答案是:就在while(rs.next()) 當rs.next()時內存不斷增大,而不是寫流的問題,JAVA的ResultSet真是麻煩,而且ResultSet還不能clone(); 所以記得在做項目的時候,經常要設置:jdbc.setMaxRows(100*10000); //設置能容納100萬行記錄-----這個就是防止內存泄露的哈------------------- 內存中載入的數據量過於龐大,如一次從資料庫取出過多數據;
jdbc.setQueryTimeout(60*30); //設置超時時間是30分鍾
2。java中的內存泄露的情況:長生命周期的對象持有短生命周期對象的引用就很可能發生內存泄露,盡管短生命周期對象已經不再需要,但是因為長生命周期對象持有它的引用而導致不能被回收,這就是java中內存泄露的發生場景,通俗地說,就是程序員可能創建了一個對象,以後一直不再使用這個對象,這個對象卻一直被引用,即這個對象無用但是卻無法被垃圾回收器回收的,這就是java中可能出現內存泄露的情況,例如,緩存系統,我們載入了一個對象放在緩存中(例如放在一個全局map對象中),然後一直不再使用它,這個對象一直被緩存引用,但卻不再被使用。
public class Stack { //長生命周期
private Object[] elements=new Object[10]; //當數組容器中沒有東西時是無用的,但是無法回收~~elements是短生命周期
private int size = 0;
public void push(Object e){
ensureCapacity();
elements[size++] = e;
}
public Object pop(){
if( size == 0)
throw new EmptyStackException();
//這里還是引用了,只是指針變位置變化而已,他確實返回了那個對象,但是他卻沒有斷開那個對象的引用,也就是說有兩個地方會持有這個對象的引用,調用pop的地方,和elements中
return elements[--size];
}
private void ensureCapacity(){
if(elements.length == size){
Object[] oldElements = elements;
elements = new Object[2 * elements.length+1];
System.array(oldElements,0, elements, 0, size);
}
}
}
上面的原理應該很簡單,假如堆棧加了10個元素,然後全部彈出來,雖然堆棧是空的,沒有我們要的東西,但是這是個對象是無法回收的,這個才符合了內存泄露的兩個條件(必要條件):無用,無法回收。
例子1
public class Bad{
public static Stack s=Stack();
static{
s.push(new Object());
s.pop(); //這里有一個對象發生內存泄露
s.push(new Object()); //上面的對象可以被回收了,等於是自愈了,因為引用被覆蓋了
}
}
因為是static,就一直存在到程序退出,但是我們也可以看到它有自愈功能,就是說如果你的Stack最多有100個對象,那麼最多也就只有100個對象無法被回收其實這個應該很容易理解,Stack內部持有100個引用,最壞的情況就是他們都是無用的,因為我們一旦放新的進取,以前的引用自然消失!
內存泄露的另外一種情況:當一個對象被存儲進HashSet集合中以後,就不能修改這個對象中的那些參與計算哈希值的欄位了,否則,對象修改後的哈希值與最初存儲進HashSet集合中時的哈希值就不同了,在這種情況下,即使在contains方法使用該對象的當前引用作為的參數去HashSet集合中檢索對象,也將返回找不到對象的結果,這也會導致無法從HashSet集合中單獨刪除當前對象,造成內存泄露。
這是屬於: 集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;
3。代碼中存在死循環或循環產生過多重復的對象實體;
4。啟動參數內存值設定的過小;
Ⅳ 內存溢出和內存泄漏的區別和如何避免內存溢
內存溢出:(out of memory)通俗理解就是內存不夠,通常在運行大型軟體或游戲時,軟體或游戲所需要的內存遠遠超出了你主機內安裝的內存所承受大小,就叫內存溢出。
內存泄漏:(Memory Leak)是指程序中己動態分配的堆內存由於某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重後果。
內存溢出解決方法:可參考
內存溢出_網路
https://ke..com/item/%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA/1430777?fr=aladdin
Ⅳ 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內存泄露
一、內存溢出類型
1、java.lang.OutOfMemoryError: PermGen space
JVM管理兩種類型的內存,堆和非堆。堆是給開發人員用的上面說的就是,是在JVM啟動時創建;非堆是留給JVM自己用的,用來存放類的信息的。它和堆不同,運行期內GC不會釋放空間。如果web app用了大量的第三方jar或者應用有太多的class文件而恰好MaxPermSize設置較小,超出了也會導致這塊內存的佔用過多造成溢出,或者tomcat熱部署時侯不會清理前面載入的環境,只會將context更改為新部署的,非堆存的內容就會越來越多。
2、java.lang.OutOfMemoryError: Java heap space
第一種情況是個補充,主要存在問題就是出現在這個情況中。其默認空間(即-Xms)是物理內存的1/64,最大空間(-Xmx)是物理內存的1/4。如果內存剩餘不到40%,JVM就會增大堆到Xmx設置的值,內存剩餘超過70%,JVM就會減小堆到Xms設置的值。所以伺服器的Xmx和Xms設置一般應該設置相同避免每次GC後都要調整虛擬機堆的大小。假設物理內存無限大,那麼JVM內存的最大值跟操作系統有關,一般32位機是1.5g到3g之間,而64位的就不會有限制了。
注意:如果Xms超過了Xmx值,或者堆最大值和非堆最大值的總和超過了物理內存或者操作系統的最大限制都會引起伺服器啟動不起來。
垃圾回收GC的角色
JVM調用GC的頻度還是很高的,主要兩種情況下進行垃圾回收:
當應用程序線程空閑;另一個是java內存堆不足時,會不斷調用GC,若連續回收都解決不了內存堆不足的問題時,就會報out of memory錯誤。因為這個異常根據系統運行環境決定,所以無法預期它何時出現。
根據GC的機制,程序的運行會引起系統運行環境的變化,增加GC的觸發機會。
為了避免這些問題,程序的設計和編寫就應避免垃圾對象的內存佔用和GC的開銷。顯示調用System.GC()只能建議JVM需要在內存中對垃圾對象進行回收,但不是必須馬上回收,
一個是並不能解決內存資源耗空的局面,另外也會增加GC的消耗。
二、JVM內存區域組成
簡單的說java中的堆和棧
java把內存分兩種:一種是棧內存,另一種是堆內存
1。在函數中定義的基本類型變數和對象的引用變數都在函數的棧內存中分配;
2。堆內存用來存放由new創建的對象和數組
在函數(代碼塊)中定義一個變數時,java就在棧中為這個變數分配內存空間,當超過變數的作用域後,java會自動釋放掉為該變數所分配的內存空間;在堆中分配的內存由java虛擬機的自動垃圾回收器來管理
堆的優勢是可以動態分配內存大小,生存期也不必事先告訴編譯器,因為它是在運行時動態分配內存的。缺點就是要在運行時動態分配內存,存取速度較慢;
棧的優勢是存取速度比堆要快,缺點是存在棧中的數據大小與生存期必須是確定的無靈活性。
java堆分為三個區:New、Old和Permanent
GC有兩個線程:
新創建的對象被分配到New區,當該區被填滿時會被GC輔助線程移到Old區,當Old區也填滿了會觸發GC主線程遍歷堆內存里的所有對象。Old區的大小等於Xmx減去-Xmn
java棧存放
棧調整:參數有+UseDefaultStackSize -Xss256K,表示每個線程可申請256k的棧空間
每個線程都有他自己的Stack
三、JVM如何設置虛擬內存
提示:在JVM中如果98%的時間是用於GC且可用的Heap size 不足2%的時候將拋出此異常信息。
提示:Heap Size 最大不要超過可用物理內存的80%,一般的要將-Xms和-Xmx選項設置為相同,而-Xmn為1/4的-Xmx值。
提示:JVM初始分配的內存由-Xms指定,默認是物理內存的1/64;JVM最大分配的內存由-Xmx指定,默認是物理內存的1/4。
默認空餘堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制;空餘堆內存大於70%時,JVM會減少堆直到-Xms的最小限制。因此伺服器一般設置-Xms、-Xmx相等以避免在每次GC 後調整堆的大小。
提示:假設物理內存無限大的話,JVM內存的最大值跟操作系統有很大的關系。
簡單的說就32位處理器雖然可控內存空間有4GB,但是具體的操作系統會給一個限制,
這個限制一般是2GB-3GB(一般來說Windows系統下為1.5G-2G,Linux系統下為2G-3G),而64bit以上的處理器就不會有限制了
提示:注意:如果Xms超過了Xmx值,或者堆最大值和非堆最大值的總和超過了物理內存或者操作系統的最大限制都會引起伺服器啟動不起來。
提示:設置NewSize、MaxNewSize相等,"new"的大小最好不要大於"old"的一半,原因是old區如果不夠大會頻繁的觸發"主" GC ,大大降低了性能
JVM使用-XX:PermSize設置非堆內存初始值,默認是物理內存的1/64;
由XX:MaxPermSize設置最大非堆內存的大小,默認是物理內存的1/4。
解決方法:手動設置Heap size
修改TOMCAT_HOME/bin/catalina.bat
在「echo "Using CATALINA_BASE: $CATALINA_BASE"」上面加入以下行:
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:MaxNewSize=256m"
四、性能檢查工具使用
定位內存泄漏:
JProfiler工具主要用於檢查和跟蹤系統(限於Java開發的)的性能。JProfiler可以通過時時的監控系統的內存使用情況,隨時監視垃圾回收,線程運行狀況等手段,從而很好的監視JVM運行情況及其性能。
1. 應用伺服器內存長期不合理佔用,內存經常處於高位佔用,很難回收到低位;
2. 應用伺服器極為不穩定,幾乎每兩天重新啟動一次,有時甚至每天重新啟動一次;
3. 應用伺服器經常做Full GC(Garbage Collection),而且時間很長,大約需要30-40秒,應用伺服器在做Full GC的時候是不響應客戶的交易請求的,非常影響系統性能。
因為開發環境和產品環境會有不同,導致該問題發生有時會在產品環境中發生,通常可以使用工具跟蹤系統的內存使用情況,在有些個別情況下或許某個時刻確實是使用了大量內存導致out of memory,這時應繼續跟蹤看接下來是否會有下降,
如果一直居高不下這肯定就因為程序的原因導致內存泄漏。
五、不健壯代碼的特徵及解決辦法
1、盡早釋放無用對象的引用。好的辦法是使用臨時變數的時候,讓引用變數在退出活動域後,自動設置為null,暗示垃圾收集器來收集該對象,防止發生內存泄露。
對於仍然有指針指向的實例,jvm就不會回收該資源,因為垃圾回收會將值為null的對象作為垃圾,提高GC回收機制效率;
2、我們的程序里不可避免大量使用字元串處理,避免使用String,應大量使用StringBuffer,每一個String對象都得獨立佔用內存一塊區域;
String str = "aaa";
String str2 = "bbb";
String str3 = str + str2;//假如執行此次之後str ,str2以後再不被調用,那它就會被放在內存中等待Java的gc去回收,程序內過多的出現這樣的情況就會報上面的那個錯誤,建議在使用字元串時能使用StringBuffer就不要用String,這樣可以省不少開銷;
3、盡量少用靜態變數,因為靜態變數是全局的,GC不會回收的;
4、避免集中創建對象尤其是大對象,JVM會突然需要大量內存,這時必然會觸發GC優化系統內存環境;顯示的聲明數組空間,而且申請數量還極大。
這是一個案例想定供大家警戒
使用jspsmartUpload作文件上傳,運行過程中經常出現java.outofMemoryError的錯誤,
檢查之後發現問題:組件里的代碼
m_totalBytes = m_request.getContentLength();
m_binArray = new byte[m_totalBytes];
問題原因是totalBytes這個變數得到的數極大,導致該數組分配了很多內存空間,而且該數組不能及時釋放。解決辦法只能換一種更合適的辦法,至少是不會引發outofMemoryError的方式解決。參考:http://bbs.xml.org.cn/blog/more.asp?name=hongrui&id=3747
5、盡量運用對象池技術以提高系統性能;生命周期長的對象擁有生命周期短的對象時容易引發內存泄漏,例如大集合對象擁有大數據量的業務對象的時候,可以考慮分塊進行處理,然後解決一塊釋放一塊的策略。
6、不要在經常調用的方法中創建對象,尤其是忌諱在循環中創建對象。可以適當的使用hashtable,vector 創建一組對象容器,然後從容器中去取那些對象,而不用每次new之後又丟棄
7、一般都是發生在開啟大型文件或跟資料庫一次拿了太多的數據,造成 Out Of Memory Error 的狀況,這時就大概要計算一下數據量的最大值是多少,並且設定所需最小及最大的內存空間值。
Ⅷ java是否有內存泄露和內存溢出
java中的內存溢出和內存泄漏
內存溢出:
對於整個應用程序來說,JVM內存空間,已經沒有多餘的空間分配給新的對象。所以就發生內存溢出。
內存泄露:
在應用的整個生命周期內,某個對象一直存在,且對象佔用的內存空間越來越大,最終導致JVM內存泄露,
比如:緩存的應用,如果不設置上限的話,緩存的容量可能會一直增長。
靜態集合引用,如果該集合存放了無數個對象,隨著時間的推移也有可能使容量無限制的增長,最終導致JVM內存泄露。
內存泄露,是應用程序中的某個對象長時間的存活,並且佔用空間不斷增長,最終導致內存泄露。
是對象分配後,長時間的容量增長。
內存溢出,是針對整個應用程序的所有對象的分配空間不足,會造成內存溢出。
內存泄漏
內存泄漏指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏並非指內存在物理上的消失,而是應用程序分配某段內存後,由於設
計錯誤,失去了對該段內存的控制,因而造成了內存的浪費。內存泄漏與許多其他問題有著相似的症狀,並且通常情況下只能由那些可以獲得程序源代碼的程序員才
可以分析出來。然而,有不少人習慣於把任何不需要的內存使用的增加描述為內存泄漏,即使嚴格意義上來說這是不準確的。
一般我們常說的內存泄漏
是指堆內存的泄漏。堆內存是指程序從堆中分配的,大小任意的(內存塊的大小可以在程序運行期決定),使用完後必須顯示釋放的內存。應用程序一般使用
malloc,realloc,new等函數從堆中分配到一塊內存,使用完後,程序必須負責相應的調用free或delete釋放該內存塊,否則,這塊內
存就不能被再次使用,我們就說這塊內存泄漏了。
內存泄漏可以分為4類:
1.
常發性內存泄漏。發生內存泄漏的代碼會被多次執行到,每次被執行的時候都會導致一塊內存泄漏。
2.
偶發性內存泄漏。發生內存泄漏的代碼只有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。所以測試環境和測試方法對檢測內存泄漏至關重要。
3.
一次性內存泄漏。發生內存泄漏的代碼只會被執行一次,或者由於演算法上的缺陷,導致總會有一塊僅且一塊內存發生泄漏。比如,在類的構造函數中分配內存,在析構函數中卻沒有釋放該內存,所以內存泄漏只會發生一次。
4.
隱式內存泄漏。程序在運行過程中不停的分配內存,但是直到結束的時候才釋放內存。嚴格的說這里並沒有發生內存泄漏,因為最終程序釋放了所有申請的內存。但
是對於一個伺服器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內存也可能導致最終耗盡系統的所有內存。所以,我們稱這類內存泄漏為隱式內存泄漏。
簡單點:
內存泄漏就是忘記釋放使用完畢的內存,讓下次使用有一定風險。
內存溢出就是一定的內存空間不能裝下所有的需要存放的數據,造成內存數據溢出。
主要從以下幾部分來說明,關於內存和內存泄露、溢出的概念,區分內存泄露和內存溢出;內存的區域劃分,了解GC回收機制;重點關注如何去監控和發現內存問題;此外分析出問題還要如何解決內存問題。
下面就開始本篇的內容:
第一部分 概念
眾所周知,java中的內存由java虛擬機自己去管理的,他不像C++需要自己去釋放。籠統地
去講,java的內存分配分為兩個部分,一個是數據堆,一個是棧。程序在運行的時候一般分配數據堆,把局部的臨時的變數都放進去,生命周期和進程有關系。
但是如果程序員聲明了static的變數,就直接在棧中運行的,進程銷毀了,不一定會銷毀static變數。
另外為了保證java內存不會溢出,java中有垃圾回收機制。
System.gc()即垃圾收集機制是指jvm用於釋放那些不再使用的對象所佔用的內存。java語言並不要求jvm有gc,也沒有規定gc如何工作。垃圾收集的目的在於清除不再使用的對象。gc通過確定對象是否被活動對象引用來確定是否收集該對象。
而其中,內存溢出就是你要求分配的java虛擬機內存超出了系統能給你的,系統不能滿足需求,於是產生溢出。
內存泄漏是指你向系統申請分配內存進行使用(new),可是使用完了以後卻不歸還(delete),結果你申請到的那塊內存你自己也不能再訪
問,該塊已分配出來的內存也無法再使用,隨著伺服器內存的不斷消耗,而無法使用的內存越來越多,系統也不能再次將它分配給需要的程序,產生泄露。一直下
去,程序也逐漸無內存使用,就會溢出。
第二部分 原理
JAVA垃圾回收及對內存區劃分
在Java虛擬機規范中,提及了如下幾種類型的內存空間:
◇ 棧內存(Stack):每個線程私有的。
◇ 堆內存(Heap):所有線程公用的。
◇ 方法區(Method Area):有點像以前常說的「進程代碼段」,這裡面存放了每個載入類的反射信息、類函數的代碼、編譯時常量等信息。
◇ 原生方法棧(Native Method Stack):主要用於JNI中的原生代碼,平時很少涉及。
而Java的使用的是堆內存,java堆是一個運行時數據區,類的實例(對象)從中分配空間。Java虛擬機(JVM)的堆中儲存著正在運行的應用程序所建立的所有對象,「垃圾回收」也是主要是和堆內存(Heap)有關。
垃圾回收的概念就是JAVA虛擬機(JVM)回收那些不再被引用的對象內存的過程。一般我們認為正在被引用的對象狀態為「alive」,而沒有
被應用或者取不到引用屬性的對象狀態為「dead」。垃圾回收是一個釋放處於」dead」狀態的對象的內存的過程。而垃圾回收的規則和演算法被動態的作用於
應用運行當中,自動回收。
JVM的垃圾回收器採用的是一種分代(generational )回收策略,用較高的頻率對年輕的對象(young
generation)進行掃描和回收,這種叫做minor collection,而對老對象(old generation)的檢查回收頻率要低很多,稱為major
collection。這樣就不需要每次GC都將內存中所有對象都檢查一遍,這種策略有利於實時觀察和回收。
(Sun JVM 1.3
有兩種最基本的內存收集方式:一種稱為ing或scavenge,將所有仍然生存的對象搬到另外一塊內存後,整塊內存就可回收。這種方法有效率,但需要有一定的空閑內存,拷貝也有開銷。這種方法用於minor
collection。另外一種稱為mark-compact,將活著的對象標記出來,然後搬遷到一起連成大塊的內存,其他內存就可以回收了。這種方法不需要佔用額外的空間,但速度相對慢一些。這種方法用於major collection.
)
一些對象被創建出來只是擁有短暫的生命周期,比如 iterators 和本地變數。另外一些對象被創建是擁有很長的生命周期,比如持久化對象等。
垃圾回收器的分代策略是把內存區劃分為幾個代,然後為每個代分配一到多個內存區塊。當其中一個代用完了分配給他的內存後,JVM會在分配的內存區內執行一個局部的GC(也可以叫minor
collection)操作,為了回收處於「dead」狀態的對象所佔用的內存。局部GC通常要比Full GC快很多。
JVM定義了兩個代,年輕代(yong generation)(有時稱為「nursery」托兒所)和老年代(old generation)。年輕代包括
「Eden space(伊甸園)」和兩個「survivor spaces」。虛擬內存初始化的時候會把所有對象都分配到 Eden
space,並且大部分對象也會在該區域被釋放。 當進行 minor GC的時候,VM會把剩下的沒有釋放的對象從Eden space移動到其中一個survivor
spaces當中。此外,VM也會把那些長期存活在survivor spaces 里的對象移動到 老生代的「tenured」 space中。當 tenured
generation 被填滿後,就會產生Full GC,Full GC會相對比較慢因為回收的內容包括了所有的 live狀態的對象。pemanet
generation這個代包括了所有java虛擬機自身使用的相對比較穩定的數據對象,比如類和對象方法等。
關於代的劃分,可以從下圖中獲得一個概況:
第三部分 總結
內存溢出主要是由於代碼編寫時對某些方法、類應用不合理,或者沒有預估到臨時對象會佔用很大內存量,或者把過多的數據放入JVM緩存,或者性能
壓力大導致消息堆積而佔用內存,以至於在性能測試時,生成龐大數量的臨時對象,GC時沒有做出有效回收甚至根本就不能回收,造成內存空間不足,內存溢出。
如果編碼之前,對內存使用量進行預估,對放在內存中的數據進行評估,保證有用的信息盡快釋放,無用的信息能夠被GC回收,這樣在一定程度上是可以避免內存溢出問題的。