JVM专题-垃圾回收
Posted IT-老牛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM专题-垃圾回收相关的知识,希望对你有一定的参考价值。
文章目录
1.如何判断对象可以回收
1.1.引用计数法
- 当一个对象被其他变量引用,该对象计数加一,当某个变量不在引用该对象,其计数减一
- 当一个对象引用没有被其他变量引用时,即计数变为0时,该对象就可以被回收
缺点:循环引用时,两个对象的计数都为1,导致两个对象都无法被释放
1.2.可达性分析算法
- JVM中的垃圾回收器通过可达性分析来探索所有存活的对象
- 扫描堆中的对象,看能否沿着GC Root对象为起点的引用链找到该对象,如果找不到,则表示可以回收
- 可以作为GC Root的对象
– 虚拟机栈(栈帧中的本地变量表)中引用的对象。
– 方法区中类静态属性引用的对象
– 方法区中常量引用的对象
– 本地方法栈中JNI(即一般说的Native方法)引用的对象
– 所有被同步锁(synchronized
关键字)持有的对象。
示例
/**
* 演示GC Roots
*/
public class Demo2_2
public static void main(String[] args) throws InterruptedException, IOException
List<Object> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
System.out.println(1);
System.in.read();
list1 = null;
System.out.println(2);
System.in.read();
System.out.println("end...");
运行程序,分别生成垃圾回收前后的dunp
文件
jps // 查看进程号
// format=b 生成文件格式为二进制;live主动触发垃圾回收,保留存活对象;file表示存放位置
jmap -dump:format=b,live,file=1.bin 进程号51125
list1置空前,生成dunmp文件1.bin
List<Object> list1 = new ArrayList<>();
list1
是局部变量,存在于活动栈帧;new ArrayList<>()
产生的对象才是存在于堆中的对象。即此处new ArrayList<>()
对应的那个对象才能作为根对象。
list1置空后,生成dunmp文件2.bin
因为在执行
jmap -dump:format=b,live,file=2.bin 51125
使用了live参数,主动调用了垃圾回收。由于list1
被置空,list
对象无人引用,所以被垃圾回收了。所以在根对象中找不到了。
2.五种引用
强引用
只有所有 GC Roots对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
强引用对象回收
软引用
仅有【软引用】引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
可以配合【引用队列】来释放软引用自身
软引用对象回收
此时A2对象可能被回收。
- A2对象仅仅只被软引用对象引用
- 在执行GC时,内存空间不足了,才会被垃圾回收
- 回收后,软引用对象本身可以通过进入引用队列进行释放
弱引用
仅有【弱引用】引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
可以配合【引用队列】来释放弱引用自身
弱引用对象的回收
此时A3对象可能会被回收
- A3对象仅仅被弱引用对象引用
- 当执行GC时,无论内存是否不足,都会被垃圾回收
- 回收后,弱引用对象本身可以通过进入引用队列进行释放
虚引用
必须配合【引用队列】使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将【虚引用】入队, 由 Reference Handler 线程调用虚引用相关方法释放【直接内存】
如上图,B对象不再引用ByteBuffer对象,ByteBuffer就会被回收。但是直接内存中的内存还未被回收。这时需要将虚引用对象Cleaner放入引用队列中,然后调用它的clean方法来释放直接内存
虚引用对象的回收
虚引用一般是对直接内存分配的应用。
-
当声明
ByteBuffer
时,ByteBuffer
会分配一块直接内存,并把直接内存的地址传递给虚引用对象Cleaner。 -
当
ByteBuffer
不再被强引用时,被回收后,直接内存还没有被释放。这时会将虚引用放入虚引用的引用队列,由Reference Handler
线程监控,发现虚引用对象,调用虚引用相关方法Unsafe.freeMemory
释放直接内存。
终结器引用
无需手动编码,但其内部配合【引用队列】使用,在垃圾回收时,【终结器引用】入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过【终结器引用】找到被引用对象并调用它的 finalize 方法,第二次 GC 时才能回收被引用对象
如上图,B对象不再引用A4对象。这时终结器对象就会被放入引用队列中,引用队列会根据它,找到它所引用的对象。然后调用被引用对象的finalize方法。调用以后,该对象就可以被垃圾回收了
终结器引用的回收
- 所有的类都继承自Object类,里面有一个终结方法finalize()方法,当对象重写了finalize()方法,且没有强引用引用它时,它就可以被当成垃圾进行垃圾回收。
- 当对象没有被强引用时,会由jvm为该对象创建一个对应的终结器引用。当这个对象被垃圾回收时,会将终结器引用加入引用队列,但是对象不会被垃圾回收。
- 再由一个优先级较低的Finalizer线程去监控引用队列是否有终结器引用,如果有,就通过终结器引用找到A4对象,调用其finalize()方法,等调用之后,等下一次垃圾回收时,就可以被垃圾回收了。
- 工作效率低,第一次GC不会回收对象,先将终结器引用入队,等到第二次垃圾回收才有可能被回收。
6.代码示例
6.1软引用
# 虚拟机参数
-Xmx20m -XX:+PrintGCDetails -verbose:gc
/**
* 演示软引用
* -Xmx20m -XX:+PrintGCDetails -verbose:gc
*/
public class Demo2_3
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args) throws IOException
/*List<byte[]> list = new ArrayList<>();
for (int i = 0; i < 5; i++)
list.add(new byte[_4MB]);
System.in.read();*/
soft();
public static void soft()
// list --> SoftReference --> byte[]
List<SoftReference<byte[]>> list = new ArrayList<>();
for (int i = 0; i < 5; i++)
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
System.out.println("循环结束:" + list.size());
for (SoftReference<byte[]> ref : list)
System.out.println(ref.get());
/**
* 演示软引用, 配合引用队列
*/
public class Demo2_4
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args)
List<SoftReference<byte[]>> list = new ArrayList<>();
// 引用队列
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
for (int i = 0; i < 5; i++)
// 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
// 从队列中获取无用的 软引用对象,并移除
Reference<? extends byte[]> poll = queue.poll();
while( poll != null)
list.remove(poll);
poll = queue.poll();
System.out.println("===========================");
for (SoftReference<byte[]> reference : list)
System.out.println(reference.get());
6.2 弱引用
虚拟机参数
-Xmx20m -XX:+PrintGCDetails -verbose:gc
/**
* 演示弱引用
* -Xmx20m -XX:+PrintGCDetails -verbose:gc
*/
public class Demo2_5
private static final int _4MB = 4 * 1024 * 1024;
public static void main(String[] args)
// list --> WeakReference --> byte[]
List<WeakReference<byte[]>> list = new ArrayList<>();
for (int i = 0; i < 10; i++)
WeakReference<byte[]> ref = new WeakReference<>(new byte[_4MB]);
list.add(ref);
for (WeakReference<byte[]> w : list)
System.out.print(w.get()+" ");
System.out.println();
System.out.println("循环结束:" + list.size());
以上是关于JVM专题-垃圾回收的主要内容,如果未能解决你的问题,请参考以下文章