複製->清空->互換java
MaxTenuringThreshold
決定,默認參數是15),最終若是仍是存活,就存入老年代。垃圾:內存中已經再也不被使用到的空間就是垃圾。linux
可達性分析算法:基本思路是經過一系列名爲 GC Roots 的對象做爲起始點,從這些對象開始向下搜索,若是一個對象到 GC Roots 沒有任何引用鏈相連,說明此對象不可用。ios
GC Roots 是一組必須活躍的引用。算法
能夠做爲 GC Roots 的對象的有:緩存
1. 標配參數:在 jdk 各個版本之間保持穩定bash
- java -version
- java -help
- java -showversion
複製代碼
2. X參數服務器
-Xint:解釋執行
-Xcomp: 第一次使用就編譯成本地代碼
-Xmixed: 默認,先編譯後執行
複製代碼
3. XX參數網絡
Boolean 類型多線程
-XX:+/- 屬性
首先使用 jps -l
查看當前運行進程 id併發
而後使用 jinfo -flag PrintGCDetails 進程id
來查看
若是開啓,結果應該是-XX:+PrintGCDetails
,不然-XX:-PrintGCDetails
KV類型
-XX:key=value
jinfo -flag MetaspaceSize 進程id
來查看。-Xms
等價於-XX:InitialHeapSize
,-Xmx
等價於-XX:MaxHeapSize
java -XX:+PrintFlagsInital
查看jvm初始參數java -XX:+PrintFlagsFinal -version
查看修改更新的參數。參數中使用 =
說明是未修改的參數,使用:=
說明是人爲或jvm修改過的參數。java -XX:+PrintCommandLineFlags
JDK 1.8 以後永久代取消了,由元空間取代。新生代由 Eden + SurvivorFrom + SurvivorTo 組成,大小用-Xmn
設置。JVM 堆由新生代和老年代共同組成,由-Xms
和-Xmx
設置大小。元空間再也不屬於堆的一部分。
元空間和永久代的區別在於:永久代使用 JVM 的堆內存,元空間不在虛擬機中而是使用本機物理內存。
默認狀況下,元空間的大小僅受本地內存限制,類的元數據放入 native memory,字符串池和類的靜態變量放入 java 堆中,這樣能夠加載多少類的元數據就再也不由 MaxPermSize 控制,而由系統的實際可用空間來控制。
-Xms
-XX:InitialHeapSize
-Xmx
-XX:MaxHeapSize
-Xss
-XX:ThreadStackSize
-Xmn
-XX:MetaspaceSize
-XX:+PrintGCDetails
[GC [PSYoungGen: 2048K -> 496K(2560K)] 2048K->777K(9728k)]
複製代碼
[GC [PSYoungGen:
表示GC類型2048K
表示 YoungGC 前新生代內存佔用496K
表示 YoungGC 後新生代內存佔用(2560K)
表示新生代總大小2048K
表示 YoungGC 前 JVM 堆內存佔用777K
表示 YoungGC 後 JVM 堆內存佔用9728K
表示 JVM 堆總大小[Full GC (System) [PSYoungGen:3408K->0K(296688k)] [PSOldGen:0K->3363K(682688K)]3408K->3363K(981376K)[Metaspace:10638K->10638K(131072K)]]
複製代碼
能夠看到 Full GC 中,分別有 新生代、老年代、堆總內存以及元空間各自的GC前、GC後以及總大小。
-XX:SurvivorRatio
-XX:NewRatio
-XX:MaxTenuringThreshold
SoftReference, WeakReference 和 PhantomReference 三個類都繼承自 Reference 類,而 Reference 類又是 Object 類的子類。
當內存不足,JVM 開始垃圾回收,可是強引用的對象,即便出現了 OOM 也不會對該對象進行回收。把一個對象賦給一個引用變量,這個引用變量就是一個強引用,只要還有強引用指向一個對象,該對象就處於可達狀態,不會被 JVM 回收。
對於只有軟引用的對象來講,系統充足時不會被回收,系統內存不足時會被回收。軟引用一般用在對內存敏感的程序中,好比高速緩存就用到軟引用。
適用場景(緩存):假設有一個應用大量讀取本地圖片,每次讀取都會IO影響性能,一次性所有加載到內存可能會內存溢出。
可使用 HashMap 保存圖片的路徑和圖片對象關聯的軟引用之間的映射關係,內存不足時, JVM 會自動回收這些緩存圖片對象所佔用的空間,有效避免了OOM的問題。
Map<String, SoftReference<Bitmap>> imageCache = new HashMap<>();
複製代碼
只要GC一運行,無論JVM的內存空間是否足夠,都會回收該對象佔用的內存。
相比於 HashMap, WeakHashMap 中的元素,當 key 被回收後,Map 中相應的鍵值對將再也不存在。
public class WeakHashMapDemo {
public static void main(String[] args) {
myHashMap();
System.out.println("======================");
myWeakHashMap();
}
private static void myWeakHashMap() {
WeakHashMap<Integer,String> map=new WeakHashMap<>();
Integer k=new Integer(1);
String v="str";
map.put(k,v);
System.out.println(map);
k=null;
System.out.println(map);
System.gc();
System.out.println(map);
}
private static void myHashMap() {
HashMap<Integer,String> map=new HashMap<>();
Integer k=new Integer(1);
String v="str";
map.put(k,v);
System.out.println(map);
k=null;
System.out.println(map);
System.gc();
System.out.println(map);
}
}
複製代碼
運行結果
{1=str}
{1=str}
{1=str}
======================
{1=str}
{1=str}
{}
複製代碼
public class ReferenceQueueDemo {
public static void main(String[] args) throws InterruptedException {
Object o1=new Object();
ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
WeakReference<Object> weakReference=new WeakReference<>(o1,referenceQueue);
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
System.out.println();
o1=null;
System.gc();
Thread.sleep(1000);
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
}
}
複製代碼
java.lang.Object@1b6d3586
java.lang.Object@1b6d3586
null
null
null
java.lang.ref.WeakReference@4554617c
複製代碼
弱引用、軟引用、虛引用在 gc 以前,會被放到引用隊列中。
虛引用顧名思義就是形同虛設,它並不會決定對象的聲明週期。若是一個對象僅僅持有虛引用,就和沒有引用同樣,隨時可能被回收。虛引用不能單獨使用,也不能經過它訪問對象,必須和引用隊列聯合使用。
虛引用的主要做用是跟蹤對象被垃圾回收的狀態,僅僅是提供了一種確保對象被 finalize 之後,作某些事情的機制。其意義在於說明一個對象已經進入 finalization 階段,能夠被回收,用來實現比 finalization 機制更加靈活的回收操做。
設置虛引用的惟一目的,就是在這個對象被回收的時候收到一個系統通知或者後序添加進一步的處理。
public class PhantomReferenceDemo {
public static void main(String[] args) throws InterruptedException {
Object o1=new Object();
ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
PhantomReference<Object> phantomReference=new PhantomReference<>(o1,referenceQueue);
System.out.println(o1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
System.out.println();
o1=null;
System.gc();
Thread.sleep(1000);
System.out.println(o1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
}
}
複製代碼
運行結果:
java.lang.Object@1b6d3586
null
null
null
null
java.lang.ref.PhantomReference@4554617c
複製代碼
能夠看到,虛引用的 get() 返回值永遠是 null。
在建立引用的時候能夠指定關聯的隊列,當 GC 釋放對象內存的時候,會將引用加入到引用隊列。若是程序發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起必要行動,至關於一種通知機制。經過這種方式,JVM容許咱們在對象被銷燬後,作一些咱們想作的事情。
當遞歸調用中,棧的調用深度過深,致使棧溢出錯誤。 繼承關係以下:
Throwable -> Error -> VirtualMachineError -> StackoverflowError
複製代碼
當對象過多,或者存在大對象等狀況下,致使堆內容超過堆的最大尺寸,致使堆溢出。
GC回收時間過程會拋出此類異常。過長的定義是超過 98% 的時間用來作 GC 而且回收了不到 2% 的堆內存。連續屢次 GC 都只回收了不到 2% 的極端狀況下才會拋出。
假如不拋出 GC overhead limit 錯誤會發生的狀況: GC 清理出的內存很快再次被填滿,破事 GC 再次執行,造成惡性循環,CPU 使用率一直是 100%,可是 GC 卻沒有任何成果。
public class GCOverheadDemo {
public static void main(String[] args) {
int i = 0;
List<String> list = new ArrayList<>();
while (true) {
list.add(String.valueOf(++i).intern());
}
}
}
複製代碼
寫 NIO 程序常用 ByteBuffer 來讀取或者寫入數據。它可使用 Native 函數庫直接分配堆外內存,而後經過一個存儲在 Java 堆裏邊的 DirectByteBuffer 對象做爲這塊內存的引用進行操做。這樣在一些場景能顯著提升性能,由於避免了在 Java 堆和 Native 堆中來回複製數據。
ByteBuffer.allocate(capability)
是分配JVM堆內存,屬於GC管轄範圍,因爲須要拷貝因此速度相對較慢。
ByteBuffer.allocateDirect(capability)
是分配 OS 本地內存,不屬於 GC 管轄範圍,因爲不須要內存拷貝因此速度相對較快。
若是不斷分配本地內存,堆內存不多使用,那麼 JVM 就不須要執行 GC,DirectByteBuffer 對象就不會被回收,致使堆內存充足,可是本地內存已經被用光了。
在高併發請求服務器時,會出現該異常。
致使的緣由:
解決辦法:
MetaSpace 是方法區在 HotSpot 中的實現,它並不在虛擬機內存中而是使用本地內存,也就是類的元數據被存儲在 MetaSpace 的 native memory 中。
其中存放了:虛擬機加載的類的信息,常量池,靜態變量和即時編譯的代碼。
串行垃圾回收器,爲單線程環境設計且只使用一個線程進行垃圾回收,會暫停全部的用戶線程,因此不適合服務器環境。
過程就是:程序運行->單GC線程->程序運行
並行垃圾回收器,多個垃圾收集線程並行工做,此時用戶線程是暫停的,適用於科學計算、大數據處理等弱交互場景。
過程:程序運行->多GC線程並行運行->程序運行
併發垃圾回收器,用戶線程和GC線程同時執行,不須要停頓用戶線程,適用於對響應時間有要求的場景(強交互)。
過程:初始標記(暫停,單線程)->併發標記(GC線程和用戶線程同時執行)->最終標記(暫停,多GC線程)->清除
G1垃圾回收器將堆內存分割成不一樣的區域(Region)而後併發的對其進行垃圾回收。
使用java -XX:+PrintCommandLineFlags -version
查看默認的垃圾回收器。
在 java8 中,默認的是 -XX:+UseParallelGC
。
java 的 gc 類型有如下幾種:
在 jvm 中的 7 種垃圾回收器中,Serial Old 已經被廢棄,因此在 gc 的源代碼中,有如上六種。
在新生代的有:
在老年代的有:
兩者均可以使用的是 G1。
DefNew: Default New Generation
Tenured: Old
ParNew: Parallel New Generation
PSYoungGen: Parallel Scavenge
ParOldGen: Parallel Old Generation
複製代碼
一個單線程的收集器,在GC的時候,必須暫停其餘全部的工做線程直到 GC 結束。
在單CPU環境下,沒有線程交互的開銷能夠得到最高的GC效率,所以 Serial 垃圾收集器是 JVM 在 Client 模式下默認的新生代垃圾收集器。JVM參數:-XX:+UseSerialGC
開啓後,新生代使用 Serial 老年代使用 Serial Old,新生代和老年代都使用串行回收收集器,新生代使用複製算法,老年代使用標記-整理算法。
GC 日誌中,新生代使用DefNew
,老年代使用Tenured
。
使用多線程進行垃圾回收,垃圾收集時,會 STW 暫停其餘線程直到 GC 結束。
ParNew 收集器是 Serial 的並行多線程版本,常見的應用場景是配合老年代的 CMS 工做。是不少 jvm 運行在 Server 模式下的 新生代的默認垃圾收集器。JVM 參數:-XX:+UseParNewGC
。啓用後,新生代使用 ParNew ,老年代使用 Serial Old,新生代使用複製算法,老年代使用標記-整理算法。 要注意的是, ParNew + Serial Old 已經再也不被推薦。
GC 日誌中,新生代使用ParNew
,老年代使用Tenured
。
Parallel Scavenge 收集器相似於 ParNew 也是一個新生代垃圾收集器,使用複製算法,是並行的多線程的垃圾收集器,是吞吐量優先的收集器。
它重點關注的是吞吐量(運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間))。高吞吐量意味着高效利用CPU的時間,適用於在後臺運算而不須要太多交互的任務。
Parallel Scavenge 和 ParNew 收集器的一個重要區別是自適應調節策略。自適應調節策略是指虛擬機會根據當前系統的運行狀況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或最大的吞吐量。
JVM 參數:-XX:+UseParallelGC
,與使用-XX:+UseParallelOldGC
效果相同,這兩個收集器默認搭配使用,因此會互相激活。開啓後,新生代使用複製算法,老年代使用標記-整理算法。
啓用該收集器後,GC 日誌中,新生代輸出PSYoungGen
,老年代輸出ParOldGen
。
在 Java 8 中,該收集器爲默認的收集器。
Parallel Old 收集器是 Parallel Scavenge 的老年代版本,使用多線程的標記-整理算法,在 JDK 1.6開始提供。
在 JDK 1.6 以前,新生代使用 Parallel Scavenge 只能搭配老年代的 Serial Old,只能保證新生代的吞吐量優先。
在 JDK 1.8 及之後,默認搭配爲 Parallel Scavenge + Parallel Old ,保證了總體的吞吐量優先。
JVM 參數:-XX:+UseParallelOldGC
,開始後,新生代使用 Parallel Scavenge,老年代使用 Parallel Old。
啓用該收集器後,GC 日誌中,新生代輸出PSYoungGen
,老年代輸出ParOldGen
。
CMS(Councurrent Mark Sweep)是一種以獲取最短回收停頓時間爲目標的收集器,適合應用在互聯網或B/S系統的服務器上,這類應用中是服務器的相應速度,但願系統停頓時間最短。CMS 適合堆內存大、CPU 核數多的服務器端應用。
JVM 參數:-XX:+UseConcMarkSweepGC
,開啓該參數後會自動開啓-XX:+UseParNewGC
。使用 ParNew (新生代) + CMS + Serail Old 的收集器組合,其中 Serial Old 做爲 CMS 出錯的後備收集器。
因爲耗時最長的併發標記和併發清除過程當中,GC 線程和用戶一塊兒併發工做,因此整體上看 CMS 的 GC 和用戶線程一塊兒併發執行。
優勢:併發收集、停頓低。
缺點:
Serial Old 是 Serial 收集器的老年代版本,是個單線程收集器,使用標記-整理算法,是 Client 默認的老年代收集器。
在 Server 模式下:
-XX:+UseSerailGC
-XX:+UseParallelGC
或-XX:+UseParallelOldGC
-XX:+UseConcMarkSweepGC
參數 | 新生代垃圾收集器 | 新生代算法 | 老年代垃圾收集器 | 老年代算法 |
---|---|---|---|---|
-XX:+UseSerialGC |
SerialGC | 複製 | SerailOldGC | 標記整理 |
-XX:+UseParNewGC |
ParNew | 複製 | SerailOldGC | 標記整理 |
-XX:+UseParallelGC /-XX:+UseParallelOldGC |
Parallel Scavenge | 複製 | Parallel Old | 標記整理 |
-XX:+UseConcMarkSweepGC |
ParNew | 複製 | CMS+Serial Old | 標記清除 |
garbage-first heap+metaspace
G1 是一種服務器端的垃圾收集器,應用在多處理器和大容量內存環境中,在實現高吞吐量的同時,儘量的知足垃圾收集暫停時間的要求。而且具備以下特性:
G1 的設計目標是取代 CMS 收集器,同 CMS 相比:
G1 的主要改變是 Eden, Survivor 和 Tenured 等內存區域再也不是連續的,而是變成了一個個大小同樣的 region。
每一個 Region 大小範圍在 1MB-32MB,最多能夠設置 2048 個區域(默認也是2048),所以能支持的最大內存爲 64 GB。
G1 仍然屬於分代收集器。這些 Region 的一部分包含新生代,新生代的垃圾(Eden)收集採用 STW 的方式,將存活對象拷貝到老年代或者 Survivor 空間。Region 的另外一部分屬於老年代, G1 經過將對象從一個區域複製到另一個區域,完成清理工做,所以不會有 CMS 內存碎片問題的存在了。
在 G1 中,還有一種特殊的區域,稱爲 Humongous 區域,若是一個對象佔用的空間超過了 Region 的 50% 以上,就被認爲是巨型對象,會直接分配在老年代。G1 劃分了一個 Humongous 區,用來存放巨型對象。若是一個 Humongous Region 裝不下一個巨型對象,G1會尋找連續的 Humongous Region 來存儲,爲了能找到連續的 H 區,有時候不得不啓動 Full GC。
Young GC: 針對 Eden 區進行收集, Eden 區耗盡後被處罰,主要是小區域收集 + 造成連續的內存塊,避免內存碎片。
top
命令查看CPU 佔用率、內存佔用率以及第一行的 load averageuptime
命令,是系統性能命令的精簡版vmstat -n 2 3
表明每2秒採樣一次,一共採樣3次mpstat -P ALL 2
查看全部 CPU 核信息pidstat -u 1 -p pid
查看每一個進程使用 CPU 的用量分解信息free -m
來查看內存狀況,單位爲 mbpidstat -p pid -r 採樣間隔秒數
df -h
iostat -xdk 2 3
pidstat -d 採樣間隔秒 -p pid
ifstat 採樣間隔秒
top
命令找出 CPU 佔比最高的進程ps -ef
或者 jps
進一步定位該進程信息ps -mp pid -o THREAD,tid,time
jstack 進程id | grep tid -A60