### 1內(nèi)存溢出(out of memory):
通俗理解就是內(nèi)存不夠赦颇,通常在運(yùn)行大型軟件或游戲時(shí)二鳄,軟件或游戲所需要的內(nèi)存遠(yuǎn)遠(yuǎn)超出了你主機(jī)內(nèi)安裝的內(nèi)存所承受大小,就叫內(nèi)存溢出媒怯。
此時(shí)軟件或游戲就運(yùn)行不了订讼,系統(tǒng)會(huì)提示內(nèi)存溢出,有時(shí)候會(huì)自動(dòng)關(guān)閉軟件扇苞,重啟電腦或者軟件后釋放掉一部分內(nèi)存又可以正常運(yùn)行該軟件或游戲一段時(shí)間欺殿。
#### 內(nèi)存溢出現(xiàn)象:
#####(第一種) Out Of MemoryError: PermGenspace
發(fā)生這種問題的原意是程序中使用了大量的jar或class,使java虛擬機(jī)裝載類的空間不夠鳖敷,與PermanentGeneration space有關(guān)脖苏。
解決這類問題有以下兩種辦法:
(1)增加java虛擬機(jī)中的XX:PermSize和XX:MaxPermSize參數(shù)的大小,其中XX:PermSize是初始永久保存區(qū)域大小定踱,XX:MaxPermSize是最大永久保存區(qū)域大小棍潘。如針對tomcat6.0,在catalina.sh 或catalina.bat文件中一系列環(huán)境變量名說明結(jié)束處(大約在70行左右)增加一行: JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m" 如果是windows服務(wù)器還可以在系統(tǒng)環(huán)境變量中設(shè)置崖媚。感覺用tomcat發(fā)布sprint+struts+hibernate架構(gòu)的程序時(shí)很容易發(fā)生這種內(nèi)存溢出錯(cuò)誤亦歉。使用上述方法,我成功解決了部署ssh項(xiàng)目的tomcat服務(wù)器經(jīng)常宕機(jī)的問題至扰。
![](https://upload-images.jianshu.io/upload_images/17812646-c9c56fa771e9972a?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
(2)清理應(yīng)用程序中web-inf/lib下的jar
如果tomcat部署了多個(gè)應(yīng)用鳍徽,很多應(yīng)用都使用了相同的jar,可以將共同的jar移到tomcat共同的lib下敢课,減少類的重復(fù)加載阶祭。這種方法是網(wǎng)上部分人推薦的,我沒試過直秆,但感覺減少不了太大的空間濒募,最靠譜的還是第一種方法。
#####(第二種)Out Of MemoryError:? Java heap space
發(fā)生這種問題的原因是java虛擬機(jī)創(chuàng)建的對象太多圾结,在進(jìn)行垃圾回收之間瑰剃,虛擬機(jī)分配的到堆內(nèi)存空間已經(jīng)用滿了,與Heap space有關(guān)筝野。解決這類問題有兩種思路:
(1)檢查程序晌姚,看是否有死循環(huán)或不必要地重復(fù)創(chuàng)建大量對象粤剧。
找到原因后,修改程序和算法挥唠。我以前寫一個(gè)使用K-Means文本聚類算法對幾萬條文本記錄(每條記錄的特征向量大約10來個(gè))進(jìn)行文本聚類時(shí)抵恋,由于程序細(xì)節(jié)上有問題,就導(dǎo)致了Java heap space的內(nèi)存溢出問題宝磨,后來通過修改程序得到了解決弧关。
(2)增加Java虛擬機(jī)中Xms(初始堆大小)和Xmx(最大堆大谢斤薄)參數(shù)的大小世囊。如:set JAVA_OPTS= -Xms256m -Xmx1024m
![](https://upload-images.jianshu.io/upload_images/17812646-5e5693afdbc80064?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#####(第三種)Out Of MemoryError:unable tocreate new native thread
在java應(yīng)用中,有時(shí)候會(huì)出現(xiàn)這樣的錯(cuò)誤:OutOfMemoryError: unable to create new native thread.這種怪事是因?yàn)镴VM已經(jīng)被系統(tǒng)分配了大量的內(nèi)存(比如1.5G)窿祥,并且它至少要占用可用內(nèi)存的一半株憾。
######出現(xiàn)的原因
引起內(nèi)存溢出的原因有很多種,常見的有以下幾種:
? 1壁肋、內(nèi)存中加載的數(shù)據(jù)量過于龐大号胚,如一次從數(shù)據(jù)庫取出過多數(shù)據(jù);
? 2、集合類中有對對象的引用浸遗,使用完后未清空猫胁,使得JVM不能回收;
? 3、代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復(fù)的對象實(shí)體;
? 4跛锌、使用的第三方軟件中的BUG;
? ????5弃秆、啟動(dòng)參數(shù)設(shè)定的過小;
###### 解決辦法:
第一步,就是修改JVM啟動(dòng)參數(shù)髓帽,直接增加內(nèi)存菠赚。
第二步,檢查錯(cuò)誤日志郑藏,查看"OutOfMemory"錯(cuò)誤前是否有其它異澈獠椋或錯(cuò)誤。
第三步必盖,安排有經(jīng)驗(yàn)的編程人員對代碼進(jìn)行走查和分析拌牲,找出可能發(fā)生內(nèi)存溢出的位置。
第四步歌粥,使用內(nèi)存查看工具動(dòng)態(tài)查看內(nèi)存使用情況塌忽。
## MAT檢查:
1、? 生成dump
2失驶、? 找出溢出源
■DominatorTree(支配樹):列出每個(gè)對象(Objectinstance)與其引用關(guān)系的樹狀結(jié)構(gòu)土居,還包含了占有多大內(nèi)存,所占百分比
■Histogram視圖(截圖里柱子那個(gè),邊上的是Dominator Tree ):列出每個(gè)class產(chǎn)生了多少個(gè)實(shí)例擦耀,以及占有多大內(nèi)存棉圈,所占百分比
■可以很容易找出站內(nèi)存最多的幾個(gè)類,根據(jù)Retained Heap排序埂奈,找出前幾個(gè)迄损。
■可以分不同的維度來查看類的Histogram視圖,Group by class账磺、Group by superclass、Group by class? loader痊远、Group by package
■只要有溢出垮抗,時(shí)間久了,溢出類的實(shí)例數(shù)量或者其占有的內(nèi)存會(huì)越來越多碧聪,排名也就越來越前冒版,通過多次對比不同時(shí)間點(diǎn)下的Histogram圖對比就能很容易把溢出類找出來。
3逞姿、定位溢出源
通過Pat通過Path to GC Roots或者M(jìn)erge Shortest Paths to GC Roots
###內(nèi)存泄漏
也稱作"存儲(chǔ)滲漏"辞嗡,用動(dòng)態(tài)存儲(chǔ)分配函數(shù)動(dòng)態(tài)開辟的空間,在使用完畢后未釋放滞造,結(jié)果導(dǎo)致一直占據(jù)該內(nèi)存單元续室。直到程序結(jié)束。
(其實(shí)說白了就是該內(nèi)存空間使用完畢之后未回收)即所謂內(nèi)存泄漏谒养。
內(nèi)存泄漏形象的比喻是"操作系統(tǒng)可提供給所有進(jìn)程的存儲(chǔ)空間正在被某個(gè)進(jìn)程榨干"挺狰,最終結(jié)果是程序運(yùn)行時(shí)間越長,占用存儲(chǔ)空間越來越多买窟,最終用盡全部存儲(chǔ)空間丰泊,整個(gè)系統(tǒng)崩潰。所以"內(nèi)存泄漏"是從操作系統(tǒng)的角度來看的始绍。這里的存儲(chǔ)空間并不是指物理內(nèi)存瞳购,而是指虛擬內(nèi)存大小,這個(gè)虛擬內(nèi)存大小取決于磁盤交換區(qū)設(shè)定的大小亏推。由程序申請的一塊內(nèi)存学赛,如果沒有任何一個(gè)指針指向它,那么這塊內(nèi)存就泄漏了径簿。
一般來說內(nèi)存泄漏有兩種情況罢屈。
#####(第一種情況) :
如在C/C++ 語言中的,在堆中的分配的內(nèi)存篇亭,在沒有將其釋放掉的時(shí)候缠捌,就將所有能訪問這塊內(nèi)存的方式都刪掉(如指針重新賦值);
#####(第二種情況):
則是在內(nèi)存對象明明已經(jīng)不需要的時(shí)候,還仍然保留著這塊內(nèi)存和它的訪問方式(引用)曼月。第一種情況谊却,在 Java 中已經(jīng)由于垃圾回收機(jī)制的引入,得到了很好的解決哑芹。所以炎辨, Java 中的內(nèi)存泄漏,主要指的是第二種情況聪姿。
內(nèi)存泄漏可以分為4類:
1. 常發(fā)性內(nèi)存泄漏碴萧。發(fā)生內(nèi)存泄漏的代碼會(huì)被多次執(zhí)行到,每次被執(zhí)行的時(shí)候都會(huì)導(dǎo)致一塊內(nèi)存泄漏末购。
2. 偶發(fā)性內(nèi)存泄漏破喻。發(fā)生內(nèi)存泄漏的代碼只有在某些特定環(huán)境或操作過程下才會(huì)發(fā)生。常發(fā)性和偶發(fā)性是相對的盟榴。對于特定的環(huán)境曹质,偶發(fā)性的也許就變成了常發(fā)性的葫隙。所以測試環(huán)境和測試方法對檢測內(nèi)存泄漏至關(guān)重要缸匪。
3. 一次性內(nèi)存泄漏井辜。發(fā)生內(nèi)存泄漏的代碼只會(huì)被執(zhí)行一次蔽莱,或者由于算法上的缺陷躬拢,導(dǎo)致總會(huì)有一塊僅且一塊內(nèi)存發(fā)生泄漏乒裆。比如痰催,在類的構(gòu)造函數(shù)中分配內(nèi)存屿岂,在析構(gòu)函數(shù)中卻沒有釋放該內(nèi)存礼饱,所以內(nèi)存泄漏只會(huì)發(fā)生一次坏为。
4. 隱式內(nèi)存泄漏。程序在運(yùn)行過程中不停的分配內(nèi)存镊绪,但是直到結(jié)束的時(shí)候才釋放內(nèi)存匀伏。嚴(yán)格的說這里并沒有發(fā)生內(nèi)存泄漏,因?yàn)樽罱K程序釋放了所有申請的內(nèi)存蝴韭。但是對于一個(gè)服務(wù)器程序够颠,需要運(yùn)行幾天,幾周甚至幾個(gè)月榄鉴,不及時(shí)釋放內(nèi)存也可能導(dǎo)致最終耗盡系統(tǒng)的所有內(nèi)存履磨。所以,我們稱這類內(nèi)存泄漏為隱式內(nèi)存泄漏庆尘。
從用戶使用程序的角度來看剃诅,內(nèi)存泄漏本身不會(huì)產(chǎn)生什么危害,作為一般的用戶驶忌,根本感覺不到內(nèi)存泄漏的存在矛辕。真正有危害的是內(nèi)存泄漏的堆積,這會(huì)最終消耗盡系統(tǒng)所有的內(nèi)存。從這個(gè)角度來說聊品,一次性內(nèi)存泄漏并沒有什么危害飞蹂,因?yàn)樗粫?huì)堆積,而隱式內(nèi)存泄漏危害性則非常大翻屈,因?yàn)檩^之于常發(fā)性和偶發(fā)性內(nèi)存泄漏它更難被檢測到
![](https://upload-images.jianshu.io/upload_images/17812646-8fdf0c9503e8d205?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
實(shí)例解釋:在這個(gè)例子中陈哑,代碼棧中存在Vector 對象的引用 v 和 Object 對象的引用 o 。在 For 循環(huán)中伸眶,我們不斷的生成新的對象惊窖,然后將其添加到 Vector 對象中,之后將 o 引用置空赚抡。問題是當(dāng) o 引用被置空后爬坑,如果發(fā)生 GC,我們創(chuàng)建的 Object 對象是否能夠被 GC 回收呢涂臣?答案是否定的。因?yàn)椋?GC 在跟蹤代碼棧中的引用時(shí)售担,會(huì)發(fā)現(xiàn) v 引用赁遗,而繼續(xù)往下跟蹤,就會(huì)發(fā)現(xiàn) v 引用指向的內(nèi)存空間中又存在指向 Object 對象的引用族铆。也就是說盡管 o 引用已經(jīng)被置空岩四,但是 Object 對象仍然存在其他的引用,是可以被訪問到的哥攘,所以 GC 無法將其釋放掉剖煌。如果在此循環(huán)之后, Object 對象對程序已經(jīng)沒有任何作用逝淹,那么我們就認(rèn)為此 Java 程序發(fā)生了內(nèi)存泄漏耕姊。