本文首发公众号洪流学堂。洪流学堂,让你快人几步。

今天分享一个网易游戏的面试题:

题目

请简述强引用和弱引用。

参考答案

我们平常用的都是对象的强引用,如果有强引用存在,GC是不会回收对象的。我们能不能同时保持对对象的引用,而又可以让GC需要的时候回收这个对象呢?.NET中提供了WeakReference来实现。弱引用可以让您保持对对象的引用,同时允许GC在必要时释放对象,回收内存。对于那些创建便宜但耗费大量内存的对象,即希望保持该对象,又要在应用程序需要时使用,同时希望GC必要时回收时,可以考虑使用弱引用。

什么是强/弱引用

了解弱引用之前,先了解一下什么是强引用

例如:Cat cat = new Cat();就是一个强引用,内存分配一份空间给用以存储Cat数据,这块内存有一个首地址,也就是cat所保存的数据,内存分配的空间中不仅仅保存着Cat对象信息,还保存着自己(Cat本身)被引用的次数。

当一个对象被强引用的形式创建的时候,本身被引用的次数已经为1。

接着如果再执行Cat c=cat; 这句代码执行之后,cat指向的Cat的存储空间已经被引用了2次,所以Cat保存的被引用数为2。

总结:强引用最终导致的结果就是被引用的对象的被引用次数+1;

相反的弱引用就是不会对被引用对象的被引用次数有任何影响。

怎样使用弱引用

弱引用类:

WeakReference //有两个重载的构造函数
WeakReference WeakObj=new WeakReference(Object);//弱引用方式

IsAlive属性是判断此弱引用对象所引用的对象是否还存在,存在:IsAlive=True;
Target属性是设置该弱引用对象所引用的数据对象的值

弱引用使用起来很简单,看下面的代码:

Cat cat= new Cat ();
WeakReference wref = new WeakReference(cat);
cat= null;

第一行代码新建了一个新的对象,这里叫它对象A,cat是对对象A的强引用。接着第二行代码新建了一个弱引用对象,参数就是对象A的强引用,第三行代码释放掉对对象A的强引用。这时如果GC进行回收,对象A就会被回收。

怎样在取得对象A的强引用呢?很简单,请看代码2:

WeakReference weakReference = new WeakReference(cat);
Cat strongReference = weakReference.Target as Cat;
if (strongReference != null)
{// Cat对象实例尚未被垃圾回收,可以通过strongReference进行访问
}
else
{// Cat对象实例已被垃圾回收
}

只要显示的将弱引用的Target属性附值就会得到弱引用所代表对象的一个强引用。不过在使用对象之前要对其可用性进行检查,因为它可能已经被回收了。如果你得到的是null,表明对象已经被回收,不能再用了,需要重新分配一个。如果不是null,就可以放心大胆的用 了。

弱引用的内部实现

弱引用看起来很神奇,似乎是凌驾于正常的垃圾回收机制之上的,它究竟是如何实现的呢?其实WeakReference类型在内部封装了一个名为GCHandle的struct类型,正是这个GCHandle使弱引用成为可能。

CLR中的每个AppDomain都拥有一个GC句柄表。这个表的每一项记录有两个信息,一个是指向堆中某个对象的指针,另一个是这个表项的类型。总共有4种表项类型,其中Weak和WeakTrackResurrection两种类型和我们今天所讨论的弱引用相关。GCHandle这个类提供了一些操纵GC句柄表的方法。我们可以使用它的Alloc方法向GC句柄表中添加一个指定类型的表项。当垃圾回收开始后,垃圾回收器找到所有可达对象(简单的说,就是有用的对象)。然后遍历GC句柄表中每个Weak类型的表项,如果发现某表项所指的对象不属于可达对象,则会把该表项的对象指针设置为null。紧接着,垃圾回收器会找出所有不可达对象中定义了析构函数的对象,并把他们放到一个被称为freachable的队列中(freachable中的对象会等待一个CLR中特定的线程来调用他们的终结函数)。由于这些freachable中的对象现在又被freachable队列所引用,所以它们又成为可达对象了。此时,垃圾回收器会遍历GC句柄表中所有WeakTrackResurrection类型的表项,和刚才一样,如果某表项所指的对象不属于可达对象,则会把该表项的对象指针设置为null。此处需注意,对于那些一开始被判定为不可达且定义了析构函数的对象来说,它们在GC句柄表中所对于的表项指针仍然不是null。这就是Weak和WeakTrackResurrection两种类型的区别。

WeakReference就是通过表示了某个GC句柄表表项的GCHandle对象来完成跟踪对象生命周期的功能的。你也一定可以看出短弱引用利用了Weak类型的GC句柄表项,而长弱引用则利用了WeakTrackResurrection类型的表项。

