Java Reference核心原理分析

带着问题,看源码针对性会更强一点、印象会更深刻、并且效果也会更好。所以我先卖个关子,提两个问题(没准下次跳槽时就被问到)。

  • 我们可以用ByteBuffer的allocateDirect方法,申请一块堆外内存创建一个DirectByteBuffer对象,然后利用它去操作堆外内存。这些申请完的堆外内存,我们可以回收吗?可以的话是通过什么样的机制回收的?
  • 大家应该都知道WeakHashMap可以用来实现内存相对敏感的本地缓存,为什么WeakHashMap合适这种业务场景,其内部实现会做什么特殊处理呢?

GC可到达性与JDK中Reference类型

上面提到的两个问题,其答案都在JDK的Reference里面。JDK早期版本中并没有Reference相关的类,这导致对象被GC回收后如果想做一些额外的清理工作(比如socket、堆外内存等)是无法实现的,同样如果想要根据堆内存的实际使用情况决定要不要去清理一些内存敏感的对象也是法实现的。为此JDK1.2中引入的Reference相关的类,即今天要介绍的Reference、SoftReference、WeakReference、PhantomReference,还有与之相关的Cleaner、ReferenceQueue、ReferenceHandler等。与Reference相关核心类基本都在java.lang.ref包下面。其类关系如下

其中,SoftReference代表软引用对象,垃圾回收器会根据内存需求酌情回收软引用指向的对象。普通的GC并不会回收软引用,只有在即将OOM的时候(也就是最后一次Full GC)如果被引用的对象只有SoftReference指向的引用,才会回收。WeakReference代表弱引用对象,当发生GC时,如果被引用的对象只有WeakReference指向的引用,就会被回收。PhantomReference代表虚引用对象(也有叫幻象引用的,个人认为还是虚引用更加贴切),其是一种特殊的引用类型,不能通过虚引用获取到其关联的对象,但当GC时如果其引用的对象被回收,这个事件程序可以感知,这样我们可以做相应的处理。最后就是最常见强引用对象,也就是通常我们new出来的对象。在继续介绍Reference相关类的源码前,先来简单的看一下GC如何决定一个对象是否可被回收。其基本思路是从GC Root开始向下搜索,如果对象与GC Root之间存在引用链,则对象是可达的,GC会根据是否可到达与可到达性决定对象是否可以被回收。而对象的可达性与引用类型密切相关,对象的可到达性可分为5种。

  • 强可到达,如果从GC Root搜索后,发现对象与GC Root之间存在强引用链则为强可到达。强引用链即有强引用对象,引用了该对象。
  • 软可到达,如果从GC Root搜索后,发现对象与GC Root之间不存在强引用链,但存在软引用链,则为软可到达。软引用链即有软引用对象,引用了该对象。
  • 弱可到达,如果从GC Root搜索后,发现对象与GC Root之间不存在强引用链与软引用链,但有弱引用链,则为弱可到达。弱引用链即有弱引用对象,引用了该对象。
  • 虚可到达,如果从GC Root搜索后,发现对象与GC Root之间只存在虚引用链则为虚可到达。虚引用链即有虚引用对象,引用了该对象。
  • 不可达,如果从GC Root搜索后,找不到对象与GC Root之间的引用链,则为不可到达。
    看一个简单的列子:
  • ObjectA为强可到达,ObjectB也为强可到达,虽然ObjectB对象被SoftReference ObjcetE 引用但由于其还被ObjectA引用所以为强可到达;而ObjectC和ObjectD为弱引用达到,虽然ObjectD对象被PhantomReference ObjcetG引用但由于其还被ObjectC引用,而ObjectC又为弱引用达到,所以ObjectD为弱引用达到;而ObjectH与ObjectI是不可到达。引用链的强弱有关系依次是 强引用 > 软引用 > 弱引用 > 虚引用,如果有更强的引用关系存在,那么引用链到达性,将由更强的引用有关系决定。

Reference核心处理流程

JVM在GC时如果当前对象只被Reference对象引用,JVM会根据Reference具体类型与堆内存的使用情况决定是否把对应的Reference对象加入到一个由Reference构成的pending链表上,如果能加入pending链表JVM同时会通知ReferenceHandler线程进行处理。ReferenceHandler线程是在Reference类被初始化时调用的,其是一个守护进程并且拥有最高的优先级。Reference类静态初始化块代码如下:

static {
    //省略部分代码...
    Thread handler = new ReferenceHandler(tg, "Reference Handler");
    handler.setPriority(Thread.MAX_PRIORITY);
    handler.setDaemon(true);
    handler.start();
    //省略部分代码...
}

 而ReferenceHandler线程内部的run方法会不断地从Reference构成的pending链表上获取Reference对象,如果能获取则根据Reference的具体类型进行不同的处理,不能则调用wait方法等待GC回收对象处理pending链表的通知。ReferenceHandler线程run方法源码:

public void run() {
    //死循环,线程启动后会一直运行
    while (true) {
        tryHandlePending(true);
    }
}

 run内部调用的tryHandlePending源码:

static boolean tryHandlePending(boolean waitForNotify) {
    Reference<Object> r;
    Cleaner c;
    try {
        synchronized (lock) {
            if (pending != null) {
                r = pending;
                //instanceof 可能会抛出OOME,所以在将r从pending链上断开前,做这个处理
                c = r instanceof Cleaner ? (Cleaner) r : null;
                //将将r从pending链上断开
                pending = r.discovered;
             
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java线程池的工作原理是这样的:首先,线程池在创建时会向系统申请一个用于执行线程队列和管理线程池的线程资源。然后,在调用execute()方法添加一个任务时,线程池会按照以下流程执行任务。 1. 当线程池接收到一个新任务时,会判断线程池中是否有空闲的线程可用。如果有,就将任务分配给其中一个空闲线程;如果没有,就将任务添加到线程队列中等待执行。 2. 线程池会从线程队列中选择一个任务,然后将其分配给一个空闲的线程执行。 3. 执行任务的线程会从任务队列中取出任务,然后执行任务的代码逻辑。 4. 任务执行完毕后,线程并不会被销毁,而是会再次回到线程池中成为空闲状态,等待下一个任务的到来。 5. 如果线程池中的线程数量超过了设定的最大线程数,多余的线程会被暂时停用。当任务队列中有新任务到来时,线程池会再次激活这些暂时停用的线程来执行任务。 Java中的线程池是通过Executor框架来实现的,其中使用了Executor、Executors、ExecutorService、ThreadPoolExecutor、Callable、Future、FutureTask等核心类。这些类提供了方便的方法来管理线程池的创建、执行和结束等操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Java 线程池的工作原理](https://blog.csdn.net/weixin_45970271/article/details/125408580)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值