1、為什么會(huì)發(fā)生內(nèi)存泄漏
創(chuàng)新互聯(lián)建站專業(yè)為企業(yè)提供喀什網(wǎng)站建設(shè)、喀什做網(wǎng)站、喀什網(wǎng)站設(shè)計(jì)、喀什網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、喀什企業(yè)網(wǎng)站模板建站服務(wù),10余年喀什做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
Java如何檢測(cè)內(nèi)在泄漏呢?我們需要一些工具進(jìn)行檢測(cè),并發(fā)現(xiàn)內(nèi)存泄漏問(wèn)題,不然很容易發(fā)生down機(jī)問(wèn)題。
編寫(xiě)java程序最為方便的地方就是我們不需要管理內(nèi)存的分配和釋放,一切由jvm來(lái)進(jìn)行處理,當(dāng)java對(duì)象不再被應(yīng)用時(shí),等到堆內(nèi)存不夠用時(shí),jvm會(huì)進(jìn)行垃圾回收,清除這些對(duì)象占用的堆內(nèi)存空間,如果對(duì)象一直被應(yīng)用,jvm無(wú)法對(duì)其進(jìn)行回收,創(chuàng)建新的對(duì)象時(shí),無(wú)法從Heap中獲取足夠的內(nèi)存分配給對(duì)象,這時(shí)候就會(huì)導(dǎo)致內(nèi)存溢出。而出現(xiàn)內(nèi)存泄露的地方,一般是不斷的往容器中存放對(duì)象,而容器沒(méi)有相應(yīng)的大小限制或清除機(jī)制。容易導(dǎo)致內(nèi)存溢出。
當(dāng)服務(wù)器應(yīng)用占用了過(guò)多內(nèi)存的時(shí)候,如何快速定位問(wèn)題呢?現(xiàn)在,Eclipse MAT的出現(xiàn)使這個(gè)問(wèn)題變得非常簡(jiǎn)單。EclipseMAT是著名的SAP公司貢獻(xiàn)的一個(gè)工具,可以在Eclipse網(wǎng)站下載到它,完全免費(fèi)的。
要定位問(wèn)題,首先你需要獲取服務(wù)器jvm某刻內(nèi)存快照。jdk自帶的jmap可以獲取內(nèi)存某一時(shí)刻的快照,導(dǎo)出為dmp文件后,就可以用Eclipse MAT來(lái)分析了,找出是那個(gè)對(duì)象使用內(nèi)存過(guò)多。
2、內(nèi)存泄漏的現(xiàn)象:
常常地,程序內(nèi)存泄漏的最初跡象發(fā)生在出錯(cuò)之后,在你的程序中得到一個(gè)OutOfMemoryError。這種典型的情況發(fā)生在產(chǎn)品環(huán)境中,而在那里,你希望內(nèi)存泄漏盡可能的少,調(diào)試的可能性也達(dá)到最小。也許你的測(cè)試環(huán)境和產(chǎn)品的系統(tǒng)環(huán)境不盡相同,導(dǎo)致泄露的只會(huì)在產(chǎn)品中暴露。這種情況下,你需要一個(gè)低負(fù)荷的工具來(lái)監(jiān)聽(tīng)和尋找內(nèi)存泄漏。同時(shí),你還需要把這個(gè)工具同你的系統(tǒng)聯(lián)系起來(lái),而不需要重新啟動(dòng)他或者機(jī)械化你的代碼。也許更重要的是,當(dāng)你做分析的時(shí)候,你需要能夠同工具分離而使得系統(tǒng)不會(huì)受到干擾。
一個(gè)OutOfMemoryError常常是內(nèi)存泄漏的一個(gè)標(biāo)志,有可能應(yīng)用程序的確用了太多的內(nèi)存;這個(gè)時(shí)候,你既不能增加JVM的堆的數(shù)量,也不能改變你的程序而使得他減少內(nèi)存使用。但是,在大多數(shù)情況下,一個(gè)OutOfMemoryError是內(nèi)存泄漏的標(biāo)志。一個(gè)解決辦法就是繼續(xù)監(jiān)聽(tīng)GC的活動(dòng),看看隨時(shí)間的流逝,內(nèi)存使用量是否會(huì)增加,如果有,程序中一定存在內(nèi)存泄漏。
3、發(fā)現(xiàn)內(nèi)存泄漏
1. jstat -gc pid
可以顯示gc的信息,查看gc的次數(shù),及時(shí)間。
其中最后五項(xiàng),分別是young gc的次數(shù),young gc的時(shí)間,full gc的次數(shù),full gc的時(shí)間,gc的總時(shí)間。
2.jstat -gccapacity pid
可以顯示,VM內(nèi)存中三代(young,old,perm)對(duì)象的使用和占用大小,
如:PGCMN顯示的是最小perm的內(nèi)存使用量,PGCMX顯示的是perm的內(nèi)存最大使用量,
PGC是當(dāng)前新生成的perm內(nèi)存占用量,PC是但前perm內(nèi)存占用量。
其他的可以根據(jù)這個(gè)類推,OC是old內(nèi)純的占用量。
3.jstat -gcutil pid
統(tǒng)計(jì)gc信息統(tǒng)計(jì)。
4.jstat -gcnew pid
年輕代對(duì)象的信息。
5.jstat -gcnewcapacity pid
年輕代對(duì)象的信息及其占用量。
6.jstat -gcold pid
old代對(duì)象的信息。
7.stat -gcoldcapacity pid
old代對(duì)象的信息及其占用量。
8.jstat -gcpermcapacity pid
perm對(duì)象的信息及其占用量。
9.jstat -class pid
顯示加載class的數(shù)量,及所占空間等信息。
10.jstat -compiler pid
顯示VM實(shí)時(shí)編譯的數(shù)量等信息。
11.stat -printcompilation pid
當(dāng)前VM執(zhí)行的信息。
一些術(shù)語(yǔ)的中文解釋:
S0C:年輕代中第一個(gè)survivor(幸存區(qū))的容量(字節(jié))
S1C:年輕代中第二個(gè)survivor(幸存區(qū))的容量(字節(jié))
S0U:年輕代中第一個(gè)survivor(幸存區(qū))目前已使用空間(字節(jié))
S1U:年輕代中第二個(gè)survivor(幸存區(qū))目前已使用空間(字節(jié))
EC:年輕代中Eden(伊甸園)的容量(字節(jié))
EU:年輕代中Eden(伊甸園)目前已使用空間(字節(jié))
OC:Old代的容量(字節(jié))
OU:Old代目前已使用空間(字節(jié))
PC:Perm(持久代)的容量(字節(jié))
PU:Perm(持久代)目前已使用空間(字節(jié))
YGC:從應(yīng)用程序啟動(dòng)到采樣時(shí)年輕代中g(shù)c次數(shù)
YGCT:從應(yīng)用程序啟動(dòng)到采樣時(shí)年輕代中g(shù)c所用時(shí)間(s)
FGC:從應(yīng)用程序啟動(dòng)到采樣時(shí)old代(全gc)gc次數(shù)
FGCT:從應(yīng)用程序啟動(dòng)到采樣時(shí)old代(全gc)gc所用時(shí)間(s)
GCT:從應(yīng)用程序啟動(dòng)到采樣時(shí)gc用的總時(shí)間(s)
NGCMN:年輕代(young)中初始化(最小)的大小(字節(jié))
NGCMX:年輕代(young)的最大容量(字節(jié))
NGC:年輕代(young)中當(dāng)前的容量(字節(jié))
OGCMN:old代中初始化(最小)的大小(字節(jié))
OGCMX:old代的最大容量(字節(jié))
OGC:old代當(dāng)前新生成的容量(字節(jié))
PGCMN:perm代中初始化(最小)的大小(字節(jié))
PGCMX:perm代的最大容量(字節(jié))
PGC:perm代當(dāng)前新生成的容量(字節(jié))
S0:年輕代中第一個(gè)survivor(幸存區(qū))已使用的占當(dāng)前容量百分比
S1:年輕代中第二個(gè)survivor(幸存區(qū))已使用的占當(dāng)前容量百分比
E:年輕代中Eden(伊甸園)已使用的占當(dāng)前容量百分比
O:old代已使用的占當(dāng)前容量百分比
P:perm代已使用的占當(dāng)前容量百分比
S0CMX:年輕代中第一個(gè)survivor(幸存區(qū))的最大容量(字節(jié))
S1CMX:年輕代中第二個(gè)survivor(幸存區(qū))的最大容量(字節(jié))
ECMX:年輕代中Eden(伊甸園)的最大容量(字節(jié))
DSS:當(dāng)前需要survivor(幸存區(qū))的容量(字節(jié))(Eden區(qū)已滿)
TT:持有次數(shù)限制
MTT:最大持有次數(shù)限制
如果定位內(nèi)存泄漏問(wèn)題我一般使用如下命令:
Jstat -gcutil15469 2500 70
[root@ssss logs]# jstat -gcutil 15469 1000 300
S0 S1 E O P YGC YGCT FGC FGCT GCT
0.00 1.46 26.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 46.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 47.04 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 65.19 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 67.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 87.54 4.61 30.14 35 0.872 0 0.000 0.872
0.00 1.46 88.03 4.61 30.14 35 0.872 0 0.000 0.872
1.48 0.00 5.56 4.62 30.14 36 0.874 0 0.000 0.874
1000 代表多久間隔顯示一次,
100 代表顯示一次。
S0 — Heap上的 Survivor space 0 區(qū)已使用空間的百分比
S1 — Heap上的 Survivor space 1 區(qū)已使用空間的百分比
E — Heap上的 Eden space 區(qū)已使用空間的百分比
O — Heap上的 Old space 區(qū)已使用空間的百分比
P — Perm space 區(qū)已使用空間的百分比
YGC — 從應(yīng)用程序啟動(dòng)到采樣時(shí)發(fā)生 Young GC 的次數(shù)
YGCT– 從應(yīng)用程序啟動(dòng)到采樣時(shí) Young GC 所用的時(shí)間(單位秒)
FGC — 從應(yīng)用程序啟動(dòng)到采樣時(shí)發(fā)生 Full GC 的次數(shù)
FGCT– 從應(yīng)用程序啟動(dòng)到采樣時(shí) Full GC 所用的時(shí)間(單位秒)
GCT — 從應(yīng)用程序啟動(dòng)到采樣時(shí)用于垃圾回收的總時(shí)間(單位秒)
如果有大量的FGC就要查詢是否有內(nèi)存泄漏的問(wèn)題了,圖中的FGC數(shù)量就比較大,并且執(zhí)行時(shí)間較長(zhǎng),這樣就會(huì)導(dǎo)致系統(tǒng)的響應(yīng)時(shí)間較長(zhǎng),如果對(duì)jvm的內(nèi)存設(shè)置較大,那么執(zhí)行一次FGC的時(shí)間可能會(huì)更長(zhǎng)。
如果為了更好的證明FGC對(duì)服務(wù)器性能的影響,我們可以使用java visualVM來(lái)查看一下:
從上圖可以發(fā)現(xiàn)執(zhí)行FGC的情況,下午3:10分之前是沒(méi)有FGC的,之后出現(xiàn)大量的FGC。
上圖是jvm堆內(nèi)存的使用情況,下午3:10分之前的內(nèi)存回收還是比較合理,但是之后大量?jī)?nèi)存無(wú)法回收,最后導(dǎo)致內(nèi)存越來(lái)越少,導(dǎo)致大量的full gc。
下面我們?cè)诳纯创罅縡ull GC對(duì)服務(wù)器性能的影響,下面是我用loadrunner對(duì)我們項(xiàng)目進(jìn)行壓力測(cè)試相應(yīng)時(shí)間的截圖:
從圖中可以發(fā)現(xiàn)有,在進(jìn)行full GC后系統(tǒng)的相應(yīng)時(shí)間有了明顯的增加,點(diǎn)擊率和吞吐量也有了明顯的下降。所以java內(nèi)存泄漏對(duì)系統(tǒng)性能的影響是不可忽視的。
3、定位內(nèi)存泄漏
當(dāng)然通過(guò)上面幾種方法我們可以發(fā)現(xiàn)java的內(nèi)存泄漏問(wèn)題,但是作為一名合格的高級(jí)工程師,肯定不甘心就把這樣的結(jié)論交給開(kāi)發(fā),當(dāng)然這也的結(jié)論交給開(kāi)發(fā),開(kāi)發(fā)也很難定位問(wèn)題,為了更好的提供自己在公司的地位,我們必須給開(kāi)發(fā)工程師提供更深入的測(cè)試結(jié)論,下面就來(lái)認(rèn)識(shí)一下MemoryAnalyzer.exe。java內(nèi)存泄漏檢查工具利器。
首先我們必須對(duì)jvm的堆內(nèi)存進(jìn)行dump,只有拿到這個(gè)文件我們才能分析出jvm堆內(nèi)存中到底存了些什么內(nèi)容,到底在做什么?
MemoryAnalyzer的用戶我在這里就不一一說(shuō)明了,我的博客里也有說(shuō)明,下面就展示我測(cè)試的成功圖:
其中深藍(lán)色的部分就為內(nèi)存泄漏的部分,java的堆內(nèi)存一共只有481.5M而內(nèi)存泄漏的部分獨(dú)自占有了336.2M所以本次的內(nèi)存泄漏很明顯,那么我就來(lái)看看那個(gè)方法導(dǎo)致的內(nèi)存泄漏:
從上圖我們可以發(fā)現(xiàn)紅線圈著的方法占用了堆內(nèi)存的67.75%,如果能把這個(gè)測(cè)試結(jié)果交給開(kāi)發(fā),開(kāi)發(fā)是不是應(yīng)該很好定位呢。所以作為一名高級(jí)測(cè)試工程師,我們需要學(xué)習(xí)的東西太多。
雖然不確定一定是內(nèi)存泄漏,但是可以準(zhǔn)確的告訴開(kāi)發(fā)問(wèn)題出現(xiàn)的原因,有一定的說(shuō)服力。
先用下面的命令監(jiān)視進(jìn)程:
while ( sleep 1 ) ; do ps -p $PID -o %cpu,%mem,rss ; done
如果看到內(nèi)存上升很快,可能是因?yàn)樘摂M機(jī)設(shè)置。如果沒(méi)有明確指定JVM的內(nèi)存設(shè)置,它將設(shè)置默認(rèn)值給他們。要獲得默認(rèn)值,使用以下命令:
java -XX:+PrintFlagsFinal -version | grep -i HeapSize
如果這些都不符合希望的,那么就需要指定JVM的內(nèi)存設(shè)置。可以用下面的命令設(shè)置最小和最大堆大?。?/p>
java -Xms128m -Xmx256m
盡管有了合理的內(nèi)存設(shè)置,也可以監(jiān)控進(jìn)程,但你仍然可能看到內(nèi)存隨時(shí)間增加。為了進(jìn)一步探究原因,可以使用下面的命令查看對(duì)象實(shí)例的直方圖:
jmap -histo $PID
如果仍然沒(méi)有足夠的信息,那么可以用以下命令進(jìn)行堆轉(zhuǎn)儲(chǔ):
jmap -dump:format=b,file=/tmp/dump1.hprof $PID
通常,會(huì)用兩個(gè)堆轉(zhuǎn)儲(chǔ),然后使用下面的jhat命令比較它們:
jhat -baseline /tmp/dump1.hprof /tmp/dump2.hprof
這個(gè)命令會(huì)啟動(dòng)一個(gè)HTTP服務(wù)器,可以用它來(lái)探索這兩個(gè)堆轉(zhuǎn)儲(chǔ)之間的差值。在默認(rèn)情況下,HTTP服務(wù)器啟動(dòng)7000端口,可以在瀏覽器中訪問(wèn)該端口。
如果有防火墻,可以通過(guò)SSH訪問(wèn),那么可以通過(guò)如下命令連接該端口:
ssh -L 7000:localhost:7000 $HOST
Java內(nèi)存泄露\x0d\x0a一般來(lái)說(shuō)內(nèi)存泄漏有兩種情況。一種情況如在C/C++語(yǔ)言中的,在堆中的分配的內(nèi)存,在沒(méi)有將其釋放掉的時(shí)候,就將所有能訪問(wèn)這塊內(nèi)存的方式都刪掉(如指針重新賦值);另一種情況則是在內(nèi)存對(duì)象明明已經(jīng)不需要的時(shí)候,還仍然保留著這塊內(nèi)存和它的訪問(wèn)方式(引用)。第一種情況,在Java中已經(jīng)由于垃圾回收機(jī)制的引入,得到了很好的解決。所以,Java中的內(nèi)存泄漏,主要指的是第二種情況。\x0d\x0a可能光說(shuō)概念太抽象了,大家可以看一下這樣的例子:\x0d\x0a1Vectorv=newVector(10);\x0d\x0a2for(inti=1;i
回答于?2022-12-14
一般來(lái)說(shuō)內(nèi)存泄漏有兩種情況。一種情況,在堆中的分配的內(nèi)存,在沒(méi)有將其釋放掉的時(shí)候,就將所有能訪問(wèn)這塊內(nèi)存的方式都刪掉(如指針重新賦值);另一種情況則是在內(nèi)存對(duì)象明明已經(jīng)不需要的時(shí)候,還仍然保留著這塊內(nèi)存和它的訪問(wèn)方式(引用)。第一種情況,在Java中已經(jīng)由于垃圾回收機(jī)制的引入,得到了很好的解決。所以,Java中的內(nèi)存泄漏,主要指的是第二種情況。
可能光說(shuō)概念太抽象了,大家可以看一下這樣的例子:
1 Vector v=new Vector(10);
2 for (int i=1;i100; i++){
3 Object o=new Object();
4 v.add(o);
5 o=null;
6 }
在這個(gè)例子中,代碼棧中存在Vector對(duì)象的引用v和Object對(duì)象的引用o。在For循環(huán)中,我們不斷的生成新的對(duì)象,然后將其添加到Vector對(duì)象中,之后將o引用置空。問(wèn)題是當(dāng)o引用被置空后,如果發(fā)生GC,我們創(chuàng)建的Object對(duì)象是否能夠被GC回收呢?答案是否定的。因?yàn)?,GC在跟蹤代碼棧中的引用時(shí),會(huì)發(fā)現(xiàn)v引用,而繼續(xù)往下跟蹤,就會(huì)發(fā)現(xiàn)v引用指向的內(nèi)存空間中又存在指向Object對(duì)象的引用。也就是說(shuō)盡管o引用已經(jīng)被置空,但是Object對(duì)象仍然存在其他的引用,是可以被訪問(wèn)到的,所以GC無(wú)法將其釋放掉。如果在此循環(huán)之后,Object對(duì)象對(duì)程序已經(jīng)沒(méi)有任何作用,那么我們就認(rèn)為此Java程序發(fā)生了內(nèi)存泄漏。
盡管對(duì)于C/C++中的內(nèi)存泄露情況來(lái)說(shuō),Java內(nèi)存泄露導(dǎo)致的破壞性小,除了少數(shù)情況會(huì)出現(xiàn)程序崩潰的情況外,大多數(shù)情況下程序仍然能正常運(yùn)行。但是,在移動(dòng)設(shè)備對(duì)于內(nèi)存和CPU都有較嚴(yán)格的限制的情況下,Java的內(nèi)存溢出會(huì)導(dǎo)致程序效率低下、占用大量不需要的內(nèi)存等問(wèn)題。這將導(dǎo)致整個(gè)機(jī)器性能變差,嚴(yán)重的也會(huì)引起拋出OutOfMemoryError,導(dǎo)致程序崩潰。
1、首先我把JVM內(nèi)存調(diào)小,便于在最短的時(shí)間內(nèi)發(fā)現(xiàn)問(wèn)題,利用jstat觀察JVM內(nèi)存回收的情況和使用情況,期間發(fā)現(xiàn)舊生代內(nèi)存的申請(qǐng)?jiān)谝恢边M(jìn)行,但是GC基本回收不回來(lái)內(nèi)存,所以很堅(jiān)信如果JVM沒(méi)有BUG的情況下,肯定是存在內(nèi)存泄漏的地方,應(yīng)該是代碼有問(wèn)題。但是如何在不翻遍整個(gè)代碼的情況下,定位問(wèn)題呢?
2、我查閱幾個(gè)JVM內(nèi)存導(dǎo)出工具,并利用JMAP把JVM全部導(dǎo)出來(lái),但是發(fā)現(xiàn)悲催的溫斗士下,這些工具根本打不開(kāi)一個(gè)G左右的導(dǎo)出文件,直接報(bào)亂七八糟的錯(cuò)誤,可能也是我的PC硬件配置不高吧,無(wú)奈之下只好找了一臺(tái)Linux服務(wù)器,在其上裝了MAT工具,然后把JVM導(dǎo)出文件放到這臺(tái)服務(wù)器上進(jìn)行分析,結(jié)果迅速定位到了存在問(wèn)題的代碼
JMAP導(dǎo)出JVM命令格式如下:
jmap -dump:live,format=b,file=heap.bin pid
MAT使用比較簡(jiǎn)單,不再介紹,只要選擇打開(kāi)導(dǎo)出的文件即可對(duì)哪些對(duì)象、類等對(duì)內(nèi)存的使用情況一目了然,從而幫助把有可能出問(wèn)題的代碼范圍盡量縮小,不用像大海撈針一樣采用人海戰(zhàn)術(shù)逐行代碼排查。