public class SystemGCTest {
public static void main(String[] args) {
new SystemGCTest();
System.gc();//提醒jvm的垃圾回收器執行gc,可是不肯定是否立刻執行gc
//與Runtime.getRuntime().gc();的做用同樣。
System.runFinalization();//強制調用使用引用的對象的finalize()方法
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("SystemGCTest 重寫了finalize()");
}
}
複製代碼
public class LocalVarGC {
public void localvarGC1() {
byte[] buffer = new byte[10 * 1024 * 1024];//10MB
System.gc();
//輸出: 不會被回收, FullGC時被放入老年代
//[GC (System.gc()) [PSYoungGen: 14174K->10736K(76288K)] 14174K->10788K(251392K), 0.0089741 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
//[Full GC (System.gc()) [PSYoungGen: 10736K->0K(76288K)] [ParOldGen: 52K->10649K(175104K)] 10788K->10649K(251392K), [Metaspace: 3253K->3253K(1056768K)], 0.0074098 secs] [Times: user=0.01 sys=0.02, real=0.01 secs]
}
public void localvarGC2() {
byte[] buffer = new byte[10 * 1024 * 1024];
buffer = null;
System.gc();
//輸出: 正常被回收
//[GC (System.gc()) [PSYoungGen: 14174K->544K(76288K)] 14174K->552K(251392K), 0.0011742 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
//[Full GC (System.gc()) [PSYoungGen: 544K->0K(76288K)] [ParOldGen: 8K->410K(175104K)] 552K->410K(251392K), [Metaspace: 3277K->3277K(1056768K)], 0.0054702 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
}
public void localvarGC3() {
{
byte[] buffer = new byte[10 * 1024 * 1024];
}
System.gc();
//輸出: 不會被回收, FullGC時被放入老年代
//[GC (System.gc()) [PSYoungGen: 14174K->10736K(76288K)] 14174K->10784K(251392K), 0.0076032 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
//[Full GC (System.gc()) [PSYoungGen: 10736K->0K(76288K)] [ParOldGen: 48K->10649K(175104K)] 10784K->10649K(251392K), [Metaspace: 3252K->3252K(1056768K)], 0.0096328 secs] [Times: user=0.01 sys=0.01, real=0.01 secs]
}
public void localvarGC4() {
{
byte[] buffer = new byte[10 * 1024 * 1024];
}
int value = 10;
System.gc();
//輸出: 正常被回收
//[GC (System.gc()) [PSYoungGen: 14174K->496K(76288K)] 14174K->504K(251392K), 0.0016517 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
//[Full GC (System.gc()) [PSYoungGen: 496K->0K(76288K)] [ParOldGen: 8K->410K(175104K)] 504K->410K(251392K), [Metaspace: 3279K->3279K(1056768K)], 0.0055183 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
}
public void localvarGC5() {
localvarGC1();
System.gc();
//輸出: 正常被回收
//[GC (System.gc()) [PSYoungGen: 14174K->10720K(76288K)] 14174K->10744K(251392K), 0.0121568 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
//[Full GC (System.gc()) [PSYoungGen: 10720K->0K(76288K)] [ParOldGen: 24K->10650K(175104K)] 10744K->10650K(251392K), [Metaspace: 3279K->3279K(1056768K)], 0.0101068 secs] [Times: user=0.01 sys=0.02, real=0.01 secs]
//[GC (System.gc()) [PSYoungGen: 0K->0K(76288K)] 10650K->10650K(251392K), 0.0005717 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
//[Full GC (System.gc()) [PSYoungGen: 0K->0K(76288K)] [ParOldGen: 10650K->410K(175104K)] 10650K->410K(251392K), [Metaspace: 3279K->3279K(1056768K)], 0.0045963 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
}
public static void main(String[] args) {
LocalVarGC local = new LocalVarGC();
local.localvarGC5();
}
}
複製代碼
舉例java
測試代碼git
public class StopTheWorldDemo {
public static class WorkThread extends Thread {
List<byte[]> list = new ArrayList<byte[]>();
public void run() {
try {
while (true) {
for(int i = 0;i < 1000;i++){
byte[] buffer = new byte[1024];
list.add(buffer);
}
if(list.size() > 10000){
list.clear();
System.gc();//會觸發full gc,進而會出現STW事件
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public static class PrintThread extends Thread {
public final long startTime = System.currentTimeMillis();
public void run() {
try {
while (true) {
// 每秒打印時間信息
long t = System.currentTimeMillis() - startTime;
System.out.println(t / 1000 + "." + t % 1000);
Thread.sleep(1000);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
WorkThread w = new WorkThread();
PrintThread p = new PrintThread();
w.start();
p.start();
}
}
複製代碼
併發和並行,在談論垃圾收集器的上下文語境中,它們能夠解釋以下:github
並行(Parallel) :指多條垃圾收集線程並行工做,但此時用戶線程仍處於等待狀態。面試
串行(Serial)算法
併發(Concurrent) :指用戶線程與垃圾收集線程同時執行(但不必定是並行的,可能會交替執行),垃圾回收線程在執行時不會停頓用戶程序的運行。數據庫
如何在GC發生時,檢查全部線程都跑到最近的安全點停頓下來呢?數組
Safepoint機制保證了程序執行時,在不太長的時間內就會遇到可進入GC的Safepoint 。可是,程序「不執行」的時候呢?例如線程處於Sleep 狀態或Blocked狀態,這時候線程沒法響應JVM的中斷請求,「走」 到安全點去中斷掛起,JVM也不太可能等待線程被喚醒。對於這種狀況,就須要安全區域(Safe Region)來解決。
安全區域是指在一段代碼片斷中,對象的引用關係不會發生變化,在這個區域中的任何位置開始GC都是安全的。咱們也能夠把Safe Region 看作是被擴展了的Safepoint。緩存
實際執行時:安全
Reference子類中只有終結器引用是包內可見的,其餘3種引用類型均爲public,能夠在應用程序中直接使用bash
public class StrongReferenceTest {
public static void main(String[] args) {
StringBuffer str = new StringBuffer ("Hello,尚硅谷");
StringBuffer str1 = str;
str = null;
System.gc();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(str1);
}
}
複製代碼
StringBuffer str = new StringBuffer ("Hello,尚硅谷");
局部變量str指向StringBuffer實例所在堆空間,經過str能夠操做該實例,那麼str就是StringBuffer實例的強引用
對應內存結構:
此時,若是再運行一個賦值語句:
StringBuffer str1 = str;
對應內存結構:
本例中的兩個引用,都是強引用,強引用具有如下特色:
Object obj = new object(); //聲明強引用
SoftReference<0bject> sf = new SoftReference<0bject>(obj);
obj = null; //銷燬強引用
複製代碼
/**
* 軟引用的測試:內存不足即回收
* -Xms10m -Xmx10m -XX:+PrintGCDetails
*/
public class SoftReferenceTest {
public static class User {
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int id;
public String name;
@Override
public String toString() {
return "[id=" + id + ", name=" + name + "] ";
}
}
public static void main(String[] args) {
//建立對象,創建軟引用
// SoftReference<User> userSoftRef = new SoftReference<User>(new User(1, "songhk"));
//上面的一行代碼,等價於以下的三行代碼
User u1 = new User(1,"songhk");
SoftReference<User> userSoftRef = new SoftReference<User>(u1);
u1 = null;//取消強引用
//從軟引用中從新得到強引用對象
System.out.println(userSoftRef.get());
System.gc();
System.out.println("After GC:");
// //垃圾回收以後得到軟引用中的對象
System.out.println(userSoftRef.get());//因爲堆空間內存足夠,全部不會回收軟引用的可達對象。
//
try {
//讓系統認爲內存資源緊張、不夠
// byte[] b = new byte[1024 * 1024 * 7];
byte[] b = new byte[1024 * 7168 - 399 * 1024];//剛好能放下數組又放不下u1的內存分配大小 不會報OOM
} catch (Throwable e) {
e.printStackTrace();
} finally {
//再次從軟引用中獲取數據
System.out.println(userSoftRef.get());//在報OOM以前,垃圾回收器會回收軟引用的可達對象。
}
}
}
複製代碼
Object obj = new object(); //聲明強引用
WeakReference<0bject> sf = new WeakReference<0bject>(obj);
obj = null; //銷燬強引用
複製代碼
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {...}
public class WeakReferenceTest {
public static class User {
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int id;
public String name;
@Override
public String toString() {
return "[id=" + id + ", name=" + name + "] ";
}
}
public static void main(String[] args) {
//構造了弱引用
WeakReference<User> userWeakRef = new WeakReference<User>(new User(1, "songhk"));
//從弱引用中從新獲取對象
System.out.println(userWeakRef.get());
System.gc();
// 無論當前內存空間足夠與否,都會回收它的內存
System.out.println("After GC:");
//從新嘗試從弱引用中獲取對象
System.out.println(userWeakRef.get());
}
}
複製代碼
object obj = new object();
ReferenceQueuephantomQueue = new ReferenceQueue( ) ;
PhantomReference<object> pf = new PhantomReference<object>(obj, phantomQueue);
obj = null;
複製代碼
public class PhantomReferenceTest {
public static PhantomReferenceTest obj;//當前類對象的聲明
static ReferenceQueue<PhantomReferenceTest> phantomQueue = null;//引用隊列
public static class CheckRefQueue extends Thread {
@Override
public void run() {
while (true) {
if (phantomQueue != null) {
PhantomReference<PhantomReferenceTest> objt = null;
try {
objt = (PhantomReference<PhantomReferenceTest>) phantomQueue.remove();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (objt != null) {
System.out.println("追蹤垃圾回收過程:PhantomReferenceTest實例被GC了");
}
}
}
}
}
@Override
protected void finalize() throws Throwable { //finalize()方法只能被調用一次!
super.finalize();
System.out.println("調用當前類的finalize()方法");
obj = this;
}
public static void main(String[] args) {
Thread t = new CheckRefQueue();
t.setDaemon(true);//設置爲守護線程:當程序中沒有非守護線程時,守護線程也就執行結束。
t.start();
phantomQueue = new ReferenceQueue<PhantomReferenceTest>();
obj = new PhantomReferenceTest();
//構造了 PhantomReferenceTest 對象的虛引用,並指定了引用隊列
PhantomReference<PhantomReferenceTest> phantomRef = new PhantomReference<PhantomReferenceTest>(obj, phantomQueue);
try {
//不可獲取虛引用中的對象
System.out.println(phantomRef.get());
//將強引用去除
obj = null;
//第一次進行GC,因爲對象可復活,GC沒法回收該對象
System.gc();
Thread.sleep(1000);
if (obj == null) {
System.out.println("obj 是 null");
} else {
System.out.println("obj 可用");
}
System.out.println("第 2 次 gc");
obj = null;
System.gc(); //一旦將obj對象回收,就會將此虛引用存放到引用隊列中。
Thread.sleep(1000);
if (obj == null) {
System.out.println("obj 是 null");
} else {
System.out.println("obj 可用");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
複製代碼
輸出
null
調用當前類的finalize()方法
obj 可用
第 2 次 gc
追蹤垃圾回收過程:PhantomReferenceTest實例被GC了
obj 是 null
複製代碼
【代碼】
github.com/willShuhuan…
【筆記】
JVM_01 簡介
JVM_02 類加載子系統
JVM_03 運行時數據區1- [程序計數器+虛擬機棧+本地方法棧]
JVM_04 本地方法接口
JVM_05 運行時數據區2-堆
JVM_06 運行時數據區3-方法區
JVM_07 運行時數據區4-對象的實例化內存佈局與訪問定位+直接內存
JVM_08 執行引擎(Execution Engine)
JVM_09 字符串常量池StringTable
JVM_10 垃圾回收1-概述+相關算法
JVM_11 垃圾回收2-垃圾回收相關概念
JVM_12 垃圾回收3-垃圾回收器