內(nèi)存溢出 out of memory,是指程序在申請內(nèi)存時,沒有足夠的內(nèi)存空間供其使用页慷,出現(xiàn)out of memory;
內(nèi)存泄露 memory leak胁附,是指程序在申請內(nèi)存后酒繁,無法釋放已申請的內(nèi)存空間,一次內(nèi)存泄露危害可以忽略控妻,但內(nèi)存泄露堆積后果很嚴重州袒,無論多少內(nèi)存,遲早會被占光。
memory leak會最終會導致out of memory弓候!
1.虛擬機棧:每個線程有一個私有的棧稳析,隨著線程的創(chuàng)建而創(chuàng)建。棧里面存著的是一種叫“棧幀”的東西弓叛,每個方法會創(chuàng)建一個棧幀,棧幀中存放了局部變量表(基本數(shù)據(jù)類型和對象引用)诚纸、操作數(shù)棧撰筷、方法出口等信息。棧的大小可以固定也可以動態(tài)擴展畦徘。當棧調(diào)用深度大于JVM所允許的范圍毕籽,會拋出StackOverflowError的錯誤,不過這個深度范圍不是一個恒定的值井辆。
2.本地方法棧:這部分主要與虛擬機用到的 Native 方法相關(guān)关筒,一般情況下, Java 應用程序員并不需要關(guān)心這部分的內(nèi)容杯缺。直接調(diào)用虛擬機的C方法蒸播,
3.PC 寄存器:也叫程序計數(shù)器。JVM支持多個線程同時運行萍肆,每個線程都有自己的程序計數(shù)器袍榆。倘若當前執(zhí)行的是 JVM 的方法,則該寄存器中保存當前執(zhí)行指令的地址塘揣;倘若執(zhí)行的是native 方法包雀,則PC寄存器中為空。
4.堆內(nèi)存是 JVM 所有線程共享的部分亲铡,在虛擬機啟動的時候就已經(jīng)創(chuàng)建才写。所有的對象和數(shù)組都在堆上進行分配葡兑。這部分空間可通過 GC 進行回收。當申請不到空間時會拋出 OutOfMemoryError赞草。存放對象實例讹堤。數(shù)組值 -Xms -Xmx 配置最大最小。當堆內(nèi)存小于配置的40%時jvm會增加到-xmx房资,當空余空間大于70%時降低到-xms大小最好設置成一樣防止頻繁調(diào)整
5.方法區(qū):也是所有線程共享蜕劝。主要用于存儲類的信息、常量池轰异、方法數(shù)據(jù)岖沛、方法代碼等。方法區(qū)邏輯上屬于堆的一部分搭独,但是為了與堆進行區(qū)分婴削,通常又叫“非堆”。 關(guān)于方法區(qū)內(nèi)存溢出的問題會在下文中詳細探討牙肝。存放要加載的類信息(名稱扒最,修飾符)。靜態(tài)變量须肆,final常量游桩,方法信息。方法區(qū)又叫做non-static非堆股缸,永久代或者metaspace衡楞。顯式的String常量放在常池,String對象放在堆中敦姻。通過Class.forName獲得的類信息都是從方法區(qū)讀取的也叫PermanetGeneration瘾境,1.7之后的字符串常量池已經(jīng)開始使用native memory 方法區(qū)的概念被淡化。
常量池:運行時常量池是方法區(qū)的一部分镰惦, Class文件中除了有類的版本迷守,字段,方法旺入,接口等描述信息外兑凿,還有一項信息是常量池,用于存放編譯期生成的各種字面量和符號引用茵瘾,這部分內(nèi)容將在類加載后存在運行時常量池中急膀。
默認最小16,最大64MB OutOfMemory
直接內(nèi)存龄捡,也叫堆外內(nèi)存卓嫂,不在堆的內(nèi)存大小控制內(nèi),通過Native函數(shù)庫直接分配堆外內(nèi)存聘殖,使用堆內(nèi)存中的一個DirectByteBuffer引用盡心操作晨雳,堆外也會出現(xiàn)內(nèi)存溢出的情況
堆內(nèi)存管理
指針碰撞:內(nèi)存分配算法行瑞,如果java堆中內(nèi)存是絕對規(guī)整的,會在已用內(nèi)存和空閑內(nèi)存中間放一個指針來標示餐禁,使用serial血久,parnew等帶compact過程的收集器使用指針碰撞。
空閑列表相反的如果不規(guī)整帮非,虛擬機會維護一個列表記錄哪些內(nèi)存塊是可用的氧吐,在分配的時候在列表中找到一塊足夠大的區(qū)域劃分給對象并做記錄,cms這種基于mark-sweep算法的收集器使用空閑列表
Thread Local Allocation Buffer: 并發(fā)情況下內(nèi)存分配也是有問題的末盔,假如對象A正在分配空間筑舅,指針還未修改,對象B又同事來分配內(nèi)存陨舱。TLAB空間為每個線程分配內(nèi)存翠拣,只有tlab用完并分配新的tlab時才會同步鎖定。
分代管理堆內(nèi)存:
新生代:大多數(shù)新創(chuàng)建的對象都放在新生代游盲,-Xmn指定新生代大小默認s0和s1各站1/10新生代大小误墓。-XX:ServivorRatio調(diào)整Eden和so,s1的大小
舊生代:存放經(jīng)過多次GC后仍然存活的對象。如緩存對象益缎,新建的對象也可能直接在舊生代創(chuàng)建
-XX:PretenureSizeThreshold=1024字節(jié)當對象超過這個時則在舊生代創(chuàng)建對象谜慌、
本地方法棧:支持native方法的執(zhí)行,存儲每個native方法調(diào)用狀態(tài)莺奔,和jvm方法棧時同一個欣范、
PC寄存器和JVM方法棧:
線程創(chuàng)建后都會創(chuàng)建程序計數(shù)器PC(program counter可能)和棧,PC存放了下一條要執(zhí)行的指令在方法內(nèi)的偏移量弊仪,棧中存放了棧幀(stackFRAME),棧幀主要分局部變量區(qū)和操作數(shù)棧杖刷。局部變量區(qū)存放局部變量和方法參數(shù)励饵,操作數(shù)棧存在方法執(zhí)行過程中產(chǎn)生的中間結(jié)果。
jvm方法棧線程私有內(nèi)存分配高效滑燃。通過-xss指定 stackOverFlow
內(nèi)存分配:
java對象占用的內(nèi)存都要在堆上進行分配役听。因此在堆配空間需要加鎖,效率緩慢表窘,當堆空間不足時會觸發(fā)GC典予。仍不足時報OutOfMemmory
jvm為每個新創(chuàng)建的線程在新生代的Eden里分配了一個占EdenSpace 1%大小(-XX:TLABWasteTargetPercent指定大欣盅稀)空間的TLAB(ThreadLocalAllocationBuffer)空間瘤袖。在TLAB上不需要加鎖效率高,如果TLAB空間用完則仍然在堆創(chuàng)建對象昂验,所有寫代碼時創(chuàng)建多個小對象分配內(nèi)存會更高效捂敌。-XX:PirintTLAB查看使用情況艾扮。
內(nèi)存回收:
根集合:
當前運行線程棧上引用的對象,常量和靜態(tài)變量占婉。傳到本地方法中還沒有被本地方法釋放的對象引用泡嘴。
1、虛擬機棧(棧幀中的本地變量)中引用的對象逆济。
2酌予、方法區(qū)中類靜態(tài)屬性引用的對象。
3奖慌、方法區(qū)中常量引用的對象抛虫。
4、本地方法棧中JNI(Native方法)引用的對象升薯。
收集器
復制:從根集合找到所有存活的對象莱褒,復制到一塊完全未使用的內(nèi)存上。高效但占內(nèi)存且對象要移動涎劈。
標記清除(Marking-Sweep) :對存活的對象進行標記再掃描清除未標記的對象广凸,對象不會移動。存活對象較多時較高效蛛枚。但是會產(chǎn)生內(nèi)存碎片
新生代的GC
采用Coping算法谅海。s0.s1負責coping切換。也叫ToSpace和FromSpace
串行g(shù)c:copying算法蹦浦。client級別cpu核數(shù)小于2內(nèi)存小于2g或32位系統(tǒng)
-XX:UseSerialGc強制指定
并行回收GC(ParallelScavage)
并行回收會根據(jù)新生代gc的頻率消耗時間動態(tài)調(diào)整Eden,s0,s1的大小-XX:UseAdaptiveSizePolicy來固定大小
PSgc不是根據(jù)-xxPretenureSizeThreshold來決定是否在舊生代創(chuàng)建對象的扭吁。而是當內(nèi)存需要的對象大于等于EdenSpace的一半時直接在舊生代創(chuàng)建、
-XX:UserParallelGC強制指定盲镶。并行的線程數(shù)為當cpu核數(shù)小于8.線程數(shù)等于核心數(shù)侥袜。大于時3+(核心數(shù)*5)/8 也可通過-XX:ParellalGCThreads=4指定
并行GC(ParNew)
并行在分配eden。s0,s1時和串行方式一樣
采用CMS算法
舊生代和持久帶的GC
串行:msc方式
1從跟集合對對象進行標識2.遍歷整個舊生代和持久代回收未標識的溉贿,3.向左滑動內(nèi)存
串行需要暫停應用枫吧。client級別
并行 采用Mark-compact將空間劃分為并行線程個數(shù)個區(qū)域(region) 通常舊生代左邊是活躍對象,gc時從左向右找到第一個需要壓縮的region 這個region的左側(cè)都不回收宇色。對右側(cè)按空間決定回收并壓縮到那一塊region
并發(fā)gc concurrent
fullGc
對整個堆進行g(shù)c包括新生代
舊生代空間不足時觸發(fā)PermanetGeneration滿 持久區(qū)(方法區(qū))
java1.8之后該區(qū)不存在變成MetaDataSpace 使用的是本地內(nèi)存-XX:MetaDataSpace指定大小九杂,GC -xx:MinMetadataSpaceFreeRatio -xx:MaxMetadataSpaceFreeRatio
jvm-xx:printGC
jconsole
jvsisualvm
jmap
jmap -dump:format=b,file=heap.bin <pid>
jstate
jstack