一对象回收算法
Java
在GC
时判断对象是否存活有两种方式;第一种是引用计数方式,第二种是可达性分析算法;
引用计数器算法:
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可用对象
额外占用空间,高效;
可达性分析算法:
从gc roots 往下搜索,走过的路径称为引用链,如果引用链断开说明对象不可达标记为可回收对象;如果引用链连续说明此对象可达标记为不可回收对象。如图所示,object4,object5,object7都是被标记为可回收对象。
jdk1.2版本之前 如果一个对象没有被引用,则无法使用这个对象;jdk1.2版本之后对象被分为4个引用级别,由高到低依次为:强引用、软引用、弱引用和虚引用;开发人员可以利用对象的引用级别灵活控制对象的生命周期。
二强引用(StrongReference)
强引用是指引用赋值,即Objecto bj = new Object()
。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
如下方式都是强引用
Object obj = new Object();
String str = "zszxz";
如果一个对象具有强引用,不会被垃圾回收器回收。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不回收对象,故通常如果对象不用的时候就需要手动赋值为 null, 方便垃圾回收器回收
示例
String str = null;
当然 通常对象不会手动赋值为null, 只有比较大的对象集合时就需要手动赋值,比如 ArrayList 里面的 clear() 方法。
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
注:养成手动释放内存能提示程序性能
三软引用(SoftReference)
软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常;
源码:
// 时间戳,右垃圾回收器更新
static private long clock;
// 时间戳,通过get方法调用更新,选择软引用时将被清除,VM可以使用这个字段,并非必须
private long timestamp;
public SoftReference(T referent) {// 构造器,给对象注入一个软引用
super(referent);
this.timestamp = clock;
}
// 给对象注入一个软引用,并且注入队列
public SoftReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.timestamp = clock;
}
// 返回对象的引用,如果引用对象已经被清除或者垃圾回收,则返回Null
public T get() {
T o = super.get();
if (o != null && this.timestamp != clock)
this.timestamp = clock;
return o;
}
如下方式,即使发生gc,只要内存没有溢出就不会回收软引用
public static void main(String[] args) throws InterruptedException {
// 给对象注入软引用
SoftReference<String> reference = new SoftReference<>(new String("zszxz"));
// zszxz
System.out.println(reference.get());
// 调用GC
System.gc();
TimeUnit.SECONDS.sleep(5);
// zszxz
System.out.println(reference.get());
}
适用于缓存场景
四弱引用(WeakReference)
当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉被弱引用关联的对象;
源码:
public class WeakReference<T> extends Reference<T> {
// 给对象注入弱引用
public WeakReference(T referent) {
super(referent);
}
// 给对象注入一个弱引用并注入引用队列
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
如下方式,一旦GC调用则会回收对象。
public static void main(String[] args) throws InterruptedException {
WeakReference<String> weakReference = new WeakReference<>(new String("zszxz"));
// zszxz
System.out.println(weakReference.get());
// 调用GC
System.gc();
TimeUnit.SECONDS.sleep(5);
// zszxz
System.out.println(weakReference.get());
}
五虚引用(PhantomReference)
为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知,除此之外没有任何作用,不决定对象的生命周期;
源码
public class PhantomReference<T> extends Reference<T> {
// 获得引用
public T get() {
return null;
}
// 创建虚引用
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
如下示例,即使不调用GC,引用也是为null。
public static void main(String[] args) throws InterruptedException {
// 引用队列
ReferenceQueue queue = new ReferenceQueue();
PhantomReference reference = new PhantomReference(new String("zszxz"),queue);
// null
System.out.println(reference.get());
}
六总结
- 强引用:从来不会被回收
- 软引用:当内存不足时会被回收
- 弱引用:正常垃圾回收时回收
- 虚引用:任何时刻都会被垃圾回收器回收
关注公众号:知识追寻者获取原创PDF,面试题