主要題目出處:《近一個(gè)月的面試總結(jié)(java)》
一涮因、 JVM內(nèi)存管理機(jī)制(Java Virtual Machine)
JVM將內(nèi)存劃分為6個(gè)部分:
- PC寄存器(也叫程序計(jì)數(shù)器):記錄當(dāng)前線(xiàn)程運(yùn)行位置渴析,每個(gè)線(xiàn)程都有一個(gè)獨(dú)立的程序計(jì)數(shù)器铝噩,線(xiàn)程的阻塞免钻、回復(fù)绳慎、掛起等操作都需要程序計(jì)數(shù)器的參與甘萧,因此是線(xiàn)程私有的台夺。
- 虛擬機(jī)棧:創(chuàng)建線(xiàn)程時(shí)創(chuàng)建慨蛙,用來(lái)存儲(chǔ)棧幀辽聊,因此也是線(xiàn)程私有。java程序中的方法執(zhí)行時(shí)股淡,會(huì)創(chuàng)建一個(gè)棧幀身隐,用于存儲(chǔ)臨時(shí)數(shù)據(jù) 、中間結(jié)果唯灵、局部變量表贾铝、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息垢揩。溢出會(huì)報(bào)StackOverflowError玖绿。
- 堆:所有線(xiàn)程共享,用于存儲(chǔ)對(duì)象叁巨。堆空間不夠斑匪,同時(shí)無(wú)法申請(qǐng)足夠內(nèi)存時(shí),會(huì)報(bào)OutOfMemoryError锋勺。
- 方法區(qū):各個(gè)線(xiàn)程共享蚀瘸,存儲(chǔ)靜態(tài)變量、運(yùn)行時(shí)常量池等信息庶橱。
- 運(yùn)行時(shí)常量池:每個(gè)類(lèi)一個(gè)贮勃,從class常量池中遷移過(guò)來(lái),程序中使用的常量值苏章。
- 本地方法棧:支持native方法寂嘉,如在java中調(diào)用C/C++。
參考資料:《面試總結(jié):java程序執(zhí)行過(guò)程 + JVM內(nèi)存管理 + GC垃圾回收機(jī)制》
二枫绅、 GC垃圾回收機(jī)制(Garbage Collection)
三個(gè)問(wèn)題:
- 哪些內(nèi)存需要回收泉孩?
線(xiàn)程私有 | 線(xiàn)程共享 |
---|---|
程序計(jì)數(shù)器、虛擬機(jī)棧并淋、本地方法棧 | java堆寓搬,方法區(qū) |
線(xiàn)程銷(xiāo)毀,內(nèi)存自動(dòng)釋放预伺,總大小已定 | 內(nèi)存空間動(dòng)態(tài)分配订咸,所以需要?jiǎng)討B(tài)回收 |
- 何時(shí)回收?
- 引用計(jì)數(shù)法:對(duì)某個(gè)對(duì)象a酬诀,若給a賦有效值脏嚷,則a的引用計(jì)數(shù)增加1,否則減少1瞒御,當(dāng)引用次數(shù)為0時(shí)父叙,即可回收。
該方法存在問(wèn)題:a=1;a=2;a=null;時(shí)肴裙,雖然顯式清空趾唱,但是引用計(jì)數(shù)仍為1,GC不回收蜻懦。- 可達(dá)性分析:設(shè)立若干根對(duì)象甜癞,每個(gè)對(duì)象都是一個(gè)子節(jié)點(diǎn),當(dāng)一個(gè)對(duì)象找不到根的時(shí)候宛乃,認(rèn)為其不可達(dá)悠咱。
根對(duì)象可以選擇:
1蒸辆、java虛擬機(jī)棧中引用對(duì)象。
2析既、方法區(qū)中靜態(tài)變量引用的對(duì)象躬贡。
3、方法區(qū)中常量引用的對(duì)象眼坏。
4拂玻、本地方法棧中引用的對(duì)象。
- 怎么回收宰译?
- 標(biāo)記-清除算法:遍歷所有GC Root檐蚜。分別標(biāo)記所有可達(dá)與不可達(dá)對(duì)象,刪去其中不可達(dá)部分沿侈。
優(yōu)點(diǎn):平庸而簡(jiǎn)便
缺點(diǎn):效率低熬甚,回收空間不連續(xù)。- 復(fù)制算法:將內(nèi)存分為兩塊肋坚,每次只使用其中一塊,當(dāng)內(nèi)存滿(mǎn)了肃廓,就將仍存活對(duì)象復(fù)制到另一塊中智厌,并嚴(yán)格按照內(nèi)存地址排列,然后原先的那塊內(nèi)存統(tǒng)一回收盲赊。
優(yōu)點(diǎn):能得到連續(xù)內(nèi)存空間
缺點(diǎn):浪費(fèi)一半內(nèi)存- 分代算法:按照對(duì)象存活時(shí)間铣鹏,分成新生代、老年代哀蘑、永久代诚卸。
新生代:蜉蝣于天地,朝生夕死绘迁,用復(fù)制算法批量刪合溺。
老年代:有的人還活著,但是他已經(jīng)活的太久了缀台。所有同齡的都已逝去棠赛,所有新生的都尚未了解。沒(méi)有人記得他膛腐,他已經(jīng)在社會(huì)層面死亡了睛约。用標(biāo)記-清除算法抹去。
永久代:不可以刪的如加載的class信息哲身,就不刪了留著吧辩涝。
參考資料:《面試總結(jié):java程序執(zhí)行過(guò)程 + JVM內(nèi)存管理 + GC垃圾回收機(jī)制》
三、 JVM內(nèi)存調(diào)優(yōu)
JVM內(nèi)存塊 = Permanent + Heap = (永久代) + (年輕代 + 年老代)
其中年輕代中存在一個(gè)Eden區(qū)勘天,以及兩個(gè)Survivor區(qū)怔揩。
大部分的對(duì)象都在Eden區(qū)生成捉邢,Eden滿(mǎn)了之后,仍存活的對(duì)象進(jìn)入Survivor區(qū)沧踏,兩個(gè)Survivor區(qū)執(zhí)行第二題中的復(fù)制算法歌逢。
經(jīng)過(guò)N(自定義)次Survivor滿(mǎn)引發(fā)的垃圾回收后,仍然存在的對(duì)象翘狱,可以進(jìn)入年老代秘案。
若年老代滿(mǎn)了,觸發(fā)一次Full GC潦匈,年輕代和年老代全部進(jìn)行清理阱高。如果仍然空間不夠,則報(bào)錯(cuò)茬缩。內(nèi)存調(diào)優(yōu):
參數(shù)說(shuō)明:
-client:設(shè)置JVM使用Client模式赤惊,啟動(dòng)快,但運(yùn)行時(shí)性能與內(nèi)存管理效率不高凰锡,用于客戶(hù)端程序或開(kāi)發(fā)調(diào)試未舟。32位環(huán)境直接運(yùn)行java程序默認(rèn)使用。
-server:設(shè)置JVM使用Server模式掂为,啟動(dòng)慢裕膀,但是性能和內(nèi)存管理效率高,適用于生產(chǎn)環(huán)境勇哗。64位JDK環(huán)境下默認(rèn)使用昼扛。
-Xmx3550m:設(shè)置JVM最大堆內(nèi)存為3550M
-Xms3550m:設(shè)置JVM初始堆內(nèi)存為3550M
-Xss128k:設(shè)置每個(gè)線(xiàn)程的棧大小
-Xmn2g:設(shè)置年輕代大小為2G,推薦采用整個(gè)堆大小的3/8
-XX:NewSize=1024m:設(shè)置年輕代初始值為1024M
-XX:MaxNewSize=1024m:設(shè)置年輕代最大值為1024M
-XX:PermSize=256m:設(shè)置持久代初始值為256M
-XX:NewRatio=4:設(shè)置年輕代(1+2)與年老代的比值
-XX:SurvivorRatio=4:設(shè)置年輕代中Eden區(qū)與Survivor區(qū)總大小的比值欲诺,此處表示Eden區(qū)占4/6,Survivor區(qū)每個(gè)子區(qū)占1/6
-XX:MaxTenuringThreshold=7:表示一個(gè)對(duì)象存活過(guò)幾次垃圾清理抄谐,就進(jìn)入年老區(qū)
推薦使用Xmn,一次設(shè)定初始值和最大值扰法,且兩者相等蛹含。三種垃圾回收器
- 串行收集器:適用于小數(shù)據(jù)量
- -XX:+UseSerrialGC:設(shè)置串行收集器
- 并行收集器:吞吐量?jī)?yōu)先
- -XX:+UseParallelGC:設(shè)置年輕代垃圾收集方式為并行收集
- -XX:+UseParallelOldGC:設(shè)置年老代垃圾收集方式為并行收集
- -XX:+ParallelGCThreads=20:設(shè)置并行收集器的線(xiàn)程數(shù),即同時(shí)有多少個(gè)線(xiàn)程一起進(jìn)行垃圾回收迹恐,一般與CPU數(shù)目相等挣惰。
- -XX:+UseAdaptiveSizePolicy:并行處理器自動(dòng)調(diào)整年輕代Eden與Survivor區(qū)比例,以達(dá)到目標(biāo)系統(tǒng)規(guī)定指標(biāo)殴边。建議始終打開(kāi)憎茂。
- 并發(fā)收集器:響應(yīng)時(shí)間優(yōu)先
- -XX:+UseConcMarkSweepGC:即CMS收集,設(shè)置年老代為并發(fā)收集
- -XX:CMSFullGCsBeforeCompaction=0:并發(fā)收集器不自動(dòng)對(duì)內(nèi)存空間壓縮整理锤岸。一段時(shí)間后會(huì)產(chǎn)生內(nèi)存碎片竖幔,設(shè)置0次Full GC后,對(duì)內(nèi)存空間進(jìn)行整理是偷,即每次Full GC后拳氢,對(duì)內(nèi)存空間進(jìn)行整理募逞。
實(shí)際案例
- 大型網(wǎng)站服務(wù)器
服務(wù)器配置:8 CPU, 8G MEM, JDK 1.6.X
參數(shù)方案:-server(服務(wù)器端)
-Xmx3550m(JVM最大堆內(nèi)存3550M) -Xms3550m(JVM初始堆內(nèi)存3550M)
-Xmn1256m(年輕代大小1256M)
-Xss128k(線(xiàn)程棧大小128K)
-XX:SurvivorRatio=6(年輕代中Eden/Survivor大小=6:1)
-XX:MaxPermSize=256m(設(shè)置持久代最大值為256M)
-XX:ParallelGCThreads=8(并行收集器線(xiàn)程數(shù)為8)
-XX:MaxTenuringThreshold=0(N的初值為0)
-XX:+UseConcMarkSweepGC(設(shè)置年老代為并發(fā)收集)調(diào)優(yōu)說(shuō)明:
- -Xms與-Xmx相同,避免重復(fù)申請(qǐng)內(nèi)存馋评。-Xmx約為系統(tǒng)內(nèi)存1/2放接,充分利用資源,同時(shí)確保安全的系統(tǒng)運(yùn)行空間留特。
- -Xms大小纠脾,官方推薦為系統(tǒng)內(nèi)存3/8
- 較小的線(xiàn)程棧,以支持更多線(xiàn)程蜕青。提升系統(tǒng)性能苟蹈。
- Eden/Survivor大小,系統(tǒng)默認(rèn)是8右核,根據(jù)經(jīng)驗(yàn)設(shè)置6慧脱。
- 并行收集器線(xiàn)程數(shù),一般等于CPU數(shù)
- 設(shè)置垃圾最大年齡贺喝,年齡長(zhǎng)菱鸥,增加在年輕代回收概率。0表示直接進(jìn)入年老代躏鱼,適合年老代比較多的應(yīng)用采缚。
- 并發(fā)的目的是減少應(yīng)用停止時(shí)間,適用于應(yīng)用中存在較多較長(zhǎng)生命周期的應(yīng)用挠他。
- 內(nèi)部集成服務(wù)器
服務(wù)器配置:1 CPU, 4G MEM, JDK 1.6.X
參數(shù)方案:-server
-XX:PermSize=196m -XX:MaxPermSize=196m
-Xmn320m -Xms768m -Xmx1024m調(diào)優(yōu)說(shuō)明:
- 內(nèi)部集成服務(wù)器,可能需要加載大量java類(lèi)進(jìn)入內(nèi)存篡帕,因此持久代適當(dāng)開(kāi)大
- 年輕代大小為整個(gè)堆3/8
- 根據(jù)系統(tǒng)大小設(shè)置堆內(nèi)存大小即可