內(nèi)存泄漏與內(nèi)存溢出

最近在畫像項(xiàng)目里需要將某個(gè)標(biāo)簽進(jìn)行篩選谱净,但是有個(gè)操作是直接把數(shù)據(jù)從庫里面拿出千萬級(jí)別的標(biāo)簽放進(jìn)內(nèi)存中進(jìn)行遍歷操作摩疑,然后程序直接掛了匕累。于是很好奇研究了下平時(shí)代碼中會(huì)不會(huì)出現(xiàn)內(nèi)存溢出的問題渡八。

內(nèi)存泄漏與內(nèi)存溢出的概念

在long long ago,相傳編程語言存在鄙視鏈霞怀,C鄙視C++惫东,C++鄙視Java,Java鄙視c#,結(jié)果最后PHP莫名其妙成了世界上最好的語言(真香警告)毙石。在大學(xué)的時(shí)候廉沮,一開始學(xué)C++程序總是容易崩潰,因?yàn)樵贑++中徐矩,對(duì)象占用的內(nèi)存空間都必須有我們自己來顯式回收滞时,如果處理完之后忘記了回收內(nèi)存,那它們所占用的內(nèi)存空間就會(huì)產(chǎn)生內(nèi)存泄漏滤灯,很容易造成程序的崩潰坪稽。對(duì)于Java來說,JVM垃圾回收機(jī)制會(huì)自動(dòng)回收無用對(duì)象所占用的內(nèi)存空間而不需要我們手動(dòng)去釋放力喷。理論上在Java中是不存在內(nèi)存泄漏與內(nèi)存溢出的場(chǎng)景刽漂,但實(shí)際上演训,如果使用不當(dāng)弟孟,Java程序也會(huì)存在內(nèi)存泄漏的問題。

程序在運(yùn)行過程中會(huì)不斷地分配內(nèi)存样悟,那些不再使用的內(nèi)存空間應(yīng)該及時(shí)回收拂募,從而保證系統(tǒng)可以再次使用這些內(nèi)存庭猩,如果存在無用的內(nèi)存沒有被回收,那么這內(nèi)存空間就存在內(nèi)存泄漏陈症。一般來說不可達(dá)的對(duì)象都是由JVM垃圾回收機(jī)制去回收蔼水,但是有些對(duì)象處于可達(dá)狀態(tài),程序卻無法再去訪問录肯,那么這些對(duì)象所占用的內(nèi)存空間就無法進(jìn)行回收趴腋,所在空間就存在內(nèi)存泄漏。簡(jiǎn)而言之也就是被分配的對(duì)象可達(dá)卻無用论咏。

  • 在垃圾回收機(jī)制中优炬,通過一系列稱為“GC Roots”的對(duì)象作為起點(diǎn),從這些節(jié)點(diǎn)開始向下搜索厅贪,搜索所走過的路徑稱為應(yīng)用鏈蠢护,一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連,用圖論的話來說就是從GC Roots到這個(gè)對(duì)象不可達(dá)养涮,對(duì)于GC Root無法達(dá)到的對(duì)象便是垃圾對(duì)象葵硕,隨時(shí)可被GC回收。相反贯吓,可達(dá)對(duì)象便是存活對(duì)象不會(huì)被GC回收懈凹。

簡(jiǎn)單來說當(dāng)我們服務(wù)器內(nèi)存不足以給程序分配內(nèi)存空間時(shí),此時(shí)程序就會(huì)報(bào)內(nèi)存溢出錯(cuò)誤宣决。也就是內(nèi)存泄漏是誘因蘸劈,在多次積累之后會(huì)導(dǎo)致內(nèi)存溢出。

分析Java運(yùn)行時(shí)內(nèi)存情況

了解了內(nèi)存泄漏的概念之后尊沸,為了更準(zhǔn)確定位導(dǎo)致內(nèi)存泄漏的地方威沫,有必要先分析下Java在運(yùn)行時(shí)內(nèi)存的使用情況。


