首页 > 编程语言 >浅谈 Java 引用:弱引用 WeakReference

浅谈 Java 引用:弱引用 WeakReference

时间:2024-06-12 11:29:06浏览次数:17  
标签:... obj 浅谈 hostname Gauge 引用 new WeakReference PoolStats

前言

最近在测试 micrometer 的 Gauge 度量时,发现被观察的目标对象,在一开始时能被采集到指标,过了一段时间后(jvm发生了gc),被观察对象的指标采集不到了,经过跟踪发现,Gauge在构建被观察对象时,使用了Java的弱引用。

测试场景回溯

  • 目标

为了采集 httpclient 的连接管理器(连接池)的指标,如 leased、pending、available、max

public interface ConnPoolControl<T> {
    ... 其它略
    PoolStats getTotalStats();
    PoolStats getStats(final T route);
}

public class PoolStats implements Serializable {
    ... 其它略
    private final int leased;
    private final int pending;
    private final int available;
    private final int max;
}
  • 伏笔点:获取连接池统计对象(PoolStats)
public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>> implements ConnPool<T, E>, ConnPoolControl<T> {
    ... 其它略
    public PoolStats getTotalStats() {
        this.lock.lock();
        try {
            return new PoolStats(						// 注意: 这里是 new 出的对象
                    this.leased.size(),
                    this.pending.size(),
                    this.available.size(),
                    this.maxTotal);
        } finally {
            this.lock.unlock();
        }
    }

    @Override
    public PoolStats getStats(final T route) {
        Args.notNull(route, "Route");
        this.lock.lock();
        try {
            final RouteSpecificPool<T, C, E> pool = getPool(route);
            return new PoolStats(					// 注意: 这里是 new 出的对象
                    pool.getLeasedCount(),
                    pool.getPendingCount(),
                    pool.getAvailableCount(),
                    getMax(route));
        } finally {
            this.lock.unlock();
        }
    }
    ... 其它略
}
  • 问题点:构建&注册 Gauge 度量
// 业务代码
PoolingHttpClientConnectionManager cm = HttpClientPoolUtil.getConnectionManager();
if (cm != null) {
    for (HttpRoute route : cm.getRoutes()) {
        String hostname = route.getTargetHost().getHostName();
        PoolStats poolStats = cm.getStats(route); // 注意:这里 getStats 返回的是 new 出的对象
        Gauge.builder("httpclient_leased", poolStats, PoolStats::getLeased).tag("hostname", hostname).register(Metrics.globalRegistry);
        Gauge.builder("httpclient_available", poolStats, PoolStats::getAvailable).tag("hostname", hostname).register(Metrics.globalRegistry);
        Gauge.builder("httpclient_pending", poolStats, PoolStats::getPending).tag("hostname", hostname).register(Metrics.globalRegistry);
        Gauge.builder("httpclient_max", poolStats, PoolStats::getMax).tag("hostname", hostname).register(Metrics.globalRegistry);
    }
}

// 下面的是 micrometer 组件源码(只贴了相关的)
public abstract class MeterRegistry {
    ... 其它略
    <T> Gauge gauge(Meter.Id id, @Nullable T obj, ToDoubleFunction<T> valueFunction) {
        return registerMeterIfNecessary(Gauge.class, id, id2 -> newGauge(id2, obj, valueFunction), NoopGauge::new);
    }

    protected abstract <T> Gauge newGauge(Meter.Id id, @Nullable T obj, ToDoubleFunction<T> valueFunction);
    ... 其它略
}

public class CompositeMeterRegistry extends MeterRegistry {
    ... 其它略
    @Override
    protected <T> Gauge newGauge(Meter.Id id, @Nullable T obj, ToDoubleFunction<T> valueFunction) {
        return new CompositeGauge<>(id, obj, valueFunction);
    }
    ... 其它略
}

class CompositeGauge<T> extends AbstractCompositeMeter<Gauge> implements Gauge {
    ... 其它略
    CompositeGauge(Meter.Id id, @Nullable T obj, ToDoubleFunction<T> f) {
        super(id);
        ref = new WeakReference<>(obj); // obj 是被观察的目标对象
        this.f = f;
    }
    ... 其它略
}

在看到 ref = new WeakReference<>(obj); 这一句时,问题就可以结束了。

