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(); } } 复制代码
举例
测试代码
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(); } } 复制代码
并发和并行,在谈论垃圾收集器的上下文语境中,它们可以解释如下:
并行(Parallel) :指多条垃圾收集线程并行工作,但此时用户线程仍处于等待状态。
串行(Serial)
并发(Concurrent) :指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),垃圾回收线程在执行时不会停顿用户程序的运行。
如何在GC发生时,检查所有线程都跑到最近的安全点停顿下来呢?
Safepoint机制保证了程序执行时,在不太长的时间内就会遇到可进入GC的Safepoint 。但是,程序“不执行”的时候呢?例如线程处于Sleep 状态或Blocked状态,这时候线程无法响应JVM的中断请求,“走” 到安全点去中断挂起,JVM也不太可能等待线程被唤醒。对于这种情况,就需要安全区域(Safe Region)来解决。
安全区域是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的。我们也可以把Safe Region 看做是被扩展了的Safepoint。
实际执行时:
Reference子类中只有终结器引用是包内可见的,其他3种引用类型均为public,可以在应用程序中直接使用
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 复制代码