垃圾回收
可达性分析算法
由 Java 确定的一些根对象 (GC Root)可访问的对象将不会被垃圾回收,若是不可访问(不可达)则作为垃圾对象进行回收。
注:List intList = new arrayList<>(),new 出来的部分,即等于号后面的部分位于堆内,等号前的部分为堆内对象的引用,位于活动栈帧内。
四种常见引用及一种不建议使用引用
1、 强引用
直接被 GC Root 对象引用的对象,除非所有则 GC Root 对象对该对象的引用全部断开,否则不会被垃圾回收。
2、软引用(可配合引用队列使用)
发生内存回收时,发现内存不足,则有被 GC Root 对象软引用的对象可能被垃圾回收。
应用:若很大量的资源同时进行强引用,则内存空间很容易溢出
eg: List 集合内存储了大量的图片资源,每一个元素都将占用 > 4M 的内存,如何解决内存溢出的问题
由于 List 集合生命周期较长,故全部强引用将会大量的占用内存,容易造成内存溢出。
// 软引用的实例
public static void soft() {
// List --> SoftReference --> byte[]
// 软引用版List
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.print1n( "循环结束: " + list.size());
for (SoftReference<byte[]> ref : list) {
System.out.println(ref.get());
}
}
运行结果:
具体步骤:
- 前两次 for 循环正常加入到 List<SoftReference<byte[]>> 集合内,内存较为不足。
- 第三次 for 循环,产生一次 Minor GC(简单理解为清除新生代),但内存仍然很紧张。
- 第四次 for 循环,加入 List 集合后,产生一次 Full GC 清理了大量的内存,代价就是将前4个元素直接清除。
- 第五次 for 循环,将元素加入到 List 集合后,完成整个过程,最终 List 集合内只有最后一次循环加入的元素。
将内存设置为20M 软引用版本的代码没有发生内存溢出的问题,但代价就是 List 集合内仅能保存最后一次循环加入的元素,其余四个元素将被垃圾回收。
3、弱引用(可配合引用队列使用)
发生内存回收时,则有被 GC Root 对象弱引用的对象被垃圾回收。
4、虚引用(必须 配合引用队列使用)
发生内存回收时,ByteBuffer 所占用的内存被垃圾回收,但由 ByteBuffer 产生的直接内存将无法被回收,虚引用对象将记录直接内存的地址,并进入引用队列,在虚引用对象(Cleaner)中的Unsafe.freeMemory() 方法将直接内存回收掉。
5、终结器引用 - 不推荐(必须 配合引用队列使用)
所有的对象都继承自 Object 类,Object 类有一个 finallize() 方法 (终结方法),**若某个对象重写了 finallize() 方法并且没有强引用进行引用的话将可以被当做垃圾进行回收。**将重写了终结方法的对象进行垃圾回收时,首先 JVM 将为这个对象创建一个终结器引用,在调用终结方法时将终结器引用放入引用队列,此时该对象还没有被垃圾回收,在一个优先级很低的线程(finallizeHandler线程)发现引用队列中存在终结器引用时将调用待回收对象的 finallize() 方法,至此将在下一次垃圾回收时将该对象垃圾回收,这个过程优先级很低,可能导致长时间没有回收该内存,故终结器引用不建议使用。