对象循环引用
a对象引用b对象,b对象也引用a对象,a、b对象没有再被其他对象引用,正常来说这两个对象已经是垃圾了,因为没有被其他对象再引用,但是计数器的数值却不是 0 ,所以使用此算法就无法被回收
可达性分析算法也是JVM 默认使用的寻找垃圾算法。
比如执行一个方法,这个方法就被当做 GC Roots 根对象,方法内的所有对象都会与跟根对象产生关联,当这些对象与 GC Roots 有关联时,不会被当做垃圾回收,当没有关联时,就会被回收
例如
Object 6
、Object 7
、Object 8
彼此之前有引用关系,但是没有与"GC Roots"相连,那么就会被当做垃圾所回收。
Object strongReference = new Object();
显示的设置 strongReference 对象为null,则 gc 认为该对象不存在引用,这时就可以回收这个对象
如果 strongReference 是 全局变量,就需要在不用这个对象时赋值为null
,因为强引用不会被垃圾回收。
strongReference = null;
应用场景:在ArrayList
集合类中定义elementData
数组,在调用clear()
方法清空集合元素时,将每个数组元素被赋值为null
。目的是为了将内存数组中存放的引用类型进行内存释放,可以及时释放内存。不选择将elementData=null
,是为了避免在后续调用add()
等方法添加新元素时,需要进行内存的重新分配
//ArrayList 源码
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
让对象超出作用域范围,即缩小强引用的作用范围
public void test() {
Object strongReference = new Object();
// 省略其他操作
}
应用场景:在一个方法的内部有一个强引用,这个引用保存在VM Stack
栈中(GC Root*),而真正的引用对象(
Object)保存在堆中。当这个方法运行完成后,就会退出方法栈,则这个对象会被回收。
使用 SoftReference 创建软引用
// 定义和访问软引用
// 强引用
String strongReference = new String("abc");
// 软引用
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<String>(str);
// 访问软引用
softReference.get();
jvm
内存不够的时候才会被回收,我们调用System.gc()
方法只是起通知作用,最终何时回收,由JVM
决定。JVM
首先将软引用中的对象引用置为null
,然后通知垃圾回收器进行回收://回收软引用
// 软引用
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<>(str);
str = null;
// Notify GC
System.gc();
try {
byte[] buff1 = new byte[900000000]; // 内存充沛
// byte[] buff2 = new byte[900000000]; // 内存不足
} catch (Error e) {
e.printStackTrace();
}
System.out.println(softReference.get()); // abc 或 null
**应用场景:**短视频APP中的视频缓存,后退时,显示的短视频内容是重新进行请求还是从缓存中取出呢?
此时,可以使用软引用解决这个实际问题:
//软引用案例
// 获取视频播放器对象
Player videoAlayer = new Player();
// 加载短视频
Video video = audioAlayer.getVideo();
// 将播放完毕的短视频设置为软引用
SoftReference softReference = new SoftReference(video);
// 回退或者再次播放时
if(softReference.get() != null) {
// 内存充足,还没有被回收器回收,直接获取缓存
video = softReference.get();
} else {
// 内存不足,软引用的对象已经回收
video = audioAlayer.getVideo();
// 重新构建软引用
softReference = new SoftReference(video);
}
创建弱引用,使用 WeakReference
String str = new String("abc");
WeakReference<String> weakReference = new WeakReference<>(str);
str = null;
System.gc();
// 一旦发生GC,弱引用一定会被回收
System.out.println(weakReference.get());
public class PhantomReference<T> extends Reference<T> {
public T get() {
return null;
}
}
目前主流JVM
虚拟机中的垃圾收集器,都遵循分代收集理论:
按照分代收集理论设计的“分代垃圾收集器”,所采用的设计原则:收集器应该将Java
堆划分成不同的区域,然后将回收对象依据其年龄(年龄即对象经历过垃圾收集过程的次数)分配到不同的区域存储
JVM
虚拟机就可以使用较低的频率,来对这个区域进行回收。堆区按照分代存储的好处:
在Java
堆区划分成不同区域后,垃圾收集器才可以每次只回收其中某一个或者某些区域,所以才有MinorGC
、MajorGC
、FullGC
等垃圾收集类型划分。
在Java
堆区划分成不同区域后,垃圾收集器才可以针对不同的区域,安排与该区域存储对象存亡特征相匹配的垃圾收集算法:标记-复制算法、标记-清除算法、标记-整理算法等
垃圾收集类型划分:
部分收集(Partial GC
):没有完整收集整个Java
堆的垃圾收集,其中又分为:
Minor GC
/ Young GC
)Major GC
/ Old GC
)Mixed GC
):收集整个新生代和部分老年代的垃圾收集。整堆收集(Full GC
):收集整个Java
堆的垃圾收集
最基础的收集算法
执行效率不稳定
如果执行垃圾收集的区域,大部分对象是需要被回收的,则需要大量的标记和清除动作,导致效率变低
内存空间碎片化
标记清除后会产生大量的不连续的内存空间碎片,会导致分配较大对象时,无法找到祖足够的连续空间,从而会触发新的垃圾收集动作
简称 “复制算法” ,为了解决“标记-清除”面对大量可回收对象时执行效率低下的问题
当内存中大多数对象都是可回收的情况,推荐使用此算法
标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向内存空间一端移动, 然后直接清理边界以外的内存,这样清理的机制,不会像标记-清除那样留下大量的内存碎片。
当前虚拟机的垃圾收集都基于分代收集思想,根据对象存活周期的不同,将内存分为几个不同的区域,在不同的区域选择使用合适的垃圾收集算法