Java運(yùn)行時(shí)數(shù)據(jù)區(qū)
  • 方法區(qū):在Java虛擬機(jī)中洼专,方法區(qū)是可供各條線程共享的運(yùn)行時(shí)的內(nèi)存區(qū)域棒掠,也就是說所有線程都可以共享。它存儲(chǔ)了每一個(gè)類的結(jié)構(gòu)信息屁商,例如烟很,運(yùn)行時(shí)常量池、字段和方法數(shù)據(jù)蜡镶、構(gòu)造函數(shù)和普通方法的字節(jié)碼內(nèi)容雾袱,還包括一些在類、實(shí)例官还、接口初始化時(shí)用到的特殊方法芹橡。方法區(qū)邏輯上屬于堆的一部分,但是為了與堆進(jìn)行區(qū)分望伦,通常又叫“非堆”林说。
    在JDK1.7及以前煎殷,HotSpot里面對(duì)方法區(qū)的實(shí)現(xiàn)稱為永久代(PermGen space),為何會(huì)稱為永久代呢腿箩?主要是在之前認(rèn)為方法區(qū)存放的數(shù)據(jù)例如常量池豪直,靜態(tài)變量和 類是靜態(tài)的,幾乎不會(huì)被GC回收可以長(zhǎng)時(shí)間存放在該區(qū)域中珠移。這種情況下方法區(qū)是很容易發(fā)生內(nèi)存溢出的弓乙,例如用cblib的代態(tài)代理通過反射生成類的時(shí)候。
    因此钧惧,在JDK1.8之后唆貌,HotSpot 已經(jīng)沒有 “PermGen space”這個(gè)區(qū)間了,而是用元空間(Metaspace)來代替了垢乙。本質(zhì)上永久代和元空間都是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn)锨咙,不過雙方有很大的區(qū)別就是方法區(qū)使用的是虛擬機(jī)的內(nèi)存,元空間使用的是本地內(nèi)存追逮。也就是你的服務(wù)器有多大內(nèi)存酪刀,元空間就可以設(shè)置多大的內(nèi)存。默認(rèn)情況下钮孵,元空間的大小僅受本地內(nèi)存限制骂倘,但可以通過以下參數(shù)來指定元空間的大小。所以使用本地內(nèi)存可以盡可能的避免內(nèi)存溢出巴席。

  • 堆:堆內(nèi)存也是所有線程共享的历涝,在虛擬機(jī)啟動(dòng)的時(shí)候就已經(jīng)創(chuàng)建好了。大部分對(duì)象以及數(shù)組都是在堆上分配的漾唉,為何不是所有對(duì)象都有堆分配呢荧库?在使用Java的時(shí)候,一般會(huì)認(rèn)為Java 創(chuàng)建的對(duì)象都是被分配到堆內(nèi)存上的赵刑,實(shí)際上Java中的逃逸分析可以導(dǎo)致對(duì)象不再堆中分配分衫。在《深入了解Java虛擬機(jī)》一書中有章節(jié)描述Java逃逸分析技術(shù)。這堆空間可由 GC 進(jìn)行回收般此,當(dāng)無法回收內(nèi)存空間的時(shí)候會(huì)拋出OutOfMemoryError蚪战。


    堆結(jié)構(gòu)
  • 虛擬機(jī)棧:每一條 Java 虛擬機(jī)線程都有自己私有的 Java 虛擬機(jī)棧,這個(gè)棧與線程同時(shí)創(chuàng)建铐懊,棧里面存著的是一種叫“棧幀”的東西邀桑。每個(gè)方法會(huì)創(chuàng)建一個(gè)棧幀,棧幀中存放了局部變量表(基本數(shù)據(jù)類型和對(duì)象引用)等信息科乎。棧幀隨著方法調(diào)用而創(chuàng)建壁畸,隨著方法結(jié)束而銷毀——無論方法是正常完成還是異常完成(拋出了在方法內(nèi)未被捕獲的異常)都算作方法結(jié)束。椣参梗可以被實(shí)現(xiàn)成固定大小瓤摧,也可以根據(jù)計(jì)算動(dòng)態(tài)來擴(kuò)展和收縮。如果線程請(qǐng)求分配的棧容量超過 Java 虛擬機(jī)棧允許的最大容量玉吁,Java 虛擬機(jī)將會(huì)拋出一個(gè)StackOverflowError 異常照弥。