WeakReference的一些注意事项

首先,WeakReference自身也实现了析构函数。也就是说,它即使不再被使用了,也不会被立即回收,而是会在内存里赖着多活一会(可能会经历不止一次的垃圾回收)。

另外,如上一节所说,WeakReference会向GC句柄表添加一个表项。而每次垃圾回收,GC句柄表都会被遍历一遍。可想而知,如果系统中存在大量的WeakReference,那么GC句柄表很可能也会非常庞大,导致垃圾回收的效率降低。

WeakReference经常会和缓存联系起来,但是它并不适和用来实现一个大型的缓存机制。这是为什么呢?一方面如前文所述,WeakReference自身实现了析构函数,也有可能导致垃圾回收的效率降低,因此应该避免在内存中创建大量的WeakReference对象实例。另一方面,我们知道一个对象如果没有被任何强引用所指向,而仅仅被弱引用所指向,那么它很有可能活不过一次垃圾回收。所以通过这样的方式所实现出来的缓存机制势必有着非常短促的缓存策略,而这种策略在大部分情况下都不会是你期望得到的。

WeakReference的三个使用场景

对象缓存

试想这样一个场景,我有一个内存受限的程序,在这个程序里经常会使用一个占用很多内存的位图对象,所幸生成这个位图对象并不复杂。所以我每次要使用那个位图对象的时候都会重新生成它,使用完毕之后就将其丢弃(不保留它的引用)。

这种方式完全能够满足我的需求,但是还能不能再优化呢?分析一下我们就可以发现,当我需要使用位图对象的时候,我上次使用的那个位图对象虽然被我丢弃了,但可能仍然没有被垃圾回收,仍然存在内存中。此时如果我能直接使用这个位图对象,就可以节省出因重建位图对象而浪费的内存和CPU资源。

改进措施很简单——使用完位图对象后,不是直接丢弃,而是用一个弱引用指向它。待下次访问位图对象时,就可以先通过弱引用判断位图对象是否还在内存中,如果还在则直接使用,否则重新创建。

辅助调试

有时,对于某种类型,我们需要知道当前程序中存在有多少对象实例,以及存在的都是哪些实例,以便于我们进行一些性能分析。这时,我们就可以使用到弱引用了。例如下面的代码:

public class A
{private static List<WeakReference> _instances = new List<WeakReference>();public A(){_instances.Add(new WeakReference(this));}public static int GetInstanceCount(){GC.Collect();return _instances.Count(x => x.Target != null);}
}

GetInstanceCount方法可以得到内存中A类型的实例个数。另外,还可以通过instances集合来检查内存中的A类型实例都有哪些。在调试内存泄露问题的时候,这些信息都可以派上用场。

相比于各种性能分析Profiler工具,这种方法更加轻巧便捷。

弱事件

.Net中的事件有时会引起内存泄露问题。例如,A注册了B的某个事件,此时B就会暗中保留A的一个强引用,导致A无法被内存回收,直到B被回收或A反注册了B的事件。例如,我有一个对象注册了主窗口的Loaded事件,只要我不反注册该事件,那么主窗口会一直引用该对象,直到主窗口被关闭,该对象才会被回收。所以,每当我们注册某个对象的事件时,都有可能在不经意间埋下内存泄露的隐患。

解决这个问题的根本方法是,在必要的时候进行事件的反注册。但是,在某些情况下,我们可能很难判定这个“必要的时候”。另外,当我们作为类库的提供者时,我们也很难保证类库的使用者都记得要反注册事件。因此,另一个解决方案就是使用弱事件。

弱事件的实现原理很简单,就是对事件进行一层封装。不让事件发布者直接引用监听者,而是让他们保留一个监听者的弱引用。当事件触发时,发布者会先检查监听者是否还存在于内存中,如果存在才通知它。如此一来,监听者的生命周期就不会依赖于发布者了。

现在试试这个面试题

【网易】四元数在左手坐标系和右手坐标系下区别和转换。

更多游戏大厂面试题,在洪流学堂公众号中回复面试题学习

