前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.Net性能调优-WeakReference

.Net性能调优-WeakReference

作者头像
蓝夏
发布2021-09-22 11:06:33
3270
发布2021-09-22 11:06:33
举报
文章被收录于专栏:bluesummer

概述

弱引用

GC在回收时检测对象是否有强引用,如果没有则可以执行回收。

那么什么是强引用的对象?简单概括说就是程序当前可以访问的对象。举两个例子

  • 某个类里定义了一个静态变量GlobalConfig,那这个GlobalConfig就是被强引用的对象,如果设置GlobalConfig=null,强引用就消失了
  • 在任意函数Method1()中,某一行执行一段代码var user=new User();new User()这个对象就会被强引用,想要去除他的强引用,设置user=null,或者在函数Method1()执行完成后该强引用也会消失

适用场景

创建回收简单,但是占用大量内存的对象,大对象

不适用场景

  1. 对象的结构复杂,弱引用对象内部的资源可能被销毁等,比如弱引用一个MQHelper,MQHelper又有一个属性Connection,Connection可能被释放了,但是MQHelper仍然被弱引用
  2. 对象更新频率过高 ,高于弱引用对象的销毁周期,并且业务无法忍受这种延迟。可通过在更新对象时设置弱引用对象为null或者更新弱引用的值来解决
  3. 维护空弱引用会长期占用过多的资源。比如建立一个字典Dictionary<id,WeakReference>存了一百万个用户的弱引用,即使所有的弱引用的对象都回收了,这个字典也会长期保持一百万个id的键和WeakReference对象本身

除了WeakReference他还有个泛型类WeakReference,两者只是提供的Api有些差别

我习惯用泛型类,下面就用泛型类来继续介绍了

创建弱引用

代码语言:javascript
复制
var weakReference = new WeakReference<Article>(new Article() { Id = 1},true)

构造函数接收两个参数

  • Target: 引用的对象
  • trackResurrection: 如果为true,并且对象实写了析构函数,则该对象在没有被强引用之后可以存活过一次GC

获取弱引用对象的强引用

代码语言:javascript
复制
var isSuccess= weakReference.TryGetTarget(out Article article)

重新设置弱引用的对象

代码语言:javascript
复制
weakReference.SetTarget(article)

怎样理解这个trackResurrection呢?请看代码

代码语言:javascript
复制
public class Article
{
    public Article() => Console.WriteLine("create new Article");
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    ~Article()
    {
        Console.WriteLine("#################################");
    }
}

WeakReference<Article> weakReference = new WeakReference<Article>(new Article() { Id = 1, Title = "title", Description = "desc" }, true);
public void TraceTest()
{
    if (IsArticleAlive())
    {
        Console.WriteLine("article1 is alive");
    }
    GC.Collect();
    GC.WaitForPendingFinalizers();

    if (IsArticleAlive())
    {
        Console.WriteLine("article2 is alive");
    }
    else
    {
        Console.WriteLine("article2 is null");
    }

    GC.Collect();
    GC.WaitForPendingFinalizers();

    if (IsArticleAlive())
    {
        Console.WriteLine("article3 is alive");
    }
    else
    {
        Console.WriteLine("article3 is null");
    }
}
public bool IsArticleAlive() => weakReference.TryGetTarget(out Article article);

执行结果:

代码语言:javascript
复制
create new Article
article1 is alive
#################################
article2 is alive
article3 is null

可以看到在第一次GC回收时,执行了Article的析构函数,但是article仍然存活,直到第二次GC执行之后,article被回收。如果设置trackResurrection为false,则article2 is null.

如果我的析构函数里将Title变为了null呢?下次获取到的article的Title就是null了,所以说弱引用的对象需要是一个简单的对象,连IDisposable都没有实现的对象

应用

据说弱引用事件是弱引用最适合的场景,但是并没有发现很好的实现方式,要么就是过于的复杂,暂时就不研究他了,大概说下为什么事件适合弱引用

比如我有一个Publisher用来发布事件,Consumer订阅事件。那么每次订阅事件的时候,Publisher的事件都会保存一个回调函数。

如果回调函数里又引用了Consumer本身的成员变量,那么创建100个Consumer,Publisher就会包含一百个回调函数,同时这100个Consumer也不会释放。当然正常的写法我们会在Consumer用完的时候用Event的-=移除事件。弱引用的作用就是防止Consumer忘记移除了,最终造成内存溢出

我这里想到的场景是用弱引用来保存数据库或其他存储区的大对象,更像是一种内存缓存的用法,但是与内存缓存不同的是,它的生命周期不可控制,不会影响GC的回收。

上代码

代码语言:javascript
复制
private static ConcurrentDictionary<int, WeakReference<Article>> _cache = new ConcurrentDictionary<int, WeakReference<Article>>();
  
public Article GetArticleByWeakReference(int id)
{
    bool created = false;
    var weakRef = _cache.GetOrAdd(id, i =>
    {
        created = true;
        Console.WriteLine("created " + i);
        return new WeakReference<Article>(_articleRepository.GetArticle(i));
    });

    weakRef.TryGetTarget(out Article article);

    //如果弱引用的对象已被回收,则重新获取对象填充
    if (article == null)
    {
        Console.WriteLine("article " + id + " is null-----------------------------");
        article = _articleRepository.GetArticle(id);
        weakRef.SetTarget(article);
    }
    else
    {
        //弱引用之前获取过,但是引用的对象已被回收
        if (!created)
            Console.WriteLine("article" + id + " is not null");
    }
    return article;
}

需要注意的是,如果Article过多,会让_cache本身变得过大,需要综合考虑

第二点就是Article在更新的时候,需要更新弱引用的对象weakRef.SetTarget(article),也可以考虑只把Article中占用空间最大的Description做弱引用。查询的时候先判断Description是否存在,再考虑拼接查询条件是否同时查出Description还是只需要查询id和name

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
  • 适用场景
  • 不适用场景
  • 创建弱引用
  • 应用
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档