第一章 JVM原理
一、什麼是JVM
JVM是Java Virtual Machine(Java虛擬機)的縮寫。
JAVA語言的平臺無關性如何實現:通過Java虛擬機,Java語言在不同平臺上運行時不需要重新編譯。Java語言使用Java虛擬機屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就可以在多種平臺上不加修改地運行。Java虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。這就是Java的能夠“一次編譯,到處運行”的原因。
上圖爲 JVM結構圖
二、JAVA代碼編譯和執行過程
JAVA代碼編譯和執行包含如下三個過程:
Java源碼編譯機制
類加載機制
類執行機制
java源碼編譯機制
最終將.java文件編譯生成.class文件
類加載機制
JVM的類通過ClassLoader及其子類來完成類加載的過程,類的層次關係和加載順序可以由下圖來描述:
(1)Bootstrap ClassLoader
負責加載$JAVA_HOME中jre/lib/rt.jar裏所有的class,由C++實現,不是ClassLoader子類
(2)Extension ClassLoader
負責加載java平臺中擴展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目錄下的jar包
(3)App ClassLoader
負責記載classpath中指定的jar包及目錄中class
(4)Custom ClassLoader
屬於應用程序根據自身需要自定義的ClassLoader,如tomcat、jboss都會根據j2ee規範自行實現ClassLoader加載過程中會先檢查類是否被已加載,檢查順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查,只要某個classloader已加載就視爲已加載此類,保證此類只所有ClassLoader加載一次。而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。
類執行機制
JVM是基於棧的體系結構來執行class字節碼的。線程創建後,都會產生程序計數器(PC)和棧(Stack),程序計數器存放下一條要執行的指令在方法內的偏移量,棧中存放一個個棧幀,每個棧幀對應着每個方法的每次調用,而棧幀又是有局部變量區和操作數棧兩部分組成,局部變量區用於存放方法中的局部變量和參數,操作數棧中用於存放方法執行過程中產生的中間結果。
三、JVM內存管理和垃圾回收
JVM棧有堆、棧、本地方法棧、方法區組成,結構如圖
1)堆
所有通過new創建的對象的內存都在堆中分配,堆的大小可以通過-Xmx和-Xms來控制。
堆分爲新生代、老年代和持久帶,新生代分爲Eden和Survivor(Survivor又分爲from和to)
新生代:新建的對象都是用新生代分配內存,Eden空間不足的時候,會把存活的對象轉移到Survivor中,新生代大小可以由-Xmn來控制,也可以用-XX:SurvivorRatio來控制Eden和Survivor的比例
老年代:用於存放新生代中經過多次垃圾回收仍然存活的對象
持久帶(Permanent Space):主要存放所有已加載的類信息,方法信息,常量池等等。可通過-XX:PermSize和-XX:MaxPermSize來指定持久帶初始化值和最大值。Permanent Space並不等同於方法區,只不過是Hotspot JVM用Permanent Space來實現方法區而已,有些虛擬機沒有Permanent Space而用其他機制來實現方法區。
-Xmx:最大堆內存,如:-Xmx512m -Xms:初始時堆內存,如:-Xms256m -XX:MaxNewSize:最大年輕區內存 -XX:NewSize:初始時年輕區內存.通常爲 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 個 Survivor 空間。實際可用空間爲 = Eden + 1 個 Survivor,即 90% -XX:MaxPermSize:最大持久帶內存 -XX:PermSize:初始時持久帶內存 -XX:+PrintGCDetails。打印 GC 信息 -XX:NewRatio 新生代與老年代的比例,如 –XX:NewRatio=2,則新生代佔整個堆空間的1/3,老年代佔2/3 -XX:SurvivorRatio 新生代中 Eden 與 Survivor 的比值。默認值爲 8。即 Eden 佔新生代空間的 8/10,另外兩個 Survivor 各佔 1/10
2)棧
每個線程執行每個方法的時候都會在棧中申請一個棧幀,每個棧幀包括局部變量區和操作數棧,用於存放此次方法調用過程中的臨時變量、參數和中間結果。
-xss:設置每個線程的堆棧大小. JDK1.5+ 每個線程堆棧大小爲 1M,一般來說如果棧不是很深的話, 1M 是絕對夠用了的。
3)本地方法棧
用於支持native方法的執行,存儲了每個native方法調用的狀態
4)方法區
存放了要加載的類信息、靜態變量、final類型的常量、屬性和方法信息。JVM用持久代(Permanet Generation)來存放方法區,可通過-XX:PermSize和-XX:MaxPermSize來指定最小值和最大值
-------------------------------------------------------------------------------------------------------------------------------------
JVM垃圾回收
JVM垃圾回收策略:引用計數、標記-清除、複製、標記-整理
JVM垃圾回收器:串行收集器、並行收集器、併發收集器
四、JVM內存調優
對JVM內存的系統級的調優主要的目的是減少GC的頻率和Full GC的次數,過多的GC和Full GC是會佔用很多的系統資源(主要是CPU),影響系統的吞吐量。
新生代設置過小會導致如下後果
1、新生代GC次數頻繁,增大系統消耗
2、導致大對象直接進入老年代,佔據老年代剩餘空間,易誘發Full GC
新生代設置過大會導致如下後果
1、導致老年代過小(heap總量一定),易誘發Full GC
2、新生代GC耗時大幅度增加
一般說來新生代佔整個堆1/3比較合適
Survivor設置過小會導致如下後果
導致對象從eden直接到達老年代,降低了在新生代的存活時間
Survivor設置過大會導致如下後果
導致eden過小,增加了GC頻率
當以吞吐量優先時的調優策略:
JVM以吞吐量爲指標,自行選擇相應的GC策略及控制新生代與舊生代的大小比例,來達到吞吐量指標。這個值可由-XX:GCTimeRatio=n來設置
當以暫停時間優先時的調優策略:
JVM以暫停時間爲指標,自行選擇相應的GC策略及控制新生代與舊生代的大小比例,儘量保證每次GC造成的應用停止時間都在指定的數值範圍內完成。這個值可由-XX:MaxGCPauseRatio=n來設置
例子:設置tomcat內存大小
直接在catalina.sh腳本最前方增加 JAVA_OPTS='-Xms1024m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m' 將xms和xmx設置一樣大,省着經常內存回收