java垃圾回收与reference

5 篇文章 0 订阅
2 篇文章 0 订阅
本文深入探讨了Java中finalize方法的工作原理及其与垃圾回收的关系,分析了finalize的局限性及其实现细节。此外,还介绍了弱引用、软引用、幽灵引用的不同应用场景,并通过Spring框架的自定义Scope实现示例,展示了不同引用类型在实际开发中的应用。
摘要由CSDN通过智能技术生成

一、finalize

相当于析构函数,主要用于销毁某些资源。

与GC的关系:在gc发现对象mutator不可达,并且存在非平凡的finalize方法时,会将改方法放入finalize队列,并重新以该队列作为trace root来标记堆中的对象。

实现上,对象拥有一个finalize标志位,当gc发现mutator不可达且拥有该标志位时,会将该对象放入ReferenceQueue,(实际上是挂到Reference的discover上,由守护线程轮询入队),同时去除该标记位(让改对象不能再次入队,保证一个对象的finalize方法只能被执行一次),同时finalizer守护线程(FinalizerThread)会不停的拉取这个队列的对象,并执行其finalize方法。

具体实现参见,省略了与主流程无关的内容

final class Finalizer extends FinalReference<Object> { /* Package-private; must be in
                                                          same package as the Reference
                                                          class */

    private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
    private static Finalizer unfinalized = null;
    private static final Object lock = new Object();

    /* Invoked by VM */
    static void register(Object finalizee) {
        new Finalizer(finalizee);
    }

    private void runFinalizer(JavaLangAccess jla) {
        synchronized (this) {
            if (hasBeenFinalized()) return;
            remove();
        }
        try {
            Object finalizee = this.get();
            if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {
                jla.invokeFinalize(finalizee);

                /* Clear stack slot containing this variable, to decrease
                   the chances of false retention with a conservative GC */
                finalizee = null;
            }
        } catch (Throwable x) { }
        super.clear();
    }

 


    private static class FinalizerThread extends Thread {
        private volatile boolean running;
        FinalizerThread(ThreadGroup g) {
            super(g, "Finalizer");
        }
        public void run() {
      
            for (;;) {
                try {
                    Finalizer f = (Finalizer)queue.remove();
                    f.runFinalizer(jla);
                } catch (InterruptedException x) {
                    // ignore and continue
                }
            }
        }
    }

    static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread finalizer = new FinalizerThread(tg);
        finalizer.setPriority(Thread.MAX_PRIORITY - 2);
        finalizer.setDaemon(true);
        finalizer.start();
    }

}

通过源码我们可以看到

  1. 对于finalize的异常,是会被忽略的
  2. FinalizerThread看上去是单线程的,这会影响并发执行的效率
  3. finalize是无序的!!!即使存在依赖关系
  4. 通过finalize暴露的this,会导致这个对象重新复活,但是这跟finalize的实现理念是相悖的,因为finalize用来清理关联的资源,在finalize清理完资源后,重新复活这个对象意味着,这个对象可能在一个不满足条件的环境下运行(资源被清理了)

finalize拥有以上的不足,于是引入了以下概念

二、各类型引用

  1. weak:弱引用
  2. soft:软引用,内存敏感
  3. phantom:幽灵引用(翻译成虚引用感觉不太贴切,幽灵表示飘渺游荡的指针),可以理解为一个对应引用的占位符。

与GC的关系,CMS的yong gc为例(其他GC同理)

  1. 根扫描,扫描并复制所有的强引用,并标记出所有的weak、soft、phantom引用(到这些引用就返回,不继续trace)
  2. 若复制过程中出现内存不够,置空所有的soft(注意是所有)并入队。如果内存足够,以soft为根进行trace跟复制。事实上,soft的处理可能比这个复杂,vm会选择入队并置空的时机!!!但一般来说,只有对象被回收了,才会入队,可能不仅在内存不够时还可能出现在长时间未get的引用
  3. 如果weak引用的对象没有被复制(需要被回收),则将weak置空,并加入Reference queue(加入的方式等同于finalize)
  4. 如果尚未被复制的对象包含了finalize标记,则走第一节所述内容(加入队列,并回到【1】)
  5. 如果幽灵引用的目标对象未被复制,入队并trace(保证对象的完整性,虽然get方法返回的是null)。注意,该引用需要手动清理,调用clear方法

