System.gc() 方法
代码示例:手动执行 GC 操作
代码示例 1
/**
* @author shkstart shkstart@126.com
* @create 2020 14:49
*/
public class SystemGCTest {
public static void main(String[] args) {
new SystemGCTest();
/*
提醒jvm的垃圾回收器执行gc,但是不确定是否马上执行gc
与Runtime.getRuntime().gc();的作用一样。
*/
//System.gc();
// 强制调用使用引用的对象的finalize()方法
System.runFinalization();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("SystemGCTest 重写了finalize()");
}
}
"C:\Program Files\Java\jdk1.8.0_144\bin\java"
SystemGCTest 重写了finalize()
Process finished with exit code 0
1234
"C:\Program Files\Java\jdk1.8.0_144\bin\java"
Process finished with exit code 0
手动 GC 理解不可达对象的回收行为
/**
* @author shkstart shkstart@126.com
* @create 2020 14:57
*/
public class LocalVarGC {
public void localvarGC1() {
byte[] buffer = new byte[10 * 1024 * 1024];//10MB
System.gc();
}
public void localvarGC2() {
byte[] buffer = new byte[10 * 1024 * 1024];
buffer = null;
System.gc();
}
public void localvarGC3() {
{
byte[] buffer = new byte[10 * 1024 * 1024];
}
System.gc();
}
public void localvarGC4() {
{
byte[] buffer = new byte[10 * 1024 * 1024];
}
int value = 10;
System.gc();
}
public void localvarGC5() {
localvarGC1();
System.gc();
}
public static void main(String[] args) {
LocalVarGC local = new LocalVarGC();
// TODO
local.localvarGC1();
}
}
-XX:+PrintGCDetails
调用 localvarGC1() 方法
[GC (System.gc()) [PSYoungGen: 15482K->10744K(76288K)] 15482K->10976K(251392K), 0.0060965 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [PSYoungGen: 10744K->0K(76288K)] [ParOldGen: 232K->10908K(175104K)] 10976K->10908K(251392K), [Metaspace: 3466K->3466K(1056768K)], 0.0048454 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 76288K, used 655K [0x000000076b380000, 0x0000000770880000, 0x00000007c0000000)
eden space 65536K, 1% used [0x000000076b380000,0x000000076b423ee8,0x000000076f380000)
from space 10752K, 0% used [0x000000076f380000,0x000000076f380000,0x000000076fe00000)
to space 10752K, 0% used [0x000000076fe00000,0x000000076fe00000,0x0000000770880000)
ParOldGen total 175104K, used 10908K [0x00000006c1a00000, 0x00000006cc500000, 0x000000076b380000)
object space 175104K, 6% used [0x00000006c1a00000,0x00000006c24a7088,0x00000006cc500000)
Metaspace used 3473K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 381K, capacity 388K, committed 512K, reserved 1048576K
1234567891011
调用 localvarGC2() 方法
[GC (System.gc()) [PSYoungGen: 15482K->872K(76288K)] 15482K->880K(251392K), 0.0008529 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 872K->0K(76288K)] [ParOldGen: 8K->668K(175104K)] 880K->668K(251392K), [Metaspace: 3466K->3466K(1056768K)], 0.0035955 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 76288K, used 655K [0x000000076b380000, 0x0000000770880000, 0x00000007c0000000)
eden space 65536K, 1% used [0x000000076b380000,0x000000076b423ee8,0x000000076f380000)
from space 10752K, 0% used [0x000000076f380000,0x000000076f380000,0x000000076fe00000)
to space 10752K, 0% used [0x000000076fe00000,0x000000076fe00000,0x0000000770880000)
ParOldGen total 175104K, used 668K [0x00000006c1a00000, 0x00000006cc500000, 0x000000076b380000)
object space 175104K, 0% used [0x00000006c1a00000,0x00000006c1aa7078,0x00000006cc500000)
Metaspace used 3473K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 381K, capacity 388K, committed 512K, reserved 1048576K
调用 localvarGC3() 方法
[GC (System.gc()) [PSYoungGen: 15482K->10736K(76288K)] 15482K->10992K(251392K), 0.0054629 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 10736K->0K(76288K)] [ParOldGen: 256K->10908K(175104K)] 10992K->10908K(251392K), [Metaspace: 3466K->3466K(1056768K)], 0.0057568 secs] [Times: user=0.05 sys=0.06, real=0.02 secs]
Heap
PSYoungGen total 76288K, used 655K [0x000000076b380000, 0x0000000770880000, 0x00000007c0000000)
eden space 65536K, 1% used [0x000000076b380000,0x000000076b423ee8,0x000000076f380000)
from space 10752K, 0% used [0x000000076f380000,0x000000076f380000,0x000000076fe00000)
to space 10752K, 0% used [0x000000076fe00000,0x000000076fe00000,0x0000000770880000)
ParOldGen total 175104K, used 10908K [0x00000006c1a00000, 0x00000006cc500000, 0x000000076b380000)
object space 175104K, 6% used [0x00000006c1a00000,0x00000006c24a7088,0x00000006cc500000)
Metaspace used 3472K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 381K, capacity 388K, committed 512K, reserved 1048576K
调用 localvarGC4() 方法
[GC (System.gc()) [PSYoungGen: 15482K->808K(76288K)] 15482K->816K(251392K), 0.0010067 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 808K->0K(76288K)] [ParOldGen: 8K->668K(175104K)] 816K->668K(251392K), [Metaspace: 3466K->3466K(1056768K)], 0.0039890 secs] [Times: user=0.00 sys=0.02, real=0.01 secs]
Heap
PSYoungGen total 76288K, used 655K [0x000000076b380000, 0x0000000770880000, 0x00000007c0000000)
eden space 65536K, 1% used [0x000000076b380000,0x000000076b423ee8,0x000000076f380000)
from space 10752K, 0% used [0x000000076f380000,0x000000076f380000,0x000000076fe00000)
to space 10752K, 0% used [0x000000076fe00000,0x000000076fe00000,0x0000000770880000)
ParOldGen total 175104K, used 668K [0x00000006c1a00000, 0x00000006cc500000, 0x000000076b380000)
object space 175104K, 0% used [0x00000006c1a00000,0x00000006c1aa7078,0x00000006cc500000)
Metaspace used 3472K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 381K, capacity 388K, committed 512K, reserved 1048576K
调用 localvarGC5() 方法
[GC (System.gc()) [PSYoungGen: 15482K->10744K(76288K)] 15482K->10968K(251392K), 0.0054897 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 10744K->0K(76288K)] [ParOldGen: 224K->10908K(175104K)] 10968K->10908K(251392K), [Metaspace: 3465K->3465K(1056768K)], 0.0048527 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (System.gc()) [PSYoungGen: 0K->0K(76288K)] 10908K->10908K(251392K), 0.0002221 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 0K->0K(76288K)] [ParOldGen: 10908K->668K(175104K)] 10908K->668K(251392K), [Metaspace: 3465K->3465K(1056768K)], 0.0055602 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] Heap PSYoungGen total 76288K, used 655K [0x000000076b380000, 0x0000000770880000, 0x00000007c0000000) eden space 65536K, 1% used [0x000000076b380000,0x000000076b423ee8,0x000000076f380000) from space 10752K, 0% used [0x000000076fe00000,0x000000076fe00000,0x0000000770880000) to space 10752K, 0% used [0x000000076f380000,0x000000076f380000,0x000000076fe00000) ParOldGen total 175104K, used 668K [0x00000006c1a00000, 0x00000006cc500000, 0x000000076b380000) object space 175104K, 0% used [0x00000006c1a00000,0x00000006c1aa7078,0x00000006cc500000) Metaspace used 3472K, capacity 4500K, committed 4864K, reserved 1056768K class space used 381K, capacity 388K, committed 512K, reserved 1048576K
图解分析
内存溢出(OOM)
内存溢出(OOM)原因分析
首先说没有空闲内存的情况:说明Java虚拟机的堆内存不够。原因有二:
说明
内存泄漏(Memory Leak)
内存泄露的举例
官方例子
左边的图:Java使用可达性分析算法,最上面的数据不可达,就是需要被回收的对象。
右边的图:后期有一些对象不用了,按道理应该断开引用,但是存在一些链没有断开,从而导致没有办法被回收。
我们的例子
关于 Stop the World 的理解
Stop the World 的注意事项
代码感受 Stop the World
/** * @author shkstart shkstart@126.com * @create 2020 15:50 */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(); }}
0.01.02.13.14.25.26.37.48.4
0.11.22.43.54.65.66.107.118.149.1510.15
并发
并行
并发与并行的对比
垃圾回收的并行与串行
垃圾回收的并发
安全点(Safepoint)
安全点的中断实现方式
如何在GC发生时,检查所有线程都跑到最近的安全点停顿下来呢?
安全区域(Safe Region)
安全区域的执行流程
再谈引用
四中引用类型的举例
Reference子类中只有终结器引用是包内可见的,其他3种引用类型均为public,可以在应用程序中直接使用
强引用
强引用代码举例
/** * 强引用的测试 * * @author shkstart shkstart@126.com * @create 2020 16:05 */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); }}
Hello,尚硅谷
StringBuffer str = new StringBuffer("hello mogublog");
StringBuffer str = new StringBuffer("hello mogublog");StringBuffer str1 = str;
总结
本例中的两个引用,都是强引用,强引用具备以下特点:
软引用(Soft Reference):内存不足即回收
在JDK1.2版之后提供了SoftReference类来实现软引用
// 声明强引用Object obj = new Object();// 创建一个软引用SoftReference<Object> sf = new SoftReference<>(obj);obj = null; //销毁强引用,这是必须的,不然会存在强引用和软引用
软引用代码举例
/** * 软引用的测试:内存不足即回收 * -Xms10m -Xmx10m * * @author shkstart shkstart@126.com * @create 2020 16:06 */public class SoftReferenceTest { public static class User { public int id; public String name; public User(int id, String name) { this.id = id; this.name = 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]; } catch (Throwable e) { e.printStackTrace(); } finally { /* 再次从软引用中获取数据 在内存不够,报OOM之前,垃圾回收器会回收软引用的可达对象。 */ System.out.println(userSoftRef.get());// } }}
[id=1, name=songhk] After GC:[id=1, name=songhk] java.lang.OutOfMemoryError: Java heap space at com.atguigu.java1.SoftReferenceTest.main(SoftReferenceTest.java:51)null
弱引用(Weak Reference)发现即回收
在JDK1.2版之后提供了WeakReference类来实现弱引用
// 声明强引用Object obj = new Object();// 创建一个弱引用WeakReference<Object> sf = new WeakReference<>(obj);obj = null; //销毁强引用,这是必须的,不然会存在强引用和弱引用
面试题:你开发中使用过WeakHashMap吗?
WeakHashMap用来存储图片信息,可以在内存不足的时候,及时回收,避免了OOM
Entry<K,V>[] table;
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> { V value; final int hash; Entry<K,V> next;
软引用代码举例
/** * 弱引用的测试 * * @author shkstart shkstart@126.com * @create 2020 16:06 */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()); }}
[id=1, name=songhk] After GC:null
虚引用(Phantom Reference):对象回收跟踪
在JDK1.2版之后提供了PhantomReference类来实现虚引用。
// 声明强引用Object obj = new Object();// 声明引用队列ReferenceQueue phantomQueue = new ReferenceQueue();// 声明虚引用(还需要传入引用队列)PhantomReference<Object> sf = new PhantomReference<>(obj, phantomQueue);obj = null;
虚引用代码示例
/** * 虚引用的测试 * * @author shkstart shkstart@126.com * @create 2020 16:07 */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