jvm详解、GC、堆内存参数调优
一些常見面試題:
JVM的位置(運行在操作系統(tǒng)上,與硬件沒有直接的交互)
一、jvm體系結(jié)構(gòu)(記住背下來)
運行時數(shù)據(jù)區(qū):有亮色的有灰色的,灰色的就是占得內(nèi)存非常小,幾乎不存在GC垃圾回收,并且線程獨占的,亮色的存在垃圾回收,并且所有線程共享。
二、類裝載器
(看做成快遞員,把class文件(class文件開頭有特定的文件標示cafe babe)字節(jié)碼加載到內(nèi)存,并將這些內(nèi)容轉(zhuǎn)換成方法區(qū)中的運行時數(shù)據(jù)結(jié)構(gòu)并且ClassLoader只負責(zé)class文件的加載,至于是否可以運行,由Execution Engine決定)
類裝載器有哪幾個?
1-啟動類加載器,負責(zé)加載%JAVA_HOME%\bin目錄下的所有jar包,或者是-Xbootclasspath參數(shù)指定的路徑;
2-擴展類加載器:負責(zé)加載%JAVA_HOME%\bin\ext目錄下的所有jar包,或者是java.ext.dirs參數(shù)指定的路徑;
3-應(yīng)用程序類加載器:負責(zé)加載用戶類路徑上所指定的類庫,如果應(yīng)用程序中沒有自定義加載器,那么次加載器就為默認加載器。
加載器之間的層次關(guān)系:
public class MyObject {public static void main(String []args){Object object = new Object();MyObject myObject = new MyObject();System.out.println(object.getClass().getClassLoader());System.out.println(myObject.getClass().getClassLoader());} }運行結(jié)果:啟動類加載器來加載java自帶的類,BootStrap是C++寫的所以輸出的null;
package JVM;public class MyObject {public static void main(String []args){Object object = new Object();System.out.println(object.getClass().getClassLoader());System.out.println();System.out.println();System.out.println();MyObject myObject = new MyObject();System.out.println(myObject.getClass().getClassLoader());System.out.println(myObject.getClass().getClassLoader().getParent());System.out.println(myObject.getClass().getClassLoader().getParent().getParent());} }雙親委派機制:
雙親委派機制得工作過程:
1-類加載器收到類加載的請求;
2-把這個請求委托給父加載器去完成,一直向上委托,直到啟動類加載器(bootstrap);
3-啟動器加載器檢查能不能加載(使用findClass()方法),能就加載(結(jié)束);否則,拋出異常,通知子加載器進行加載。
4-重復(fù)3;
當加載一個類會先到啟動類加載器去找,找得到就用,找不到就到擴展類加載器找,找不到再去應(yīng)用程序類加載器去找。(從頂層往下開始找)
雙親委派機制的理由?
是沙箱安全機制
例:
String類,String是java.lang包下的類,默認情況下是啟動類加載器進行加載的。假設(shè)我也自定義一個String?,F(xiàn)在你會發(fā)現(xiàn)自定義的String可以正常編譯,但是永遠無法被加載運行。
這是因為申請自定義String加載時,總是啟動類加載器,不會是其他的加載器,也就是不會用應(yīng)用程序加載器加載。
運行結(jié)果:是無法被加載的
三、本地方法棧(加載native方法,了解)
四、程序計數(shù)器(類似一個指針,一條指令執(zhí)行完用來指向下一個指令 )
五、方法區(qū)(類的模板工廠)
六、java棧(灰色,線程私有,不存在gc )
棧中主要存儲3類數(shù)據(jù):
本地變量:輸入?yún)?shù)和輸出參數(shù),方法內(nèi)的變量
棧操作:記錄出棧、入棧的操作
棧幀數(shù)據(jù):包括類文件、方法等。
七、堆(一個jvm實例只存在一個堆空間,大小可以調(diào)節(jié))
堆分為三部分:新生區(qū),養(yǎng)老區(qū),永久區(qū)
新生區(qū)分為:伊甸區(qū)、幸存者0區(qū)、幸存者1區(qū)
java8把永久區(qū)改為元空間
(物理上堆分為新生區(qū)、養(yǎng)老區(qū);邏輯上分為新生區(qū)、養(yǎng)老區(qū)、元空間)
java7之前, java8把永久區(qū)換成了元空間()
詳細版:復(fù)制 -> 清空 -> 交換
對象生命周期和GC
jvm堆參數(shù)調(diào)優(yōu):
-Xms:start起始內(nèi)存
-Xmx:max最大內(nèi)存
-Xmn:一般不會調(diào)這個參數(shù)!
java8中,永久代被移除,被元空間取代,元空間的本質(zhì)和永久代類似。
元空間與永久代之間最大的區(qū)別在于:
永久帶使用的JVM的堆內(nèi)存,但是java8以后的元空間并不在虛擬機中而是使用本機物理內(nèi)存。
因此,默認情況下,元空間的大小僅受本地內(nèi)存限制。類的元數(shù)據(jù)放入native memory,字符串池和類的靜態(tài)變量放入java堆中,這樣可以加載多少的類的元數(shù)據(jù)就不再有MaxPermSize控制,而由系統(tǒng)的實際可用空間來控制。
默認
-Xms:為物理內(nèi)存大小的1/64
-Xmx:為物理內(nèi)存大小的1/4
jvm參數(shù)調(diào)優(yōu):實際-Xms和-Xmx大小必須一致,防止GC和應(yīng)用程序爭搶內(nèi)存,理論值的峰值和峰度忽高忽低。
先查看內(nèi)存大小:
更改-Xms和-Xmx參數(shù):-Xms1024m -Xmx1024m -XX:+PrintGCDetails
運行:
上圖: 新生代+老年代的內(nèi)存大小等于981.5MB,元空間用的是物理內(nèi)存空間!
GC 垃圾收集機制(分代回收算法)
分代收集算法:
- 次數(shù)上頻繁收集Young區(qū)
- 次數(shù)上較少收集Old區(qū)
- 基本不動元空間
GC 4個算法
1.引用計數(shù)法(了解,不用)
2. 復(fù)制算法(年輕代中的GC,不會產(chǎn)生內(nèi)存碎片速度快,但是消耗內(nèi)存空間)
當Eden區(qū)滿了,會回收,沒有被回收的會和survivorFrom區(qū)沒有被回收的使用復(fù)制算法復(fù)制到survivorTo區(qū),然后from區(qū)和Eden區(qū)全部清空,然后from區(qū)和to去進行交換(from區(qū)變成to區(qū),to區(qū)變成from區(qū),所以說誰空誰是to),對象沒熬過一次Minor GC年齡就會+1,當對象年齡達到默認設(shè)置的15
(-XX:MaxTenuring Threshold參數(shù)來設(shè)置)那么就會被送到老年代。
3.標記清除(老年代使用,一般由標記清除或者是標記清除與標記整理的混合實現(xiàn)。。。與復(fù)制算法比較:節(jié)約了內(nèi)存空間,但是會產(chǎn)生內(nèi)存碎片,標記和清除會掃描兩次耗時嚴重)
當程序運行期間,若可以使用的內(nèi)存被耗盡的時候,GC線程就會被觸發(fā)并將程序暫停,隨后將要回收的對象標記一遍,最終統(tǒng)一回收這些對象,完成標記清理工作接下來便讓應(yīng)用程序恢復(fù)運行
4.標記壓縮(標記完成后不會進行清除,而是所有存活對象都像一端移動,然后直接清除邊界以外的內(nèi)存,這樣不會產(chǎn)生內(nèi)存碎片,但是時間會更長)
原理:
工作中實際使用:(標記壓縮,多次GC后才會進行清除)
GC算法小總結(jié):
有沒有最好的GC算法??(沒有最好的算法,只有最適合的算法,所以采用分代收集,標記整理好但是耗時)
但是有個G1垃圾收集算法: Garbage First(G1) 垃圾收集器(目前就不往下深入學(xué)習(xí)了,這里留個問題,以后再學(xué)習(xí))
總結(jié)
以上是生活随笔為你收集整理的jvm详解、GC、堆内存参数调优的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 腾讯《三体》电视剧预告及海报 实力派演员
- 下一篇: 祝贺!翟志刚王亚平获二级航天功勋奖章