JVM篇 笔记1 :内存结构
https://blog.csdn.net/qq_46539281/article/details/120174709
JVM篇 笔记3:类加载与字节码技术
https://blog.csdn.net/qq_46539281/article/details/120174860
JVM篇 笔记4:内存模型
https://blog.csdn.net/qq_46539281/article/details/120174885
垃圾回收
- 如何判断对象可以回收
- 垃圾回收算法
- 分代垃圾回收
- 垃圾回收器
- 垃圾回收调优
1. 如何判断对象可以回收
1.1 引用计数法
有一个对象被其他的变量引用,就计数加1 , 多一个就加以。少一个就减一。为0 就可以被回收。
但是会出现一个闭端:
两个对象互相引用,就一直回收不了。
1.2 可达性分析算法
JVM 使用的判断释放是垃圾的算法。
先确定一系列的根对象,其他的对象是不是 根对象 直接间接的使用。 如果不是,就可以直接被垃圾回收。
- Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
- 扫描堆中的对象,看是否能够沿着 GC Root对象 为起点的引用链找到该对象,找不到,表示可以
回收 - 哪些对象可以作为 GC Root ?
使用该工具可以查看有哪些根对象,等等。
1.3 四种引用
-
强引用 (平时使用的引用,都是强引用)
只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收 -
软引用(SoftReference)
仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用
对象可以配合引用队列来释放软引用自身 -
弱引用(WeakReference)
仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
可以配合引用队列来释放弱引用自身 -
虚引用(PhantomReference)
必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,
由 Reference Handler 线程调用虚引用相关方法释放直接内存 -
终结器引用(FinalReference)
无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象
暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize
方法,第二次 GC 时才能回收被引用对象
软引用:当没有直接根对象直接引用时,触发minor gc 时,是不会清理的。当 堆中的内存不够时,触发full gc 时,JVM认为这个虚引用不够重要,才把它当作垃圾进行回收了。
弱引用:当没有直接根对象直接引用时,只要触发了垃圾回收,就会把这个弱引用对象给回收了。
-Xmx20m -XX:+PrintGCDetails -verbose:gc 设置堆内存20m
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
/**
* 演示软引用
* -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());
}
}
}
在堆中,已经生成了有4个 byte对象后,导致内存不够了,所以触发了full gc ,将前面4个软对象 的内存释放了,有空间后,就生成了第五个byte对象。所以打印后,就会出现这种情况。
当然,生成的虚引用也是一个对象,也是占内存的。并没有释放掉的。
需要释放的话,使用引用队列进行释放。
演示软引用, 配合引用队列 , 回收虚引用对象后,将虚引用也进行回收了。使用引用队列。
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
/**
* 演示软引用, 配合引用队列
*/
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());
}
}
}
结果和上面的案例一样。