內(nèi)存結(jié)構(gòu)
java內(nèi)存結(jié)構(gòu)主要有三大區(qū)塊:棧內(nèi)存,堆內(nèi)存,堆外內(nèi)存(直接內(nèi)存)。其中:
1.棧內(nèi)存主要是存放線程的棧信息,每個線程有獨立的線程棧。
2.堆內(nèi)存有元信息區(qū)和對象區(qū),對象區(qū)則采用分代的策略,便于垃圾回收。
3.元信息區(qū)主要存放類信息,靜態(tài)變量信息。
4.此外還有堆外內(nèi)存,這部分內(nèi)存不受垃圾回收器的管理,其內(nèi)存管理自成另一套體系。
所有的內(nèi)存空間之和不能超過物理內(nèi)存,以及操作系統(tǒng)規(guī)定的內(nèi)存限制。
棧內(nèi)存
每個線程擁有獨立的線程棧。占中存放線程的調(diào)用信息,對象引用,以及基本類型的對象(大小確定的,如int ,short, long, byte, float, double,boolean,char)。
可以通過-Xss 指定每個線程棧的大小,默認(rèn)會使用jdk自帶的大小。
當(dāng)遞歸層數(shù)過多,或者線程的對象應(yīng)用過多,可能造成棧內(nèi)存不足的報錯:StackOverFlowError.
堆內(nèi)存
存放通過new 創(chuàng)建的對象實例。所有線程共享。運行時動態(tài)分配內(nèi)存。jvm會監(jiān)控對象實例的引用狀態(tài)等,并采用垃圾回收機(jī)制,盡可能優(yōu)化堆內(nèi)存的使用,釋放無用的對象實例。
垃圾回收
jvm通過垃圾回收來回收堆和方法區(qū)中的內(nèi)存,基本原理就是找到城中不再被使用的對象,然后回收掉這些對象占用的內(nèi)存。自動回收機(jī)制,以使得程序員擺脫內(nèi)存管理的繁雜事務(wù)。垃圾回收有可達(dá)性檢測,整理無用對象實例,以及分代/分區(qū)思想等內(nèi)容。
可達(dá)性檢測
即檢測對象是否不再被使用。主要有引用計數(shù)和跟蹤計數(shù)兩種。
引用計數(shù)器記錄對象是否被引用,當(dāng)計數(shù)器為0時,說明對象已經(jīng)不再被使用,可以回收。但是java中的引用關(guān)系非常復(fù)雜,存在循環(huán)引用的情況,因此引用計數(shù)器不適合。
跟蹤計數(shù):執(zhí)行的時候,從根集合開始掃描對象的引用關(guān)系,從節(jié)點向下搜索,如果一個對象到根集合沒有引用鏈,那么該對象是不可用的。根集合主要值:虛擬機(jī)棧中引用的對象、方法區(qū)中類靜態(tài)屬性引用的休想、方法區(qū)中敞亮引用的對象、本地方法棧中jni引用的對象。
整理策略
- 復(fù)制:從根集合中掃描出存活的對象,然后將存活的對象復(fù)制到一塊新的未使用空間。當(dāng)存活對象較少時,比較高效。
- 標(biāo)記清除:從根集合開始掃描,標(biāo)記存活的對象。再掃描整個空間未標(biāo)價的對象,然后回收,對象無需移動,高效但是有碎片。
- 標(biāo)記壓縮:標(biāo)記形式與“標(biāo)記清除”一樣,但是回收不存在的對象后,會做一次內(nèi)存整理。成本高但是無碎片。
分代、分區(qū)思想
分代和分區(qū)思想,將堆內(nèi)存區(qū)分管理,對不同的區(qū)塊進(jìn)行不同策略的回收機(jī)制,使得生命周期短的對象能夠快速被收回,生命周期長的對象能夠在適當(dāng)?shù)臅r候做統(tǒng)一回收。
年輕代,年老代的設(shè)置即時分代和分區(qū)思想的一種實現(xiàn)。
垃圾回收器
串行收集器(Serial):指的是使用一個單線程進(jìn)行垃圾收集,工作的時候會暫停其他工作線程。
并行收集器(Parallel):使用多線程完成垃圾收集,工作的時候會暫停其他工作線程。
CMS收集器:并發(fā)標(biāo)記清除,少量時間會暫停其他工作線程,其余時間與工作線程共同執(zhí)行。
G1收集器:垃圾優(yōu)先收集器,設(shè)計初衷是盡量縮短超大堆(大于4G)時產(chǎn)生的停頓,相對于cms來說內(nèi)存碎片大大降低,但是也會存在可能會造成停頓時間較長。jdk 1.7u4以上可以使用。不過目前線上還沒有使用這種策略。
堆外內(nèi)存
將對象分配到j(luò)ava虛擬機(jī)以外的內(nèi)存,這部分內(nèi)存直接受操作系統(tǒng)管理,可以減少受到垃圾回收對應(yīng)用程序造成的影響。使用java.nio.DirectByteBuffer對象進(jìn)行堆外內(nèi)存的管理和使用。
為什么要有堆外內(nèi)存
減少垃圾回收;加快復(fù)制到遠(yuǎn)程的速度:堆內(nèi)存在flush到遠(yuǎn)程時,會先復(fù)制到直接內(nèi)存,然后再發(fā)送,而堆外內(nèi)存少了復(fù)制這一步。
堆外內(nèi)存的問題
內(nèi)存難以控制
gc觸發(fā)時機(jī)的問題
何時觸發(fā)minor gc
- 當(dāng)eden區(qū)分配內(nèi)存,發(fā)現(xiàn)空間不足,jvm就會觸發(fā)minor gc
- 程序中調(diào)用Sysetm.gc()
何時觸發(fā)cms gc
- 老年代或者持久代已經(jīng)使用的空間達(dá)到設(shè)定的百分比時(CMSInitiatingOccupancyFraction這個設(shè)置old區(qū),perm區(qū)也可以設(shè)置);
- JVM自動觸發(fā)(JVM的動態(tài)策略,也就是悲觀策略)(基于之前GC的頻率以及舊生代的增長趨勢來評估決定什么時候開始執(zhí)行),如果不希望JVM自行決定,可以通過-XX:UseCMSInitiatingOccupancyOnly=true來禁用
何時觸發(fā)full gc
- 老年代空間不足:java.lang.OutOfMemoryError: java heap space
- Perm空間不足:java.lang.OutOfMemoryError: PermGen space
- cms gc 時出現(xiàn) promotion failed 和 concurrent mode failure(cms正在進(jìn)行,但是老年代內(nèi)存不足,需要盡快回收老年代的對象)
- 統(tǒng)計得到的minor gc 晉升到老年代的平均大小大于老年代的剩余空間
- 主動觸發(fā) full gc : 執(zhí)行 jmap -histo:live [pid]
關(guān)于內(nèi)存管理的jvm 參數(shù)設(shè)置
大小
- -Xms5g 最小堆內(nèi)存
- -Xmx5g 最大堆內(nèi)存,建議與最小堆內(nèi)存一致,避免jvm動態(tài)調(diào)整
- -Xmn2g 新生代內(nèi)存
- -XX:MetaspaceSize=512m jdk8 中元信息的空間大小
- -XX:MaxMetaspaceSize=512m jdk8 中元信息的最大空間大小
- -XX:MaxDirectMemorySize=1g 直接內(nèi)存的最大空間,超過空間,會執(zhí)行一次內(nèi)存回收。如果不設(shè)置,會和-Xmx基本差不多,導(dǎo)致直接內(nèi)存和堆內(nèi)存相加超過物理內(nèi)存,報內(nèi)存空間不足的錯誤。
- -XX:SurvivorRatio=8 eden和suvivor的比例。例如設(shè)置為8,則eden:s0:s1的比例為8:1:1
垃圾回收器策略
- -XX:+UseConcMarkSweepGC 使用cms(并發(fā)標(biāo)記消除)進(jìn)行老年代的回收,默認(rèn)新生代采用ParNew回收
- -XX:+UseParallelGC 使用并行
- -XX:+UseSerialGC 串行g(shù)c
其他
cms相關(guān)的參數(shù)
- -XX:CMSInitiatingOccupancyFraction=75 內(nèi)存使用率達(dá)到75%后,開始cms收集
- -XX:+UseCMSInitiatingOccupancyOnly 禁用jvm cms的悲觀策略,僅根據(jù)內(nèi)存使用率來觸發(fā)cms
- -XX:+UseCMSCompactAtFullCollection cms是消除策略,有內(nèi)存碎片。指定該參數(shù)后,在fgc后會整理碎片。
- -XX:CMSMaxAbortablePrecleanTime =5000
- -XX:+CMSClassUnloadingEnabled 方法區(qū)使用cms,允許對類的元數(shù)據(jù)進(jìn)行回收
- -XX:CMSPermGenSweepingEnabled 方法區(qū)使用cms
gc日志相關(guān)參數(shù)
- -Xloggc=/gc.log gc日志輸出的地址
- -XX:+PrintGCDetails 開啟詳細(xì)gc模式
- -XX:+PrintGCDateStamps 每一行前面加上絕對時間戳
- -XX:+HeapDumpOnOutOfMemoryError 內(nèi)存溢出時輸出堆日志
- -XX:HeapDumpPath=/heap.hprof 堆日志輸出地址
參考資料:
[1]【原】java內(nèi)存區(qū)域理解-初步了解 http://iamzhongyong.iteye.com/blog/1333100
[2] Java HotSpot VM Options http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html#Options