JVM深入淺出(一)

JDK版本:1.8
官網:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

學JVM的終極目標:生產環境上的性能調優。

JVM的Server模式和Client模式的區別(瞭解)

後面講的重點以Server模式爲主。因爲當Windows操作系統爲32位時,JVM默認使用Client模式,如果是其它操作系統,當2G 2C以上默認是Server模式,低於這個配置是Client模式。一般64位都是Server模式。
在這裏插入圖片描述

運行模式

Java有幾種模式:
解釋型int:這種模式不會把Java代碼轉換成本地代碼
編譯型comp:這種模式第一次會編譯成本地代碼,第一次會慢一點
混合型mixed:代碼是解釋還是編譯交由JVM來處理

JVM裏的參數類型詳解以及實操

1)標準參數 穩定的
什麼叫標準參數,舉個例子:java -version 、java -help等等,這些參數是不會變的。標準參數和這些類似。從jdk1.0到現在,這一批參數幾乎不會發生變化。
2)X參數 相對變化較少的
可能會變,但是變得又不多,比如:
在這裏插入圖片描述
從上面可以看到加上-X有很多模式,解釋模式去執行你的代碼,編譯模式去執行你的代碼,混合模式去執行你的代碼(這件事情是解釋還是編譯交由JVM來處理)。
那麼Java是解釋執行的還是編譯執行的?不能確定,因爲有個mixed混合執行。

再比如:
jdk7:有個東西叫永久代,在jdk8裏面變成了Metaspace。發生了很大的變化。

3)XX參數 JVM調優的重點
XX參數分爲:boolean布爾類型和非布爾類型。

舉例:標準寫法(boolean布爾類型)
-XX:[±]UseG1GC :表示把UseG1GC這個參數給啓用或者禁用
還有一些參數是這樣寫(非布爾類):
-XX:MaxPermSize=64m :表示把MaxPermSize這個參數設置成64M

舉個例子:
jps查看到某個pid(jps命令是查看Java進程用的),然後ps -ef查看這個pid的相關信息,可以看到很多相應的參數設置,如下:(jinfo -flags 直接跟pid可以列出所有的)
在這裏插入圖片描述

用PrintGCDetails這個參數再舉個例子:
用IDEA開發一段代碼:
在這裏插入圖片描述
按照下面方式可以看到,並沒有啓用PrintGCDetails這個參數。
在這裏插入圖片描述
怎麼啓用這個參數?
找到下面這個頁面,並在VM Options裏面設置:-XX:+PrintGCDetails
在這裏插入圖片描述
再啓動代碼,可以看到現在已經啓動了這個參數:
在這裏插入圖片描述
再看IDEA控制檯,已經把GC相關信息打印出來了:
在這裏插入圖片描述

再比如:如何查看某個進程使用的什麼GC ?
可以這樣看,先查到這個進程的進程號,然後可以這樣:
jinfo -flag UseG1GC 進程號
jinfo -flag UseParallelGC 進程號
jinfo -flag UseConcMarkSweepGC 進程號

可以看一下這個進程使用了那種GC。下面用了UseParallelGC這種垃圾回收。
在這裏插入圖片描述
上面是布爾類型,下面是非布爾類型
比如:-XX:MetaspaceSize參數
在這裏插入圖片描述
可以看大MetaspaceSize是20多M,加入現在想把它設置大一點,設置成128M,如何設置?和上面類似。在這個地方加上-XX:MetaspaceSize=128m
在這裏插入圖片描述
啓動代碼,再看它已經變成了128M
在這裏插入圖片描述

jinfo命令詳解以及實操

看幫助:
在這裏插入圖片描述

舉例:
在這裏插入圖片描述
如下:

//這個是默認參數
Non-default VM flags: 
-XX:CICompilerCount=3 
-XX:InitialHeapSize=134217728 
-XX:MaxHeapSize=2124414976 
-XX:MaxNewSize=707788800 
-XX:MetaspaceSize=134217728 
-XX:MinHeapDeltaBytes=524288 
-XX:NewSize=44564480 
-XX:OldSize=89653248 
-XX:+PrintGCDetails 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:+UseFastUnorderedTimeStamps 
-XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC

//這個是IDEA中自己設置的參數
Command line:  -XX:+PrintGCDetails -XX:MetaspaceSize=128m 
-javaagent:D:\JetBrains\IntelliJ IDEA Community Edition 2018.2.8\lib\idea_rt.jar=54178:
D:\JetBrains\IntelliJ IDEA Community Edition 2018.2.8\bin -Dfile.encoding=UTF-8

PrintFlags系列參數及實操

-XX:+PrintFlagsInitial
-XX:+PrintFlagsFinal

在命令行輸入:java -XX:+PrintFlagsInitial 回車後會有很多初始化參數。
在這裏插入圖片描述
= 是默認值
:= 是修改過的

-Xmx 、-Xms、-Xss參數及實操