下面用图表述问题:当栈中引用结束后,只剩下方法区的常量对象(即globalRegistry)meterMap中的Gauge对象,以弱引用的形式执行堆在的对象PoolStats,这时候,对于jvm来说,PoolStats 对象就可以被 gc 了,因此问题发生。

最后

只要规避堆中的对象仅仅只被一个弱引用指向(还被强引用指向),此问题就不会发生,不在此赘述。

标签:...,obj,浅谈,hostname,Gauge,引用,new,WeakReference,PoolStats
From: https://blog.csdn.net/qq_43083126/article/details/139621743

相关文章

  • 使引用作为函数参数将变量i和j的值互换
            C++之所以增加引用机制,主要是把它作为函数参数,以扩充函数传递数据的功能。解题思路:        传递变量的地址。形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)得到实参变量的地址,因此指向实参变量单元。编写程序:运行结果:程序分析:  ......
  • 如果引用另一个文件函数
    提问Rust如果引用另一个文件函数回答使用pubpubfnfib(n:u32)->u32{returnifn<2{n}else{fib(n-1)+fib(n-2)}}参考https://rustwiki.org/zh-CN/book/ch07-05-separating-modules-into-different-files.html#:~:text=Rust......
  • 嵌入式浅谈之CANopen
    在工业控制和汽车领域,CAN是一种流行的现场总线,到处都可以看到它的身影。而CAN协议本身只定义了物理层和链路层,对应用层没有规定。各家都可以自定义自己的应用层协议内容,但是这样一来各家协议就不能兼容,大家各自搞一套,似乎是有点重复造轮子的意思,而且协议稳定需要较长的验证,这样......
  • C++缺省参数、缺省参数的概念、缺省参数的分类、函数重载、函数重载的概念、C++支持函
    文章目录前言一、缺省参数1.缺省参数的概念2.缺省参数的分类二、函数重载1.函数重载的概念2.C++支持函数重载的原理三、引用1.引用的概念2.引用的特性3.常引用4.引用的使用场景5.传值和传引用效率比较6.引用和指针的区别总结前言C++缺省参数、缺省参数......
  • 嵌入式浅谈之“梯形”加减速MCU算法实现
    书接上回,上章我们讲到原理,本章我们来聊聊实现。在笔者的实际项目经历中,梯形加减速运用的比较广泛,主要以其优秀的加减速能力、对算法实现资源的需求较小、实现难度适中而被广泛应用。下面就简单介绍一下基于MCU的算法实现过程,以STM32为例。采用“梯形”加减速算法,在运动过......
  • 浅谈2024年全国中学生生物学联赛
    坐标gd谈谈联赛流水账day-n吃了蛋糕感谢家乐哥、圆领姐、刘sir和黄豆前来捧场,不过忘记叫盛哥来了教练把已经退役的人拉回来考试?然后在教学楼大发雷霆这几天身体一直不太好,先是发烧,退烧了之后就开始感冒+腹泻,十分难受。保济丸狂闷随便看了点比解day0感冒好得差不多,疯狂......
  • JAVA lambda表达式方法引用+构造器引用
    若Lambda体中的内容有方法已经实现了,使用“方法引用”注意:Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。静态方法与实例方法的区别1、静态方法属于整个类所有,因此调用它不需要实例化,可以直接调用(类.静态方法())。实例......
  • Dubbo 3.x源码(21)—Dubbo服务引用源码(4)
    基于Dubbo3.1,详细介绍了Dubbo服务的发布与引用的源码。此前我们学习了createInvokerForRemote方法中的Wrapper有哪些以及作用,接下来我们将会的学习真正的本地、应用级别、接口级别的Protocol的引入逻辑,以及创建Proxy服务接口代理对象的逻辑。Dubbo3.x服务引用源码:Dub......
  • 基本类型值,是按值复制的,而不是按引用复制的。(深浅拷贝)
    letobj=[1,2,4]letobj2=Array.from(obj)obj2[0]=23console.log(obj)这是浅拷贝吗?在给出的例子中,Array.from(obj) 实际上执行的是对数组 obj 的浅拷贝。这是因为数组在JavaScript中是一种特殊的对象,其元素存储在索引属性中。Array.from() 方法创建了一个新的数组实例......
  • 一篇文章带你搞懂C++引用(建议收藏)
    引用6.1引用概念引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"typedef是给类型取别名引用是给变量取别名注意:引用类型必须和引用实体是同种类......