玩轉Java虛擬機(十)

打卡學習JVM,第十天

本人學習過程中所整理的代碼,源碼地址

內存分配

在這裏插入圖片描述

- 堆上分配

  • 大多數情況在Eden上分配,偶爾會直接在old上分配
  • 細節取決於GC的實現

- 棧上分配

  • 原子類型的局部變量

內存回收

GC要做的就是將那些dead的對象所佔用的內存回收掉

  • Hotspot認爲沒有引用的對象是dead的
  • Hotspot將引用分爲四種:Strong(默認通過Object obj = new Object()這種方式賦值的引用)和Soft、Weak、Phantom(都繼承了Reference)

在Full GC時會對Reference類型的引用進行特殊處理

  • Soft:內存不夠時或者長期不用時一定會被GC
  • Weak:一定會被GC,當被mark爲dead,會在ReferenceQueue中通知
  • Phantom:本來就沒引用,當從JVM堆中釋放時會通知

GC的時機

在分代模型的基礎上,GC從時機上 分爲兩種:MinorGC和Full GC

- Minor GC

  • 觸發時機:新對象生產時,Eden空間滿了
  • 理論上Eden區大多數對象會在Minor GC回收,複製算法的執行效率會很高,Minor GC時間比較短

- Full GC

  • 對整個JVM進行整理,包括新生代,老年代,永久代
  • 主要觸發時機:1)老年代滿了 2)永久代滿了 3)System.gc()
  • 效率很低,儘量減少Full GC

GC要做的就是將那些dead的對象所佔用的內存回收掉

  • Hotspot認爲沒有引用的對象是dead的
  • Hotspot將引用分爲四種:Strong(默認通過Object obj = new Object()這種方式賦值的引用)和Soft、Weak、Phantom(都繼承了Reference)

在Full GC時會對Reference類型的引用進行特殊處理

  • Soft:內存不夠時或者長期不用時一定會被GC
  • Weak:一定會被GC,當被mark爲dead,會在ReferenceQueue中通知
  • Phantom:本來就沒引用,當從JVM堆中釋放時會通知

垃圾回收器在這裏插入圖片描述

  • 分代模型:GC的宏觀願景
  • 垃圾回收器:GC的具體實現
  • Hotspot JVM提供多種垃圾回收器,需要根據具體應用的需要採用不同的垃圾回收器
  • 沒有萬能的垃圾回收器

- 垃圾回收器的“並行”和“併發”

  • 並行(Parallel):指多個收集器的線程同時工作,但是用戶線程處於等待狀態
  • 併發(Concurrent):指收集器在工作的同時可以允許用戶線程工作

併發不代表解決了GC停頓的問題,在關鍵的步驟還是要停頓。比如在收集器標記垃圾的時候。但在清理垃圾的時候,用戶線程可以和GC線程併發執行

- Serial收集器

  • 最早的收集器單線程收集器,沒有多線程切換的額外開銷,簡單實用,收集時會暫停所有工作線程(Stop The World,STW),虛擬機運行在Client模式時的默認新生代收集器
  • 在新生代採用複製算法,在老年代採用標記-整理算法
  • Hotspot Client模式缺省的收集器
    在這裏插入圖片描述

- ParNew收集器

  • Serial的多線程版本
  • 對應的這種收集器是虛擬機運行在Server模式的默認新生代收集器,在單CPU的環境中性能和Serial收集器差不多
  • 使用複製算法
  • 可以通過-XX:ParallelGCThreads來控制GC線程數的多少,需要結合具體CPU的個數
  • Server模式下新生代的缺省收集器
    在這裏插入圖片描述

- Parallel Scavenge收集器

Parallel Scavenge 收集器是以吞吐量最大化(即GC時間佔總運行時間最小)爲目標的收集器實現,它允許較長時間的STW換取總吞吐量最大化

- Serial Old收集器

Serial Old是單線程收集器,使用標記-整理算法,是老年代的收集器

- Parallel Old收集器

老年代版本吞吐量優先收集器,使用多線程和標記-整理算法

  • Parallel Scavenge在老年代的實現
  • 在JVM1.6纔出現
  • 採用多線程,標記-整理算法
  • 更注重吞吐量
  • Parallel Scavenge + Parallel Old = 高吞吐量,但GC停頓可能不理想在這裏插入圖片描述

    - CMS(Concurrent Mark Sweep)收集器

CMS是一種以最短停頓時間爲目標的收集器,使用CMS並不能達到GC效率最高(總體GC時間最小),但它能儘可能降低GC時服務的停頓時間,CMS收集器使用的是標記-清除算法

  • 追求最短停頓時間,非常適合Web應用
  • 只針對老年代,一般結合ParNew使用
  • GC線程與用戶線程儘量併發工作
  • 標記-清除算法
  • 多CPU環境下才有意義
  • 使用-XX:+UseConcMarkSweepGC打開
  • 缺點:以犧牲CPU資源的代價來減少用戶線程的停頓;CMS併發清理過程中,由於用戶線程還在允許,因此需要預留一部分空間給用戶線程;碎片化問題
    在這裏插入圖片描述

Java內存泄漏的經典原因

- 對象定義在錯誤的範圍(Wrong Scope)

  • 如果Foo實例對象的生命較長,會導致臨時性內存泄漏(這裏的names變量其實只有臨時作用)
class Foo {
	private String[] names;
	public void doIt(int length) {
		if (names == null || names.length < length) {
			names = new String[length];
			populate(names);
			System.out.println(names);
		}
	}
}
  • JVM喜歡生命週期短的對象,這樣做已經足夠高效
class Foo {
	public void doIt(int length) {
		String[] names = new String[length];
		populate(names);
		System.out.println(names);
	}
}

- 異常(Exception)處理不當

Connection conn = DriverManager.getConnection(url,name,pwd);
try {
    String sql = "do a query sql";
    PreparedStatement stmt = conn.prepareStatement(sql);
    ResultSet rs = stmt.executeQuery();
    while (rs.next()){
        doSomeStuff();
    }
    rs.close();
    conn.close();
 }catch (Exception e){
 }

如果doSomeStuff()方法拋出異常,rs.close()和conn.close()不會被調用,因此會導致內存泄漏和數據庫連接泄漏

- 集合數據管理不當

  • 當使用基於數組的數據結構(ArrayList,HashMap等)時,儘量減少resize
  • 如果一個List只需要順序訪問,不需要隨機訪問,用LinkedList代替ArrayList,前者本質是鏈表,不需要resize

GC垃圾收集器的JVM參數定義

在這裏插入圖片描述
在這裏插入圖片描述

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