垃圾回收相关概念
一.System.gc() 的理解
public class SystemGCTest {
public static void main(String[] args) {
new SystemGCTest();
// 就是调了这个 Runtime.getRuntime().gc();
System.gc(); // 提醒jvm的垃圾回收器执行gc, 但是不确定是否马上执行gc
// 强制调用使用引用的对象的finalize()方法
// System.runFinalization();
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("重写了finalize().....");
}
}
代码示例
/**
* -XX:+PrintGCDetails
*/
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();
}
}
GC1
GC2
GC3
GC4
GC5
二.内存溢出与内存泄漏
内存溢出(OOM)
内存泄漏(Memory Leak)
如下图右边有个应该断开的引用没有断开,就导致了内存的泄漏
举例
例子1: 比如Runtime对象,声明周期一直存在
三.Stop The World
public class StopTheWorldDemo {
public static class WorkThread extends Thread {
List<byte[]> list = new ArrayList<>();
@Override
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 e) {
e.printStackTrace();
}
}
}
public static class PrintThread extends Thread {
public final long startTime = System.currentTimeMillis();
@Override
public void run() {
try {
while (true) {
long end = System.currentTimeMillis() - startTime;
System.out.println(end / 1000 + "." + end % 1000);
Thread.sleep(1000);
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
WorkThread w = new WorkThread();
PrintThread p = new PrintThread();
w.start();
p.start();
}
}
四.垃圾回收的并行与并发
并发(Concurrent)
并行(Parallel)
垃圾回收的并发与并行
五.安全点与安全区域
安全点(Safepoint)
安全区域(Safe Region)
六.再谈引用: 强引用
强引用(Strong Reference) – 不回收
七.再谈引用: 软引用(Soft Reference) – 内存不足即回收
/**
* -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 "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public static void main(String[] args) {
// 创建对象, 建立软引用
SoftReference<User> us = new SoftReference<>(new User(1, "胡琦"));
/* 这段代码和上面的一句的作用是有一样的
User huqi = new User(1, "huqi");
SoftReference<User> userSoftReference = new SoftReference<>(huqi);
huqi = null;*/
// 从软引用中重新获得强引用对象
System.out.println(us.get());
System.gc();
System.out.println("After GC : ");
// 垃圾回收之后获得软引用中的对象
System.out.println(us.get()); // 由于堆内存空间足够, 所以不会回收软引用的可达对象
try {
// 让系统以为内存资源紧张
byte[] b = new byte[1024 * 1024 * 7];
}catch (Throwable throwable) {
throwable.printStackTrace();
}finally {
// 再次从软引用中获取数据
System.out.println(us.get());// 在报OOM之前, 垃圾回收器会回收软引用的可达对象
}
}
}
八.再谈引用: 弱引用(Weak Reference) – 发现即回收
关于weakHashMap可参考 https://blog.csdn.net/u013467442/article/details/105826778
九.再谈引用: 虚引用(Phantom Reference) – 对象回收跟踪
public class PhantomReferenceTest {
/**
* 当前类对象的声明
*/
public static PhantomReferenceTest obj;
/**
* 引用队列
*/
static ReferenceQueue<PhantomReferenceTest> phantomQueue = null;
public static class CheckRefQueue extends Thread {
@Override
public void run() {
while (true) {
// 当GC回收的时候将虚引用对象放入队列中的时候,会进入循环对队列进行操作
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了 -> " +
JSON.toJSONString(objt));
}
}
}
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("调用当前类的finalize()方法");
// 让当前对象复活
obj = this;
}
public static void main(String[] args) {
Thread t = new CheckRefQueue();
// 设置为守护线程, 当程序中没有非守护线程时, 守护线程才会结束
t.setDaemon(true);
t.start();
phantomQueue = new ReferenceQueue<>();
obj = new PhantomReferenceTest();
// 构造了 PhantomReferenceTest 对象的虚引用, 并指定了引用队列
PhantomReference<PhantomReferenceTest> phantomReference = new PhantomReference<>(obj, phantomQueue);
try {
// 不可获取虚引用中的对象
System.out.println(phantomReference.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("第二次gc");
obj = null;
// 一旦将obj对象回收, 就会将次虚引用存放到引用队列中
System.gc();
Thread.sleep(1000);
if (obj == null) {
System.out.println("obj 是 null");
} else {
System.out.println("obj 可用");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
十.再谈引用: 终结器引用(Final reference)