本文主要介紹JVM和GC解析
本文較長,分爲上下篇(可收藏,勿吃塵)
若有須要,能夠參考
若有幫助,不忘 點贊 ❥java
一文理清JVM和GC下篇
web
其中方法區和堆被JVM中多個
線程共享
,好比類的靜態常量就被存放在方法區,供類對象之間共享,虛擬機棧、本地方法棧、程序計數器是每一個線程獨立
擁有的,不會與其餘線程共享。因此Java在經過new建立一個類對象實例的時候,一方面會在虛擬機棧中建立一個對該對象的引用,另外一方面會在堆上建立類對象的實例,而後將對象引用指向該對象的實例。對象引用存放在每個方法對應的棧幀中。算法
虛擬機棧:
虛擬機棧中執行每一個方法的時候,都會建立一個棧幀用於存儲局部變量表,操做數棧,動態連接,方法出口等信息。本地方法棧:
與虛擬機棧發揮的做用類似,相比於虛擬機棧爲Java方法服務,本地方法棧爲虛擬機使用的Native方法服務,執行每一個本地方法的時候,都會建立一個棧幀用於存儲局部變量表,操做數棧,動態連接,方法出口等信息。方法區:
它用於存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據,方法區在JDK1.7版本及以前稱爲永久代,從JDK1.8以後永久代被移除。堆:
堆是Java對象的存儲區域,任何new字段分配的Java對象實例和數組,都被分配在了堆上,Java堆可以使用 - Xms / - Xmx 進行內存控制,從JDK1.7版本以後,運行時常量池從方法區移到了堆上。程序計數器:
指示Java虛擬機下一條須要執行的字節碼指令。從圖中咱們能夠看出JAVA8的JVM 用元空間取代了永久代
數組
JVM的實現通常不採用這種方式緩存
缺點:
1. 每次對對象賦值時均要維護引用計數器,且計數器自己也有必定的消耗;
2. 較難處理循環引用;網絡
Java 堆從GC的角度能夠細分爲:新生代(Eden區、From Survivor區 和 To Survivor區)和 老年代。
特色:
複製算法不會產生內存碎片,但會佔用空間。用於新生代。app
算法分紅標記和清除兩個階段,先標記出要回收的對象,而後統一回收這些。
特色:
不會佔用額外空間,但會掃描兩次,耗時,容易產生碎片,用於老年代jvm
優勢:
沒有內存碎片,能夠利用bump
缺點:
須要移動對象的成本,用於老年代
原理:post
- 標記:與標記清除同樣
2.壓縮:再次掃描,並往一段滑動存活對象
Java中,引用和對象是有關聯的。若是要操做對象則必須用引用進行。
所以,很顯然的一個方法就是經過引用計數來判斷一個對象是否能夠回收。簡單來講就是給對象添加一個引用計數器。每當有一個地方引用它,計數器的值加1,每當有一個引用失效時,計數器的值減1。
任什麼時候刻計數器值爲0的對象就是不可能再被使用的,那麼這個對象就是可回收對象。
缺點: 很難解決對象之間相互循環引用的問題性能
所謂「GC roots」 或者說tracing GC 的 "根集合" 就是一組必須活躍的引用。
基本思路就是經過一系列名爲「GC Roots」 的對象做爲起始點,從這個被稱爲GC Roots的對象開始向下搜索,如GC Roots沒有任何引用鏈相連是,則說明此對象不可用。也即給定一個集合的引用做爲根出發,經過引用關係
標配參數
X參數
XX參數
兩個經典參數
-XX:+PrintFlagsInitial
查看默認初始值-XX:+PrintFlagsFinal
查看修改更新
經典案例設置:
-Xms128m -Xmx4096m -Xss1024k -XX:Metaspacesize=512m -XX:+PrintCommandLineFlags -XX:PrintGCDetails -XX:UseSerialGC
-Xms
初始化大小內存,默認爲物理內存1/64
等價於 -XX:InitialHeapSize
-Xmx
最大分配內存,默認爲物理內存1/4
等價於 -XX:MaxHeapSize
-Xss
設置單個線程的大小,通常默認爲5112K~1024K
等價於 -XX:ThreadStackSize
-Xmn
設置年輕代大小
-XX:MetaspaceSize
設置元空間大小
-XX:+PrintGCdetails
輸出詳細的GC收集日誌信息
-XX:SurvivorRatio
設置新生代中eden和S0/S1空間的比例
默認:
-XX:SurvivorRatio=8 --> Eden:S0:S1=8:1:1
修改:
-XX:SurvivorRatio=4 --> Eden:S0:S1=4:1:1
SurvivorRatio值就是設置eden區的比例佔多少,S0/S1相同
-XX:NewRatio
設置年輕代與老年代在堆結構的佔比
默認
-XX:NewRatio=2 新生代佔1,老年代佔2,年輕代佔整個堆的1/3
修改
-XX:NewRatio=4 新生代佔1,老年代佔4,年輕代佔整個堆的1/5
NewRatio值就是設置老年代的佔比,剩下的1給新生代
-XX:MaxTenuringThreshold
設置垃圾最大年齡
-XX:MaxTenuringThreshold=0:設置垃圾最大年齡。若是設置爲0的話,則年輕代對象不通過Survivor區,直接進入老年代。對於老年代比較多的應用,能夠提升效率。若是將此值設置爲一個較大值,則年輕代對象會在Survivor區進行屢次複製,這樣能夠增長對象在年輕代的存活時間,增長年輕代被回收的概論。
強引用
public static void main(String[] args) {
Object o1 = new Object(); //默認爲強引用
Object o2 = o1; //引用賦值
o1 = null; //置空 讓垃圾收集
System.gc();
System.out.println(o1); // null
System.out.println(o2); // java.lang.Object@1540e19d
}
複製代碼
軟引用
public static void main(String[] args) {
Object o1 = new Object();
SoftReference softReference = new SoftReference(o1);
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(softReference.get());
}
複製代碼
弱引用
public static void main(String[] args) {
Object o1 = new Object();
WeakReference weakReference = new WeakReference(o1);
o1 = null;
System.gc();
System.out.println(o1); //null
System.out.println(weakReference.get()); //null
}
複製代碼
虛引用
public static void main(String[] args) {
Object o1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(o1,referenceQueue);
System.out.println(o1); //java.lang.Object@1540e19d
System.out.println(phantomReference.get()); //null
System.out.println(referenceQueue.poll()); //null
}
複製代碼
擴展
public static void main(String[] args) {
WeakHashMap<Integer,String> weakHashMap = new WeakHashMap<>();
Integer key = new Integer(1);
weakHashMap.put(key,"測試1");
System.out.println(weakHashMap); //{1=測試1}
key=null;
System.out.println(weakHashMap); //{1=測試1}
System.gc();
System.out.println(weakHashMap+"\t"+weakHashMap.size()); //{} 0
}
複製代碼
本文較長,能看到這裏的都是好樣的,成長之路學無止境
今天的你多努力一點,明天的你就能少說一句求人的話!好久好久以前,有個傳說,聽說:
看完不讚,都是壞蛋