1、JVM概述java
JVM (JAVA 虛擬機),定義了一套編譯,加載,解釋執行JAVA代碼的規範,算法
基於這套規範市場上不一樣產品實現,例如Hotspot,JRockit,J9等.安全
其簡易內存體系結構以下:數據結構
2、堆的內存劃分:工具
Java堆的內存劃分如圖所示,分別爲年輕代、Old Memory(老年代)、Perm(永久代)。其中在Jdk1.8中,永久代被移除,使用MetaSpace代替。
一、新生代:
(1)使用複製清除算法(Copinng算法),緣由是年輕代每次GC都要回收大部分對象。新生代裏面分紅一份較大的Eden空間和兩份較小的Survivor空間。每次只使用Eden和其中一塊Survivor空間,而後垃圾回收的時候,把存活對象放到未使用的Survivor(劃分出from、to)空間中,清空Eden和剛纔使用過的Survivor空間。
(2)分爲Eden、Survivor From、Survivor To,比例默認爲8:1:1
(3)內存不足時發生Minor GC
二、老年代:
(1)採用標記-整理算法(mark-compact),緣由是老年代每次GC只會回收少部分對象。
三、Perm:用來存儲類的元數據,也就是方法區。
(1)Perm的廢除:在jdk1.8中,Perm被替換成MetaSpace,MetaSpace存放在本地內存中。緣由是永久代進場內存不夠用,或者發生內存泄漏。
(2)MetaSpace(元空間):元空間的本質和永久代相似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用本地內存。性能
3、GC垃圾回收:優化
常見的垃圾回收算法: spa
一、Mark-Sweep(標記-清除算法):
(1)思想:標記清除算法分爲兩個階段,標記階段和清除階段。標記階段任務是標記出全部須要回收的對象,清除階段就是清除被標記對象的空間。
(2)優缺點:實現簡單,容易產生內存碎片
二、Copying(複製清除算法):
(1)思想:將可用內存劃分爲大小相等的兩塊,每次只使用其中的一塊。當進行垃圾回收的時候了,把其中存活對象所有複製到另一塊中,而後把已使用的內存空間一次清空掉。
(2)優缺點:不容易產生內存碎片;可用內存空間少;存活對象多的話,效率低下。
三、Mark-Compact(標記-整理算法):
(1)思想:先標記存活對象,而後把存活對象向一邊移動,而後清理掉端邊界之外的內存。
(2)優缺點:不容易產生內存碎片;內存利用率高;存活對象多而且分散的時候,移動次數多,效率低下線程
四、分代收集算法:(目前大部分JVM的垃圾收集器所採用的算法):3d
思想:把堆分紅新生代和老年代。(永久代指的是方法區)
(1) 由於新生代每次垃圾回收都要回收大部分對象,因此新生代採用Copying算法。新生代裏面分紅一份較大的Eden空間和兩份較小的Survivor空間。每次只使用Eden和其中一塊Survivor空間,而後垃圾回收的時候,把存活對象放到未使用的Survivor(劃分出from、to)空間中,清空Eden和剛纔使用過的Survivor空間。
(2) 因爲老年代每次只回收少許的對象,所以採用mark-compact算法。
(3) 在堆區外有一個永久代。對永久代的回收主要是無效的類和常量。
幾種不一樣的垃圾回收類型:
(1)Minor GC:從年輕代(包括Eden、Survivor區)回收內存。
(2)Major GC:清理整個老年代,當eden區內存不足時觸發。
(3)Full GC:清理整個堆空間,包括年輕代和老年代。當老年代內存不足時觸發。
4、JVM優化
一、通常來講,當survivor區不夠大或者佔用量達到50%,就會把一些對象放到老年區。經過設置合理的eden區,survivor區及使用率,能夠將年輕對象保存在年輕代,從而避免full GC,使用-Xmn
設置年輕代的大小
二、對於佔用內存比較多的大對象,通常會選擇在老年代分配內存。若是在年輕代給大對象分配內存,年輕代內存不夠了,就要在eden區移動大量對象到老年代,而後這些移動的對象可能很快消亡,所以致使full GC。經過設置參數:-XX:PetenureSizeThreshold=1000000
,單位爲B,標明對象大小超過1M時,在老年代(tenured)分配內存空間。
三、通常狀況下,年輕對象放在eden區,當第一次GC後,若是對象還存活,放到survivor區,此後,每GC一次,年齡增長1,當對象的年齡達到閾值,就被放到tenured老年區。這個閾值能夠同構-XX:MaxTenuringThreshold
設置。若是想讓對象留在年輕代,能夠設置比較大的閾值。
四、設置最小堆和最大堆:-Xmx
和-Xms
穩定的堆大小堆垃圾回收是有利的,得到一個穩定的堆大小的方法是設置-Xms和-Xmx的值同樣,即最大堆和最小堆同樣,若是這樣子設置,系統在運行時堆大小理論上是恆定的,穩定的堆空間能夠減小GC次數,所以,不少服務端都會將這兩個參數設置爲同樣的數值。穩定的堆大小雖然減小GC次數,可是增長每次GC的時間,由於每次GC要把堆的大小維持在一個區間內。
五、一個不穩定的堆並不是毫無用處。在系統不須要使用大內存的時候,壓縮堆空間,使得GC每次應對一個較小的堆空間,加快單次GC次數。基於這種考慮,JVM提供兩個參數,用於壓縮和擴展堆空間。
(1)-XX:MinHeapFreeRatio
參數用於設置堆空間的最小空閒比率。默認值是40,當堆空間的空閒內存比率小於40,JVM便會擴展堆空間
(2)-XX:MaxHeapFreeRatio
參數用於設置堆空間的最大空閒比率。默認值是70, 當堆空間的空閒內存比率大於70,JVM便會壓縮堆空間。
(3)當-Xmx和-Xmx相等時,上面兩個參數無效
六、經過增大吞吐量提升系統性能,能夠經過設置並行垃圾回收收集器。
(1)-XX:+UseParallelGC
:年輕代使用並行垃圾回收收集器。這是一個關注吞吐量的收集器,能夠儘量的減小垃圾回收時間。
(2)-XX:+UseParallelOldGC
:設置老年代使用並行垃圾回收收集器。
七、嘗試使用大的內存分頁:使用大的內存分頁增長CPU的內存尋址能力,從而系統的性能。-XX:+LargePageSizeInBytes
設置內存頁的大小
八、使用非佔用的垃圾收集器。-XX:+UseConcMarkSweepGC
老年代使用CMS收集器下降停頓。
九、-XXSurvivorRatio=3
,表示年輕代中的分配比率:survivor:eden = 2:3
十、JVM性能調優的工具:
(1)jps(Java Process Status):輸出JVM中運行的進程狀態信息(如今通常使用jconsole)
(2)jstack:查看java進程內線程的堆棧信息。
(3)jmap:用於生成堆轉存快照
(4)jhat:用於分析jmap生成的堆轉存快照(通常不推薦使用,而是使用Ecplise Memory Analyzer)
(3)jstat:是JVM統計監測工具。能夠用來顯示垃圾回收信息、類加載信息、新生代統計信息等。
(4)VisualVM:故障處理工具
5、類加載機制
一、能夠在elipse類中右鍵Run configurations-->Arguments-->VM Arguments中設置參數:
-XX:+TraceClassLoading ----查看類的加載順序
-XX:MetaspaceSize=2m ----設置metaspace的大小
-XX:MaxMetaspaceSize=10m ----設置metaspace大小的最大值
二、每個類的最早加載的三個類
三、類加載器把class文件中的二進制數據讀入到內存中,存放在方法區,而後在堆區建立一個java.lang.Class對象,用來封裝類在方法區內的數據結構。類加載的步驟以下:
加載:查找並加載類的二進制數據(把class文件裏面的信息加載到內存裏面)
鏈接:把內存中類的二進制數據合併到虛擬機的運行時環境中
(1)驗證:確保被加載的類的正確性。包括:
A、類文件的結構檢查:檢查是否知足Java類文件的固定格式 B、語義檢查:確保類自己符合Java的語法規範 C、字節碼驗證:確保字節碼流能夠被Java虛擬機安全的執行。字節碼流是操做碼組成的序列。每個操做碼後面都會跟着一個或者多個操做數。字節碼檢查這個步驟會檢查每個操做碼是否合法。 D、二進制兼容性驗證:確保相互引用的類之間是協調一致的。
(2)準備:爲類的靜態變量分配內存,並將其初始化爲默認值
(3)解析:把類中的符號引用轉化爲直接引用(好比說方法的符號引用,是有方法名和相關描述符組成,在解析階段,JVM把符號引用替換成一個指針,這個指針就是直接引用,它指向該類的該方法在方法區中的內存位置)
初始化:爲類的靜態變量賦予正確的初始值。當靜態變量的等號右邊的值是一個常量表達式時,不會調用static代碼塊進行初始化。只有等號右邊的值是一個運行時運算出來的值,纔會調用static初始化。
四、常見的三種類加載器:
AppClassLoader--應用類加載器,負責加載咱們本身寫的類
ExtClassLoader--擴展類加載器,負責加載擴展包(jre\lib\ext\*.jar)
BootstrapClassLoader--根類加載器,負責加載核心包(jre\lib\rt.jar)
五、類加載類的兩種方式----顯式加載和隱式加載
(1)顯式加載
1 class classA{ 2 //類加載時能夠執行靜態代碼塊,但不必定會執行 3 static { 4 System.out.println(11); 5 } 6 } 7 8 //不會執行靜態代碼塊 9 loader.loadClass("cn.shizhe.ClassLoader.classA"); 10 //會執行靜態代碼塊 11 Class.forName("cn.shizhe.ClassLoader.classA",true,loader); 12 //不會執行靜態代碼塊 13 Class.forName("cn.shizhe.ClassLoader.classA",false,loader);
(2)隱式加載的時機:--默認都會進行初始化
訪問類的靜態屬性時?(分狀況)
訪問static final修飾的八種基本數據類型和字符串時不會觸發類的加載
訪問static 修飾的任意屬性時都會觸發類的加載
訪問類的靜態方法
構建類的對象時
六、類的被動加載與主動加載
1 class class1{ 2 static int a = 100; 3 static { 4 System.out.println("class1.static"); 5 } 6 } 7
8 class class2 extends class1{ 9 static { 10 System.out.println("class2.static"); 11 } 12 } 13 public class TestClassObject08 { 14 public static void main(String[] args) { 15 //class1爲主動加載,class2爲被動加載(不執行static初始化操做)
16 System.out.println(class2.a); 17 } 18 }