System.gc() 方法
代码示例:手动执行 GC 操作
代码示例 1
代码
public class SystemGCTest {
public static void main(String[] args) {
new SystemGCTest();
System.runFinalization();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("SystemGCTest 重写了finalize()");
}
}
有时候会调用 finalize() 方法,有时候并不会调用
"C:\Program Files\Java\jdk1.8.0_144\bin\java"
SystemGCTest 重写了finalize()
Process finished with exit code 0
"C:\Program Files\Java\jdk1.8.0_144\bin\java"
Process finished with exit code 0
手动 GC 理解不可达对象的回收行为
代码
public class LocalVarGC {
public void localvarGC1() {
byte[] buffer = new byte[10 * 1024 * 1024];
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();local.localvarGC1();
}
}
JVM 参数
-XX:+PrintGCDetails
调用 localvarGC1() 方法
执行 System.gc() 仅仅是将年轻代的 buffer 数组对象放到了老年代(Why?buffer 数组年龄应该还没达到阈值吧~~~)
[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
调用 localvarGC2() 方法
由于 buffer 数组对象没有引用指向它,执行 System.gc() 将被回收
[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() 方法
虽然出了代码块的作用域,但是 buffer 数组对象并没有被回收
[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
来看看字节码:实例方法局部变量表第一个变量肯定是 this
调用 localvarGC4() 方法
就多定义了一个局部变量 value ,怎么就能把字节数组回收了呢?
[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
看,value 位于局部变量表中索引为 1 的位置
调用 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
代码
public class StopTheWorldDemo {
public static class WorkThread extends Thread {
List
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();
}
}
} 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();p.start();
}
}
关闭工作线程 w ,观察打印输出:当前时间间隔与上次时间间隔最多相差 0.1s
0.0
1.0
2.1
3.1
4.2
5.2
6.3
7.4
8.4
开启工作线程 w ,观察打印输出:当前时间间隔与上次时间间隔最多相差 0.4s ,可以明显感受到 Stop the World 的存在
0.1
1.2
2.4
3.5
4.6
5.6
6.10
7.11
8.14
9.15
10.15
并发
并行
并发与并行的对比
垃圾回收的并行与串行
垃圾回收的并发
安全点(Safepoint)
安全点的中断实现方式
如何在GC发生时,检查所有线程都跑到最近的安全点停顿下来呢?
安全区域(Safe Region)
安全区域的执行流程
再谈引用
四中引用类型的举例
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);
}
}
程序输出
Hello,尚硅谷
局部变量str指向stringBuffer实例所在堆空间,通过str可以操作该实例,那么str就是stringBuffer实例的强引用对应内存结构:
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;
软引用代码举例
代码
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"));
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 {
System.out.println(userSoftRef.get());
}
}
}
在 JVM 内存不足时,会清理软引用对象
[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
来,看源码,WeakHashMap 类中同样也维护了一个 Entry[] 数组
Entry
Entry 类继承了 WeakReference 类
private static class Entry
软引用代码举例
代码
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;
虚引用代码示例
代码
public class PhantomReferenceTest {
public static PhantomReferenceTest obj;
static ReferenceQueue
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 {
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();
PhantomReference<PhantomReferenceTest> phantomRef = new PhantomReference<PhantomReferenceTest>(obj, phantomQueue);
try {
System.out.println(phantomRef.get());
obj = null;
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();
Thread.sleep(1000);
if (obj == null) {
System.out.println("obj 是 null");
} else {
System.out.println("obj 可用");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
第一次尝试获取虚引用的值,发现无法获取的,这是因为虚引用是无法直接获取对象的值,然后进行第一次GC,因为会调用finalize方法,将对象复活了,所以对象没有被回收
但是调用第二次GC操作的时候,因为finalize方法只能执行一次,所以就触发了GC操作,将对象回收了,同时将会触发第二个操作就是将待回收的对象存入到引用队列中。
null
调用当前类的finalize()方法
obj 可用
第 2 次 gc
追踪垃圾回收过程:PhantomReferenceTest实例被GC了
obj 是 null
你只管学习,我来负责记笔记 关注公众号! ,更多笔记,等你来拿,谢谢
手机扫一扫
移动阅读更方便
你可能感兴趣的文章