內(nèi)存泄漏與內(nèi)存溢出場(chǎng)景

  • 內(nèi)存中加載的數(shù)據(jù)量過于龐大,如一次從數(shù)據(jù)庫取出過多數(shù)據(jù)进副,這就會(huì)造成內(nèi)存溢出問題这揣。
  • 長(zhǎng)生命周期對(duì)象持有短生命周期對(duì)象的引用。造成內(nèi)存泄漏


    demo_1

    上述demo中影斑,由于obj對(duì)象一直被靜態(tài)map引用给赞,對(duì)于GC來說,該對(duì)象可達(dá)但無用矫户,這就存在內(nèi)存泄漏片迅。

  • 代碼中存在循環(huán)產(chǎn)生過多重復(fù)的對(duì)象實(shí)體。造成堆內(nèi)存溢出(OutOfMemoryError: Java heap space)


    demo_2

    result_1

    上述demo中皆辽,造成了堆內(nèi)存溢出柑蛇。在開始創(chuàng)建對(duì)象時(shí)候,對(duì)象只會(huì)存在于Eden區(qū)和Survivor區(qū)驱闷,當(dāng)Eden區(qū)不夠空間的時(shí)候就會(huì)觸發(fā)Minor GC將對(duì)象復(fù)制到Survior區(qū)耻台,當(dāng)Survior區(qū)不夠的時(shí)候就會(huì)復(fù)制到老年代。當(dāng)老年代空間不夠就會(huì)觸發(fā)Full GC對(duì)整個(gè)堆進(jìn)行Full GC空另,但是這次Full GC并不會(huì)回收對(duì)象的軟連接盆耽。如果還不夠空間就會(huì)再次進(jìn)行Full GC,這時(shí)會(huì)對(duì)軟連接進(jìn)行回收扼菠。所以當(dāng)你的項(xiàng)目頻繁進(jìn)行Full GC的時(shí)候摄杂,很有可能存在內(nèi)存泄漏。

  • 當(dāng)加載到方法區(qū)的class太多的時(shí)候就可能會(huì)報(bào)出permgen溢出的錯(cuò)誤循榆。(OutOfMemoryError: PermGen space)特別是用cglib動(dòng)態(tài)生成類 的時(shí)候匙姜。
  • 代碼中存在死循環(huán)或遞歸調(diào)用,會(huì)產(chǎn)生棧溢出冯痢。(StackOverflowError)


    image.png
  • 對(duì)文件流氮昧,數(shù)據(jù)庫連接流等操作沒有關(guān)閉流(emmmmm 靜態(tài)工具檢查最多這個(gè)問題。浦楣。)

排查方式

  • 生成dump文件袖肥,借助工具例如MAT進(jìn)行分析
  • 利用jps命令查找Java進(jìn)程pid
  • 使用命令top -p <pid> ,顯示Java進(jìn)程的內(nèi)存情況
  • jstat -gccause pid 10000 每各10秒輸出結(jié)果
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末振劳,一起剝皮案震驚了整個(gè)濱河市椎组,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌历恐,老刑警劉巖寸癌,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件专筷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蒸苇,警方通過查閱死者的電腦和手機(jī)磷蛹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溪烤,“玉大人味咳,你說我怎么就攤上這事∶枢郑” “怎么了槽驶?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鸳兽。 經(jīng)常有香客問我掂铐,道長(zhǎng),這世上最難降的妖魔是什么揍异? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任堡纬,我火速辦了婚禮,結(jié)果婚禮上蒿秦,老公的妹妹穿的比我還像新娘烤镐。我一直安慰自己,他們只是感情好棍鳖,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布炮叶。 她就那樣靜靜地躺著,像睡著了一般渡处。 火紅的嫁衣襯著肌膚如雪镜悉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天医瘫,我揣著相機(jī)與錄音侣肄,去河邊找鬼。 笑死醇份,一個(gè)胖子當(dāng)著我的面吹牛稼锅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播僚纷,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼矩距,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了怖竭?” 一聲冷哼從身側(cè)響起锥债,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后哮肚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體登夫,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年允趟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恼策。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡拼窥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蹋凝,到底是詐尸還是另有隱情鲁纠,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布鳍寂,位于F島的核電站改含,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏迄汛。R本人自食惡果不足惜捍壤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鞍爱。 院中可真熱鬧鹃觉,春花似錦、人聲如沸睹逃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沉填。三九已至疗隶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間翼闹,已是汗流浹背斑鼻。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猎荠,地道東北人坚弱。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像关摇,于是被迫代替她去往敵國(guó)和親史汗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容