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