- JVM調(diào)優(yōu)是調(diào)整:方法區(qū)和堆(主要是堆)
- 棧管運行陌宿,堆管存儲
- 堆和方法區(qū)是所有線程共享的內(nèi)存區(qū)域咆疗;棧和程序計數(shù)器是每個線程私有的內(nèi)存區(qū)域涧黄。
注:由于棧是每個線程私有的篮昧,棧空間是每個線程的運行內(nèi)存 - 運行時數(shù)據(jù)區(qū)也就是我們的JVM內(nèi)存模型區(qū)域笋妥。JVM在運行程序時會將他擁有的內(nèi)存 邏輯性的劃分懊昨,讓每部分內(nèi)存做它該做的事情。
類加載器(class loader):
加載 .class文件春宣,生成類模板酵颁。(只負責(zé)類的加載,能不能運行要看Execution Engine執(zhí)行引擎)
- 類加載器分為:
- 啟動類加載器
- 擴展類加載器
- 應(yīng)用類加載器
雙親委派機制:往上拋月帝,上面的如果可以加載躏惋,就用上面的
本地方法接口(Native Interface):調(diào)用C/C++的本地庫
本地方法棧(Native Method Stack):讓執(zhí)行引擎加載本地方法
程序計數(shù)器:用來存儲指向下一條指令的地址,也即將要執(zhí)行的指令代碼
執(zhí)行引擎(Execution Engine):負責(zé)解釋命令,提交操作系統(tǒng)執(zhí)行
棧(stack):
- 每個線程都有一個私有的棧嚷辅,隨著線程的創(chuàng)建而創(chuàng)建
- 棧存儲8種基本數(shù)據(jù)類型 + 引用變量(存儲堆中地址)+ 實例方法
- 壓棧簿姨,先進后出
- java.lang.StackOverFlowError 棧溢出異常(創(chuàng)建的引用太多,或死循環(huán))
方法區(qū)(Method Area):
存儲 靜態(tài)變量簸搞,常量扁位,類模板(構(gòu)造方法/接口定義)
堆(heap)
分為三個區(qū)域
- 新生代
- 伊甸園區(qū)
- 幸存0區(qū)
- 幸存1區(qū)
- 老年代
- 永久代
- 新生代 + 老年代 = 實際堆內(nèi)存
- java1.7永久代 = 方法區(qū)(存放靜態(tài)變量,常量趁俊,類模板)域仇;
java1.8永久代 = 方法區(qū)(存放靜態(tài)變量,常量)+本地內(nèi)存-元空間(存放類模板), 改善了之前依賴的jar包太多寺擂,加載的類模板太多暇务,啟動項目時會出現(xiàn)永久區(qū)OOM:PermGen space的情況 - 空間占用
· 新生代占堆空間的1/3泼掠,其中伊甸園區(qū):From區(qū):To區(qū) = 8:1:1
· 老年代占堆空間的2/3
GC(Garbage Collection)
分代垃圾收集算法:新生代和老年代的收集算法不同
- 頻繁收集新生代:minorGC 輕量級GC—使用復(fù)制算法
- 較少收集老年代:majorGC,也叫fullGC:使用標(biāo)記清除算法般卑,標(biāo)記清除壓縮算法
- 基本不動永久代
majorGC速度比minorGC慢10倍以上
GC的四種回收算法:
引用計數(shù)法(native源碼中有武鲁,但不使用):維護一個kv結(jié)構(gòu)爽雄,key為變量蝠检,v為引用次數(shù),每次清理v=0的變量挚瘟;
· 缺點:變量互相引用后叹谁,如果將變量賦值為null,此時對象會沒有引用乘盖,但對象的v>0焰檩,不會被清除
下列算法使用 可達性分析算法 判斷對象是否被引用:被棧、方法區(qū)正在引用著的對象订框,是可達對象析苫;清理沒有引用的對象(清理不可達對象)。復(fù)制算法:只復(fù)制可達的對象到to區(qū)(幸存0/1區(qū))穿扳,其余對象全部清除衩侥;
優(yōu)點:沒有內(nèi)存碎片
缺點:因為只復(fù)制存活的對象,所以復(fù)制算法適用于存活率較低的場景(新生代)矛物;如果因為代碼問題導(dǎo)致存活率較高時茫死,復(fù)制的時間會越來越長。且復(fù)制算法的內(nèi)存占用大標(biāo)記清除算法(Mark-Sweep):顧名思義履羞,將可達對象標(biāo)記后峦萎,清除不可達對象
優(yōu)點:內(nèi)存占用較小
缺點:清除后內(nèi)存空間不連續(xù)會生成內(nèi)存碎片
4、標(biāo)記清除壓縮算法(Mark-Sweep-Compact):多次標(biāo)記清除后忆首,才觸發(fā)壓縮操作爱榔。有效減少壓縮的執(zhí)行時間(GMS老年代實際使用的算法)
擴展:標(biāo)記清除其實是三次標(biāo)記,一次清除糙及,第1详幽、3次標(biāo)記時會stop the world
所以如果remark標(biāo)記階段把A對象標(biāo)記為1,而程序運行時又將A變?yōu)榭蛇_對象丁鹉,但A仍然會被清除
?????? new對象的時候妒潭,對象會出生在伊甸園區(qū),當(dāng)伊甸園區(qū)滿了揣钦,會執(zhí)行minorGC(輕量級GC)雳灾,伊甸園區(qū)沒有引用的對象和幸存0區(qū)(from區(qū))沒有引用的對象,大約98%的對象會被minorGC回收冯凹,存活的對象copy(復(fù)制算法)到幸存1區(qū)(to區(qū))谎亩,然后將存儲對象的to區(qū)變成from區(qū)炒嘲,將空的from區(qū)變成to區(qū),幸存對象的年齡會加1匈庭;
?????? 當(dāng)伊甸園區(qū)又滿了之后夫凸,新的from區(qū)(里面是上次minorGC后幸存的對象)中沒有引用的對象和伊甸園區(qū)沒有引用的對象,會被minorGC回收阱持,當(dāng)幸存對象的年齡達到15夭拌,這個對象將會存儲到老年代。
?????? 當(dāng)老年代滿了之后會執(zhí)行majorGC(重量級GC)使用標(biāo)記清除壓縮算法衷咽,先標(biāo)記幸存對象鸽扁,清除被未標(biāo)記的對象;多次清理后镶骗,整理成內(nèi)存連續(xù)的空間
注:當(dāng)minorGC執(zhí)行后桶现,將存活的對象copy到To區(qū)時,如果To區(qū)內(nèi)存不夠鼎姊,會直接將剩下的對象放入老年代骡和,所以某些特定場景下可以增大幸存0/1區(qū)的內(nèi)存,減少對象進入老年代的數(shù)量相寇,從而減少fullGC
堆內(nèi)存溢出問題的原因和解決辦法:
1慰于、JVM堆內(nèi)存設(shè)置不夠
2、老年代滿了(存在大量對象裆赵,不能被GC清理东囚,對象有引用)
上述問題會引起java.lang.OutOfMemoryError:java heap space 堆內(nèi)存溢出異常。
解決辦法
可以分析MAT(Eclipse Memory Analyzer 內(nèi)存分析器)
· 分析dump快照文件战授,快速定位內(nèi)存泄漏
· 獲取堆中對象的數(shù)據(jù)
· 獲取對象相互引用的關(guān)系
具體步驟參照另一篇文章:一次線上JVM內(nèi)存泄漏的分析和解決
JVM的基礎(chǔ)調(diào)優(yōu)策略
堆內(nèi)存調(diào)優(yōu)常用參數(shù)解析:
-Xms :分配初始內(nèi)存页藻,默認物理內(nèi)存的1/64
-Xmx:分配最大內(nèi)存,默認為物理內(nèi)存的1/4
-XX:MaxTenuringThreshold:對象進入老年區(qū)要經(jīng)過minorGC的次數(shù)閾值植兰,默認15份帐,經(jīng)驗值31