-Xmx 、-Xms、-Xss這三個參數是屬於XX參數類型裏面的。
其中-Xms 就相當於 -XX:InitalHeapSize(JVM初始化堆大小)
-Xmx 就相當於 -XX:MaxHeapSize(JVM最大堆大小)
-Xss 就相當於 -XX:ThreadStackSize(JVM線程)

-XX:InitalHeapSize:這個JVM初始化堆大小佔電腦的memory的1/64,如果電腦是12G內存,那麼它差不多是200M。
-XX:MaxHeapSize:這個JVM最大堆大小佔電腦的memory的1/4,如果電腦是12G內存,那麼它是3G。

-XX:ThreadStackSize:和環境有關。

**生產上的最佳實踐,一般設置InitalHeapSize等於MaxHeapSize。**最小的和最大的是相等的。

JVM運行時數據區

運行時數據區必看文檔:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5

通過兩個圖來說明一下:
在這裏插入圖片描述
在這裏插入圖片描述
JVM運行時數據區中,有些區是JVM啓動時就被創建,JVM退出時被銷燬,跟隨JVM,另外一些區是每個線程都有的,線程創建它就被創建,線程退出它就退出,跟隨線程。

1.The pc Register:pc計數器。
不是重點。

2.Java Virtual Machine Stacks:Java虛擬機棧。
每個線程都有它自己的Java虛擬機棧,伴隨着線程被創建。像拋出異常的時候,那些信息就是虛擬機棧裏面打印出來的。

3.Heap:堆(重點)。
JVM的Heap(堆)是所有JVM線程所共享的。它是在JVM虛擬機啓動的時候被創建。
Heap裏面裝的是:對象實列和數組(class instances and arrays)。
Heap裏面存儲的對象,由自動存儲管理系統回收,這就是總所周知的GC垃圾回收,用垃圾回收器來進行管理。
舉例:
你現在創建了一個class User。
然後在另一個地方new一個:User user = new User();
前面user是引用,後面new User()是對象,這個對象放在哪裏?就放在Heap裏面。

可以根據實現者的系統需求來選擇合適的垃圾回收器。
堆可以是固定大小的,也可以根據計算需要進行擴展,如果不需要更大的堆,則可以收縮堆。堆的內存不需要是連續的。

如果計算需要的堆比自動存儲管理系統提供的堆多,Java虛擬機將拋出OutOfMemory錯誤。

4.Method Area 方法區
JVM的方法區也是所有JVM線程所共享的。它並不是堆裏面的。
方法區在jdk1.8裏面就是那個Metaspace。

It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (§2.9) used in class and instance initialization and interface initialization.
方法區存儲每個類結構,如運行時常量池、字段和方法數據,以及方法和構造函數的代碼,包括在類和實例初始化以及接口初始化中使用的特殊方法。

User user = new User();
引用 對象
定義了一個User類,會有User.class文件,它就存儲在方法區裏面,就是Metaspace裏面。
上面引用user是在棧裏面,對象new User()是在堆裏面。

方法區和堆是GC調優的重點
方法區和堆都能存東西,但是存的東西是不一樣的。
方法區存的是class等,堆存的是對象。

5.Run-Time Constant Pool 運行時常量池
不是重點。

6.Native Method Stacks 本地方法棧

JVM的內存結構

在jdk1.8裏面,有兩大區域,堆和非堆。
JVM堆內存空間分爲兩個區域:新生代(Young generation)和老生代(Old generation)。新生代用以保存生存週期短的對象,而老生代則是保存生存週期長的對象。
新生代區域被進一步劃分爲三個子區域:Eden,Survivor0,Survivor1。
在這裏插入圖片描述
s0也叫from,s1也叫to。
so和s1大小都是一樣的,在同一個時間點,只有一個是開啓的,可用的,另外一個是空的,可以理解爲主備。大部分情況下都是:User user = new User(); new一個對象首先放在Eden區,如果Eden區滿了,就進行垃圾回收,如果對象還活着就放到s0區。每一次垃圾回收,會把Eden區和s0區的活的對象放到s1區,把其他的給清掉,這個時候Eden區和s0區就會空下來。每次回收都會有個年齡age的增加,每次加1。某個對象每次加1,當達到了某個限定值,就會被放到老年代區。前面是小GC,如果最終如果老生代也快滿了,full GC(全局GC)就會啓動。
Spark GC調優的目標就是確保老生代(Old generation )只保存生命週期長的RDD,而同時新生代(Young generation )的空間又能足夠保存生命週期短的對象。這樣就能在任務執行期間,避免啓動full GC來收集任務執行期間創建的臨時對象。

非堆區:
ccs:CompressedClassPointers壓縮類指針
短指針:32位
長指針:64位
如果開啓了ccs,就會使用短指針。
每new一個對象都會放在堆裏面,每new一個對象有指向自己的class的指針,class是在方法區MetaSpace裏面的。如果是在64位的環境下,指針是64位的,考慮到性能,有時候可以修改成32位的短指針。

CodeCache:跟JIT、jni有關係。如果編譯了本地代碼就在這裏,如果沒有編譯就不在。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章