finalize的处理实际上zao

三、一个基于spring的refreshscope实现

意图:通过查看源码发现apollo与spring Configuration的绑定是相当耗时的,时间复杂度正比于配置项的平方,并且不能自动刷新,因此简单重新实现了绑定流程,采用了自定义的scope,但是自定义scope需要自己管理bean的生命周期。这个demo不合理,但是能表现意图,主要还是Reference是调用是不保证顺序的

package com.yupaopao.platform.audit.gateway.framework;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 避免每次都去创建,如果没刷新,直接拿
 * 区别于proptotype,每次都要创建
 * <p>
 * 提示:自定义scope需要自己管理bean的生命周期,如destory等,但是这里不是特别好实现,因为如果这个bean被pin住,bean就不能销毁。这里交给了GC
 * 但是bean的依赖关系可能会导致销毁问题,主要是reference queue的非无序性,强制把生命周期拉到了gc阶段,不合理
 *
 * @author hanweiwei
 */
@Slf4j
public class RefreshScope extends Thread implements Scope {

    public static final String scopeName = "apolloRefresh";

    private ConcurrentHashMap<String, BeanLifecycleWrapper> wrapperCache = new ConcurrentHashMap<>();

    private ConcurrentHashMap<WeakReference<Object>, WeakReference<Runnable>> referenceCache = new ConcurrentHashMap<>();

    private ThreadLocal<BeanLifecycleWrapper> beanLifecycleLocal = new ThreadLocal<>();

    private ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        beanLifecycleLocal.remove();
        try {
            BeanLifecycleWrapper beanLifecycleWrapper = wrapperCache.computeIfAbsent(name, key -> new BeanLifecycleWrapper(key, objectFactory));
            return beanLifecycleWrapper.getBean();
        } finally {
            beanLifecycleLocal.remove();
        }
    }

    @Override
    public Object remove(String name) {
        BeanLifecycleWrapper wrapper = wrapperCache.remove(name);
        if (wrapper != null && wrapper.callback != null) {
            referenceCache.put(new WeakReference<>(wrapper.bean, referenceQueue), new WeakReference<>(wrapper.callback));
        }
        //如果这直接返回bean,会导致这个bean还在使用,却被提前销毁
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {
        beanLifecycleLocal.get().setDestroyCallback(callback);
    }

    /**
     * 不支持,可以用表达式替换
     *
     * @param key
     * @return
     */
    @Override
    public Object resolveContextualObject(String key) {
        return wrapperCache.get(key);
    }

    /**
     * 无绑定id,只与当前环境有关
     *
     * @return
     */
    @Override
    public String getConversationId() {
        return null;
    }

    /**
     * 占位
     */
    private class BeanLifecycleWrapper {

        private final String name;

        private final ObjectFactory<?> objectFactory;

        private Object bean;

        private Runnable callback;

        BeanLifecycleWrapper(String name, ObjectFactory<?> objectFactory) {
            this.name = name;
            this.objectFactory = objectFactory;
        }

        public String getName() {
            return this.name;
        }

        public void setDestroyCallback(Runnable callback) {
            this.callback = callback;
        }

        public Object getBean() {

            if (this.bean == null) {
                synchronized (this) {
                    if (this.bean == null) {
                        this.bean = this.objectFactory.getObject();
                        if (this.bean != null) {
                            beanLifecycleLocal.set(this);
                        }
                    }
                }
            }
            return this.bean;
        }

        public void destroy() {
            if (this.callback == null) {
                return;
            }
            Runnable callback = this.callback;
            callback.run();
            this.callback = null;
            this.bean = null;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((this.name == null) ? 0 : this.name.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            BeanLifecycleWrapper other = (BeanLifecycleWrapper) obj;
            if (this.name == null) {
                return other.name == null;
            } else return this.name.equals(other.name);
        }
    }

    @Override
    public void run() {
        for (; ; ) {
            try {
                Reference<?> r = referenceQueue.remove();
                if (r != null) {
                    WeakReference<Runnable> runnableWeakReference = referenceCache.remove(r);
                    Runnable runnable = runnableWeakReference.get();
                    if (runnable != null) {
                        runnable.run();
                    }
                    r.clear();
                }
            } catch (Exception e) {
                log.warn("", e);
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值