JVM基础知识汇总
1.jvm是什么
???????Java Virtual Machine(Java虛擬機),它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的.
???????本文jvm是基于jdk7寫的,關于jdk8的jvm,和jdk7的稍有不同,詳見文章末尾鏈接
2.jvm能做什么
???????java語言之所以可以跨平臺,就是jvm屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就可以在多種平臺上不加修改地運行.Java虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行.這就是Java的能夠“一次編譯,到處運行”的原因.
3.jvm分類
3.1 SUN公司(已被oracal收購)有好幾款虛擬機,包括hotSport
3.2 BEA公司:jRockit(專注于服務器端應用)
3.3 IBM公司:J9 VM(用于作為IBM公司各種java產品的執行平臺)
3.4 微軟:Microsoft JVM(為了在瀏覽器中運行java程序,已經被sun公司搞死了)
3.5 其他(還有很多)
最常熟知的就是sun公司的hotSpotVm,也就是我們常用的vm.
我也是主要以這個為重點來展開的
4.jvm內存區域(HotSpot)
4.1 程序計數器(線程私有)
???????占用很小的內存空間,可以看成是當前線程所執行的字節碼的行號指示器,所以他是每個線程私有的,各線程的計數器互不影響,獨立存儲
4.2 java棧(線程私有)
4.2.1 java虛擬機棧
???????執行java方法
4.2.2 本地方法棧
???????執行本地方法(可以理解為java同c語言的接口)
???????每個方法在執行的同時都會創建一個棧幀用于存儲局部變量表,操作數棧,動態鏈接,方法出口的信息.
局部變量表:
------存儲方法中的局部變量(包括在方法中聲明的非靜態變量以及函數形參)
------對于基本數據類型的變量,則直接存儲它的值,對于引用類型的變量,則存的是指向對象的引用.
------局部變量表的大小在編譯器就可以確定其大小了,因此在程序執行期間局部變量表的大小是不會改變的.
操作數棧:
------所有計算過程都是在借助于操作數棧來完成的.
方法出口:
------方法返回地址,當一個方法執行完畢之后,要返回之前調用它的地方,因此在棧幀中必須保存一個方法返回地址。
4.3 java堆(線程共享)
???????虛擬機啟動的時候創建,主要用來存放對象實例,GC的主要陣地.
4.3.1 新生代
------Eden
------Form Survivor
------To Survivor
4.3.2 老年代
4.4 方法區(線程共享)
非堆永久代(hotSpot特有),為了讓垃圾收集器像管理堆一樣管理這部分內存.
方法區存儲了每個類的信息,比如:
4.4.1.Classloader 引用
4.4.2.字段數據(每個字段的字段名,類型,修飾符,屬性)
4.4.3.方法數據(每個方法的方法名,返回值類型,參數類型,修飾符,屬性)
4.4.4.方法代碼(每個方法的字節碼,操作數棧大小,局部變量大小,局部變量表,異常表,每個異常處理器,開始點,結束點,異常處理代碼的程序計數器(PC)偏移量,被捕獲的異常類對應的常量池下標)
???????因為所有線程共享同一個方法區,因此訪問方法區數據的和動態鏈接的進程必須線程安全。如果兩個線程試圖訪問一個還未加載的類的字段或方法,必須只加載一次,而且兩個線程必須等它加載完畢才能繼續執行。
4.5 運行時常量池
jvm7之前屬于方法區,用來存儲數值型常量,字段引用,方法引用,屬性
關于常量池,字符串常量池,運行時常量池區別,請戳這里:
區別
java內存對象模型如下圖
5.jvm垃圾收集算法
5.1 標記-清除算法
先標記需要回收的對象,后統一清除,缺點是效率不高,而且會產生大量不連續的內存碎片,導致后面的大對象無法存儲,從而導致再一次收集.
5.2 復制算法(適用于對象存活率較低的內存區域,比如新生代)
先將可用內存劃分成兩塊A B,每次使用一塊A,當這塊A滿了,就將這塊中還存活的對象放在另一塊B上,然后一次性清理掉A.復制算法簡單粗暴效率高.
如果將AB的內存比例設置為1:1,那么相當于將可用內存減半了,太不劃算,考慮到98%的對象都是朝生夕死,所以將新生代分為三塊:Eden,Form Survivor,To Survivor,默認8:1:1
(兩個survivor,是為了保證任何時候都有一個survivor是空的)
5.3 標記-整理算法(適用于對象存活率高的內存區域,比如老年代)
先標記需要回收的對象,然后讓所有存活的對象都向一端移動,最后清理掉端邊界以外的內存,這樣一次收集后,空閑的內存會比較連續,更容易存放大對象.
同樣的,這個算法,效率更低.
6.JVM垃圾收集器
首先說收集器的目標:低停頓,高吞吐,大覆蓋
a. 停頓時間
停頓時間越短就越適合于用戶交互的程序,良好的響應速度能提升用戶體驗。
b. 吞吐量
高吞吐量則可以高效率利用CPU時間,盡快完成運算任務。適合在后臺計算而不需要太多交互的任務。
c. 覆蓋區
在達到前面兩點的情況下,盡量減少堆的內存空間,可以獲得更好的空間局部性。
6.1 Serial收集器 ['s??r??l]
單線程收集器.
特點1:簡單高效
特點2:Stop The Word.即收集垃圾的時候,會把其他線程全部停掉
“-XX:+UseSerialGC” : 該參數用來顯示的添加串行垃圾收集器
6.2 Parnew收集器['pɑ?nju?]
是Serial收集器的多線程版本
特點:除了Serial收集器,目前只有Parnew收集器能與CMS收集器配合工作
“-XX:+UseConcMarkSweepGC”:指定使用CMS后,會默認使用ParNew作為新生代收集器
“-XX:+UseParNewGC”:強制指定使用ParNew收集器
“-XX:ParallelGCThreads”:指定垃圾收集的線程數量,ParNew默認開啟的收集線程與CPU的數量相同
6.3 Parallel Scavenge收集器 ['p?r?lel]['sk?v?nd?]
特點1:關注點在于吞吐量,而其他線程一般都注重縮短垃圾回收時,用戶停頓時間
吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)
特點2:多了一個GC自適應調節策略,-XX:+UseAdaptiveSizePolicy
“-XX:MaxGCPauseMillis”:控制最大垃圾收集停頓時間
“-XX:GCTimeRatio”:直接設置吞吐量大小(0-100),默認值是99,就是允許最大1%(即 1/(1+99))的垃圾收集時間
6.4 Serial Old收集器
Serial收集器的老年代版本,
特點:單線程收集器.新生代采取復制算法,老年代采取標記整理算法
6.5 Parallel Old收集器
Parallel Scavenge收集器的老年代版本,1.6開始出現
特點:多線程,標記整理算法,主要是和Parallel Scavenge配合使用
“-XX:+UseParallelOldGC”:指定使用Parallel Old收集器。
6.6 CMS收集器(Concurrent Mark Sweep)
標記清除算法
特點:回收停頓時間短,并發收集
“-XX:+UseConcMarkSweepGC”: 指定使用CMS收集器(用了這個后,年輕代會默認使用ParNew收集器)
“-XX:CMSInitiatingOccupancyFraction=75” :是指設定CMS在對內存占用率達到75%的時候開始GC(因為CMS會有浮動垃圾,所以一般都較早啟動GC)
“-XX:+UseCMSInitiatingOccupancyOnly”:只是用設定的回收閾值(上面指定的75%),如果不指定,JVM僅在第一次使用設定值,后續則自動調整.
一般情況下,"-XX:CMSInitiatingOccupancyFraction=75"和"-XX:+UseCMSInitiatingOccupancyOnly"是配合使用的
CMS缺點1:并發導致cpu資源敏感,尤其是4C以下,CMS默認的收集線程數量是 = (CPU數量 + 3)/4,所以說核數越大,CMS并發對用戶的影響越小
解決 : 多核cup考慮一下
CMS缺點2:浮動垃圾(并發清除的時候產生的垃圾)可能導致再一次FullGC(啟動Serial Old收集器),這次收集時間較長
解決 : “-XX:CMSInitiatingOccupancyFraction”:設置CMS預留內存空間,就是給浮動垃圾用的,當這塊內存不夠用的時候,就會再次導致FullGC
CMS缺點3:產生大量內存碎片(標記清除算法 )
解決1: “-XX:+UseCMSCompactAtFullCollection”,默認開啟,用于fullGC前的合并整理,但這個整理過程需要停頓
解決2: “-XX:+CMSFullGCBeforeCompaction”,默認0,當執行n次不壓縮的fullGC后,下次FullGC帶壓縮
總而言之,服務器內存越大,核數越多,就越適合用CMS
6.7 G1收集器
堆空間分割收集
特點1:使用并發讓GC的時候,應用程序繼續執行(G1在另一個CPU上運行)
特點2:分代收集,但不需要其他收集器配合,能夠采用不同方式處理不同時期的對象,但新生代和年老代不再是物理隔離,它們都是一部分Region(不需要連續)的集合
特點3:整體標記整理,局部復制算法(堆劃分為多個大小相等的獨立區域Region),總之不是標記清除算法
特點4:做到實時垃圾收集(RTSJ),即可預測的停頓,建立可預測的停頓時間模型,可以明確指定M毫秒時間片內,垃圾收集消耗的時間不超過N毫秒,這樣在收集的時候就可以有選擇的在用戶設置的時間范圍內回收最優先需要回收的region(G1跟蹤各個Region獲得其收集價值大小,在后臺維護一個優先列表)
特點5:整體優于CMS的低停頓
“-XX:UseG1GC”:指定使用G1收集器.
“-XX:MaxGCPauseMillis”:為G1收集器設置暫停時間目標,默認值為200毫秒
“-XX:G1HeapRegionSize”:設置每個Region大小,范圍1MB到32MB;在最小Java堆時可以擁有大約2048個Region
垃圾收集器關系圖:
jdk1.7 默認垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.8 默認垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.9 默認垃圾收集器G1
7.jvm可視化
8.jvm調優
-XX:+PrintGC 輸出GC日志 -XX:+PrintGCDetails 輸出GC的詳細日志 -XX:+PrintGCTimeStamps 輸出GC的時間戳(以基準時間的形式) -XX:+PrintGCDateStamps 輸出GC的時間戳(以日期的形式,如 2013-05-04T21:53:59.234+0800) -XX:+PrintHeapAtGC 在進行GC的前后打印出堆的信息 -XX:+PrintGCApplicationStoppedTime // 輸出GC造成應用暫停的時間 -Xloggc:../logs/gc.log 日志文件的輸出路徑 -XX:+HeapDumpOnOutOfMemoryError //發生OOM的時候自動dump堆棧方便分析9.jvm常見問題
???????9.1 如果A和B對象循環引用,是否可以被GC?
???????9.2 如何判斷對象是否需要回收,有哪幾種方式?
???????9.3 Java中能不能主動觸發GC?
???????9.4 Java中的內存溢出是什么,和內存泄露有什么關系?
???????9.5 Java中的內存溢出是什么,和內存泄露有什么關系?
???????9.6 ClassLoader的類加載方式
10.jvm–jdk1.8
參考:
https://blog.csdn.net/joeyon1985/article/details/39080125
11.jvm本地小測試
參考:
https://blog.csdn.net/universe_ant/article/details/58585854
總結
- 上一篇: 改进初学者的PID-积分饱和
- 下一篇: 外设驱动库开发笔记12:TSEV01CL