java的內(nèi)存泄露是如何發(fā)生的,如何避免和發(fā)現(xiàn)
java的垃圾回收與內(nèi)存泄露的關(guān)系:【新手可忽略不影響繼續(xù)學(xué)習(xí)】
馬克- to-win:馬克 java社區(qū):防盜版實(shí)名手機(jī)尾號(hào): 73203。
馬克-to-win:上一節(jié)講了,(i)對(duì)象被置成null.(ii)局部對(duì)象(無需置成null)當(dāng)程序運(yùn)行到右大括號(hào).(iii)匿名對(duì)象剛用完,垃圾回收線程就早早晚晚都能把它過去占的內(nèi)存給回收了。這么說,java中難道就沒有c++的內(nèi)存泄露的問題了嗎?(內(nèi)存泄露的定義就是: 咱自己程序不用的內(nèi)存,系統(tǒng)本應(yīng)回收但由于各種原因卻沒有回收成功)馬克-to-win:答案: 錯(cuò),java中有內(nèi)存泄露。下面我們就通過一個(gè)例子來說明。下面的例子中,Mark_to_win m作為實(shí)例是占有內(nèi)存空間的。即使后來m = null;把它置為null,垃圾回收線程也回收不了它占有的空間。因?yàn)榈任覀兒竺婕峡蚣軐W(xué)習(xí)了Vector以后,你就會(huì)知道:Vector v是一個(gè)類似數(shù)組的東西。馬克-to-win:任何通過v.add(m);加到Vector里的東西,Vector都會(huì)保留一個(gè)對(duì)它的引用。正因?yàn)橛羞@個(gè)引用,垃圾回收系統(tǒng)當(dāng)中的有向圖會(huì)認(rèn)為,這個(gè)對(duì)象還是可達(dá)的,所以不會(huì)回收它的內(nèi)存空間。因?yàn)閟ize_Make_to_win非常大,(是maxMemory的0.8倍),所以系統(tǒng)最后就崩潰了。馬克-to-win: 用專業(yè)術(shù)語講,就是開始時(shí)是內(nèi)存泄漏,泄露多了就造成內(nèi)存溢出了,所以就曝出OutOfMemoryError的錯(cuò)誤了。
例2.1.5
import java.util.Vector;
class Mark_to_win {
long data;
}
public class Test {
static Vector v = new Vector(10);
public static void main(String[] args) throws InterruptedException {
/*maxMemory:獲取系統(tǒng)所能提供的最大內(nèi)存。*/
int size_Make_to_win = (int) (Runtime.getRuntime().maxMemory() * 0.8);
for (int i = 1; i < size_Make_to_win; i++) {
Mark_to_win m = new Mark_to_win();
v.add(m);
m = null;
}
System.out.println("finish");
}
}
輸出結(jié)果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source)
at java.util.Arrays.copyOf(Unknown Source)
at java.util.Vector.ensureCapacityHelper(Unknown Source)
at java.util.Vector.add(Unknown Source)
at Test.main(Test.java:13)
上面例題之所以發(fā)生內(nèi)存溢出的原因是:任何通過v.add(m);加到Vector里的東西,Vector都會(huì)保留一個(gè)對(duì)它的引用。馬克-to-win: 所以只要我們通過v.remove(m)把這個(gè)引用去除。垃圾回收系統(tǒng),就可以把不用的對(duì)象所占用的空間給回收了。這樣就不報(bào)內(nèi)存溢出的錯(cuò)誤了。見下面的例子。
例2.1.5_1
import java.util.Vector;
class Mark_to_win {
long data;
}
public class Test {
static Vector v = new Vector(10);
public static void main(String[] args) throws InterruptedException {
/*maxMemory:獲取系統(tǒng)所能提供的最大內(nèi)存。*/
int size_Make_to_win = (int) (Runtime.getRuntime().maxMemory() * 0.8);
for (int i = 1; i < size_Make_to_win; i++) {
Mark_to_win m = new Mark_to_win();
v.add(m);
// 這里是干你的事
v.remove(m);
m = null;
}
System.out.println("finish");
}
}
輸出結(jié)果:
finish
馬克-to-win:通過以上的例子,說明了java中存在內(nèi)存泄露。當(dāng)泄露的很隱蔽時(shí),后果就會(huì)很嚴(yán)重,導(dǎo)致內(nèi)存溢出。通常市面上有幾款查內(nèi)存泄露的商業(yè)或非商業(yè)軟件,幫助大家監(jiān)控內(nèi)存泄露的問題。Rational 公司的Purify,dmalloc,Optimizeit Profiler,JProbe Profiler,JinSight,mtrace,memwatch,馬克-to-win:這里我們用jconsole來觀察一下前面我們的程序內(nèi)存泄漏的情況。例2.1.5是有內(nèi)存泄露的,為了能夠更好地觀察這個(gè)程序內(nèi)存泄漏的情況,我們加了一句,Thread.sleep(1),讓它能夠睡一毫秒。
例2.1.5_sleep版本:
import java.util.Vector;
class Mark_to_win {
long data;
}
public class Test {
static Vector v = new Vector(10);
public static void main(String[] args) throws InterruptedException {
/*maxMemory:獲取系統(tǒng)所能提供的最大內(nèi)存。*/
int size_Make_to_win = (int) (Runtime.getRuntime().maxMemory() * 0.8);
for (int i = 1; i < size_Make_to_win; i++) {
Mark_to_win m = new Mark_to_win();
Thread.sleep(1);
v.add(m);
// 這里是干你的事
m = null;
}
System.out.println("finish");
}
}
輸出結(jié)果:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source)
at java.util.Arrays.copyOf(Unknown Source)
at java.util.Vector.ensureCapacityHelper(Unknown Source)
at java.util.Vector.add(Unknown Source)
at Test.main(Test.java:13)
以上程序運(yùn)行起來以后,我們啟動(dòng)jconsole來監(jiān)視它:(新手可忽略此章節(jié))
馬克-to-win:從上圖可以看出,隨著時(shí)間的流逝,內(nèi)存消耗呈現(xiàn)穩(wěn)定上升狀態(tài)?;究梢耘卸ǎ谖磥硪粋€(gè)時(shí)間點(diǎn),系統(tǒng)會(huì)發(fā)生內(nèi)存溢出。即系統(tǒng)崩潰。一旦 jconsole呈現(xiàn)這種圖形,我們就需要修改程序了。這就是工具如何幫我們發(fā)現(xiàn)內(nèi)存泄漏以及內(nèi)存溢出。馬克-to-win:例2.1.5_1是內(nèi)存不泄露的, 我們加上Thread.sleep(1), 讓它睡一下,從而用jconsole也觀察一下, 看一下正常的內(nèi)存使用應(yīng)是什么情況的。
例2.1.5_1_sleep:
import java.util.Vector;
class Mark_to_win {
long data;
}
public class Test {
static Vector v = new Vector(10);
public static void main(String[] args) throws InterruptedException {
/*maxMemory:獲取系統(tǒng)所能提供的最大內(nèi)存。*/
int size_Make_to_win = (int) (Runtime.getRuntime().maxMemory() * 0.8);
for (int i = 1; i < size_Make_to_win; i++) {
Mark_to_win m = new Mark_to_win();
Thread.sleep(1);
v.add(m);
// 這里是干你的事
v.remove(m);
m = null;
}
System.out.println("finish");
}
}
輸出結(jié)果:
finish
以上程序運(yùn)行起來以后,我們啟動(dòng)jconsole來監(jiān)視它:(新手可忽略此章節(jié))
曲線呈現(xiàn)平穩(wěn)波動(dòng)。內(nèi)存使用情況正常。