【网易游戏面试题】.NET中强引用和弱引用是什么相关推荐

  1. 的引用_java中的强引用,软引用,弱引用,虚引用

    一对象回收算法 Java在GC时判断对象是否存活有两种方式:第一种是引用计数方式,第二种是可达性分析算法: 引用计数器算法: 在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一:当引用 ...

  2. 网易游戏面试题刷刷刷(1)

    被多校赛虐的心累,来找一下网易游戏面试题做做,看看自己还有多少差距. 第一回 牛客网/公司真题/网易游戏(互娱)-游戏研发/初级游戏研发/平台开发岗部分真题汇总 https://www.nowcode ...

  3. c语言弱符号与函数指针,浅谈C语言中的强符号、弱符号、强引用和弱引用【转】...

    首先我表示很悲剧,在看<程序员的自我修养--链接.装载与库>之前我竟不知道C有强符号.弱符号.强引用和弱引用.在看到3.5.5节弱符号和强符号时,我感觉有些困惑,所以写下此篇,希望能和同样 ...

  4. 【Java】强软弱虚四种引用,弱引用在ThreadLocal中的应用

    Java中的引用类型 - 强软虚弱 1.强引用(StrongReference) Object strongReference = new Object(); 只要有引用指向它,就不会被回收.当内存空 ...

  5. Java中的强引用、软引用、弱引用与虚引用

    一.前言 今天我们浅谈一下java的四种引用,分别是强引用.软引用.弱引用与序引用.在谈引用之前我们不得不提一下Java的垃圾回收器机制GC,GC算是Java的一大特点,我们都知道c语言是不能自动释放 ...

  6. JavaScript中强引用和弱引用

    在计算机程序设计中,有一个弱引用的概念: 一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的,并因此可能在任何时刻被回收. 在JS中,WeakMap 和 WeakSet 给我们提供了弱引用 ...

  7. JAVA中强引用和弱引用的区别

    前景概述: 在java中,每一个变量都像是我们生活中手边的生活用品,有的使用频繁,有的使用不频繁,在JDK1.2以前的版本中,当一个对象不被任何变量引用,那么程序就无法再使用这个对象.也就是说,只有对 ...

  8. java强引用、软引用、弱引用、虚引用-Java的引用类型总共有四种,你都知道吗

    目录 谈引用 强引用(Strong Reference)--不回收 强引用例子 软引用(Soft Reference)--内存不足即回收 弱引用(Weak Reference)--发现即回收 面试题: ...

  9. java强引用弱引用_Java 的强引用、弱引用、软引用、虚引用

    1.强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.如下: Object o=new Object(); // 强引用 当内存空间 ...

最新文章

  1. 计算属性一般是没有set方法, 只读属性 ||计算属性的缓存
  2. akka---Getting Started Tutorial (Java): First Chapter
  3. 零基础的跨界自学拿到腾讯Offer的学习资料
  4. 【Linux】一步一步学Linux——touch命令(33)
  5. Continue to debug QDD504 read - Service Order extractor debug
  6. idal 创建springboot 项目_SpringBoot教程:Maven方式创建SpringBoot项目
  7. c语言 将点同时保证x坐标从小到大,y坐标从小到大地排序,C语言程序设计实验报告(二)...
  8. QT嵌入式之_QT介绍_安装_以及环境变量配置---QT嵌入式图形框架工作笔记001
  9. Spring源码系列(十一)——Spring源码总结
  10. ROS配置环境搞错了,每次开终端都有bash: export: `/home/yyq/ork_ws/devel/setup.bash‘: 不是有效的标识符
  11. PHP - 支付宝小程序授权登录
  12. wordpress文章页面添加字体增大减小链接
  13. CodeForces 868C Qualification Rounds
  14. 不推荐二周目跑重复剧情 不推荐开混10,建议开混11,游戏文本的奖励减半并没有生效. 混10=混11+混乱词条 1.经济获取(学自大佬:小小银Salmon【B站号】) 金币获取:刚通关开混乱11,拿主
  15. uniapp微信小程序打电话
  16. 汇编MOVSX指令详解
  17. docker容器的创建
  18. 百度CEO李彦宏:电子商务平台将在年前发布
  19. ubuntu安装与卸载搜狗输入法
  20. 2008 网马王网站分析

热门文章

  1. (转)jquery基础教程八 load方法及小技巧
  2. 在Eclipse新建菜单中添加菜单项,其他地方添加菜单项类似
  3. python简单的购物程序代码-Python实现购物程序思路及代码
  4. 网络存储空间_网络存储服务器的三大分类,你都清楚吗?
  5. java uml eclipse_eclipse uml 工具
  6. python猴子偷桃递归_C++猴子偷桃问题
  7. redhat怎样修改语言_硕士博士个人陈述(PS)辅导及修改服务带你极速前进!
  8. c++ 项目_罗纳尔多相信C罗从事技巧类项目,其成就不会亚于他在足坛的成绩
  9. 草莓甜品海报设计,甜出画面,受得住诱惑么?
  10. UI界面排版搞不定 ?看看这些优秀的实例模板,可临摹学习!