过早的给方法中 引用对象 设为 null 可被 GC提前回收吗?

经常在代码中看到有人将 null 赋值给引用类型,来达到让 GC 提前回收的目的,这样做真的有用吗?今天我们就来研究一下。

为了方便讲解,来一段测试代码,提前将 test1=null ,然后调用 GC.Collect() 看看是否能提前回收。

平台采用: .net5

public class Program{static void Main(string[] args){ProcessRequest();}static void ProcessRequest(){var test1 = new Test() { a = 10 };Console.WriteLine($"query.a={test1.a}");var test2 = new Test() { a = 11 };Console.WriteLine($"query.a={test2.a}");//提前释放test1 = null;var test3 = new Test() { a = 12 };Console.WriteLine($"query.a={test3.a}");GC.Collect();Console.WriteLine("垃圾回收啦!");Console.ReadLine();}}public class Test{public int a;}

接下来我们从 DebugRelease 两种模式下观察。

一:Debug 模式

要找到这个答案,我们用 windbg 附加一下,找到 test1 然后用 !gcroot 查看下引用即可。

0:000> !clrstack -a
OS Thread Id: 0x4dd0 (0)
Child SP       IP Call Site
0057F2A4 79863539 System.Console.ReadLine() [/_/src/System.Console/src/System/Console.cs @ 463]
0057F2AC 04c405d1 ConsoleApp1.Program.ProcessRequest() [D:\net5\ConsoleApp2\ConsoleApp1\Program.cs @ 37]LOCALS:0x0057F2D4 = 0x000000000x0057F2D0 = 0x0283cd540x0057F2CC = 0x0283cd900:000> !dumpheap -type TestAddress       MT     Size
0283a7c0 04c39008       12     
0283cd54 04c39008       12     
0283cd90 04c39008       12     0:000> !gcroot 0283a7c0
Thread 4dd0:0057F2AC 04C405D1 ConsoleApp1.Program.ProcessRequest() [D:\net5\ConsoleApp2\ConsoleApp1\Program.cs @ 37]ebp+14: 0057f2c8->  0283A7C0 ConsoleApp1.TestFound 1 unique roots (run '!gcroot -all' to see all roots).

是不是很惊讶,test1 虽被赋 null,但并没有被 GC.Collection 所回收,原因在于 test1 被栈中的 ebp+14 位置所持有?那这个位置是咋回事?我们反编译下代码看看,简化后如下:

0:000> !U 04C405D1
Normal JIT generated code
ConsoleApp1.Program.ProcessRequest()
ilAddr is 0268205C pImport is 052FB030
Begin 04C40488, size 154D:\net5\ConsoleApp2\ConsoleApp1\Program.cs @ 22:
04c404aa b90890c304      mov     ecx,4C39008h (MT: ConsoleApp1.Test)
04c404af e8182c9afb      call    005e30cc (JitHelp: CORINFO_HELP_NEWSFAST)
04c404b4 8945ec          mov     dword ptr [ebp-14h],eax
04c404b7 8b4dec          mov     ecx,dword ptr [ebp-14h]
04c404ba ff152890c304    call    dword ptr ds:[4C39028h] (ConsoleApp1.Test..ctor(), mdToken: 06000004)
04c404c0 8b4dec          mov     ecx,dword ptr [ebp-14h]
04c404c3 c741040a000000  mov     dword ptr [ecx+4],0Ah
04c404ca 8b4dec          mov     ecx,dword ptr [ebp-14h]
04c404cd 894df8          mov     dword ptr [ebp-8],ecxD:\net5\ConsoleApp2\ConsoleApp1\Program.cs @ 29:
04c4055c 33c9            xor     ecx,ecx
04c4055e 894df8          mov     dword ptr [ebp-8],ecx

虽然 !gcroot 上显示的是 ebp+14,反向就是 ebp-14,仔细看上面的汇编代码,可以发现 test1 实例被放在了 ebp-14ebp-8 两个栈位置,而 test1=null 只是抹去了 ebp-8 的栈单元,所以它能被回收的时机只能是等 ProcessRequest() 方法销毁之后,这也就是 Debug 模式下的 方法作用域,应该是为了 Debug 调试用的,从 gcinfo 上也可以看出来,ebp-14 是禁止被GC跟踪的内部用途的栈单元。

0:000> !U -gcinfo 04C405D1
Normal JIT generated code
ConsoleApp1.Program.ProcessRequest()
ilAddr is 0268205C pImport is 052FCA58
Begin 04C40488, size 154D:\net5\ConsoleApp2\ConsoleApp1\Program.cs @ 21:[EBP-08H] an untracked  local[EBP-0CH] an untracked  local[EBP-10H] an untracked  local[EBP-14H] an untracked  local[EBP-18H] an untracked  local[EBP-1CH] an untracked  local[EBP-20H] an untracked  local[EBP-24H] an untracked  local[EBP-28H] an untracked  local[EBP-2CH] an untracked  local[EBP-30H] an untracked  local

二:Release 模式

大家或许都知道 Release 是一种高度优化的激进模式,我也很好奇在这种模式下 compile 或者  JIT 会做出怎么样的优化。

1. 编译器层面的优化

要寻找这个答案,我们用 ILSpy 打开生成的 IL代码,简化后如下:

.method private hidebysig static void ProcessRequest () cil managed {// Method begins at RVA 0x2058// Code size 144 (0x90).maxstack 3.locals init ([0] class ConsoleApp1.Test test1,[1] class ConsoleApp1.Test test2,[2] class ConsoleApp1.Test test3)IL_0050: ldnullIL_0051: stloc.0} // end of method Program::ProcessRequest

idnull 上来看,没有做任何优化,居然直接翻译了,哎。。。

2. JIT优化

查看 JIT 层面的优化,只能看最终的汇编代码托管堆 啦。

0:000> !dumpheap -type TestAddress       MT     Size
02eaab38 02634b10       12     
02ead344 02634b10       12     
02ead380 02634b10       12     Statistics:MT    Count    TotalSize Class Name
02634b10        3           36 ConsoleApp1.Test
Total 3 objects0:000> !U /d 0262549d
Normal JIT generated code
ConsoleApp1.Program.ProcessRequest()
ilAddr is 025B2058 pImport is 04AFB108
Begin 02625370, size 131D:\net5\ConsoleApp2\ConsoleApp1\Program.cs @ 22:
02625370 55              push    ebp
02625371 8bec            mov     ebp,esp
0262538a b9104b6302      mov     ecx,2634B10h (MT: ConsoleApp1.Test)
0262538f e83cddfefd      call    006130d0 (JitHelp: CORINFO_HELP_NEWSFAST)
02625394 8945f0          mov     dword ptr [ebp-10h],eax
02625397 8b4df0          mov     ecx,dword ptr [ebp-10h]
0262539a e871f9ffff      call    02624d10
0262539f 8b4df0          mov     ecx,dword ptr [ebp-10h]
026253a2 c741040a000000  mov     dword ptr [ecx+4],0Ah
026253a9 8b4df0          mov     ecx,dword ptr [ebp-10h]
026253ac 894dfc          mov     dword ptr [ebp-4],ecxD:\net5\ConsoleApp2\ConsoleApp1\Program.cs @ 29:
02625430 33c9            xor     ecx,ecx
02625432 894dfc          mov     dword ptr [ebp-4],ecx

从汇编代码看,Release 模式下也是采用双栈保存的,也就是 方法级作用域

二:可以得出结论了吗?

至少在 .NET5 平台, ReleaseDebug 模式下的 test1 = null; 是没有任何区别的,其实这里有个问题 , .NET5 下没区别,不代表其他平台下也没有问题,毕竟不同的 JIT 会作用不同的抉择,接下来我们将同样的代码搬到 .NET Framework 4.5 下看看情况。

1. .NET Framework 4.5 平台

  1. Debug 模式

我们直接看托管代码

0:006> !dumpheap -type TestAddress       MT     Size
02564bfc 00754ddc       12     
02564c70 00754ddc       12     Statistics:MT    Count    TotalSize Class Name
00754ddc        2           24 ConsoleApp2.Test
Total 2 objects

居然是 2 个了,那为什么会这样呢?我们还是看下汇编。

0:000> !U /d 023509a6
Normal JIT generated code
ConsoleApp2.Program.ProcessRequest()
Begin 02350880, size 187
D:\net5\ConsoleApp2\ConsoleApp2\Program.cs @ 21:
023508b1 b9dc4da200      mov     ecx,0A24DDCh (MT: ConsoleApp2.Test)
023508b6 e839286cfe      call    00a130f4 (JitHelp: CORINFO_HELP_NEWSFAST)
023508bb 8945ec          mov     dword ptr [ebp-14h],eax
023508be 8b4dec          mov     ecx,dword ptr [ebp-14h]
023508c1 ff15fc4da200    call    dword ptr ds:[0A24DFCh] (ConsoleApp2.Test..ctor(), mdToken: 06000004)
023508c7 8b45ec          mov     eax,dword ptr [ebp-14h]
023508ca c740040a000000  mov     dword ptr [eax+4],0Ah
023508d1 8b45ec          mov     eax,dword ptr [ebp-14h]
023508d4 8945f8          mov     dword ptr [ebp-8],eax
D:\net5\ConsoleApp2\ConsoleApp2\Program.cs @ 28:
0235097b 33d2            xor     edx,edx
0235097d 8955f8          mov     dword ptr [ebp-8],edx0:000> dp ebp-14h L1
0019f4e8  02472358 0:000> !do 02472358
Name:        ConsoleApp2.Test
MethodTable: 00a24ddc
EEClass:     00a21330
Size:        12(0xc) bytes
File:        D:\net5\ConsoleApp2\ConsoleApp2\bin\Debug\ConsoleApp2.exe
Fields:MT    Field   Offset                 Type VT     Attr    Value Name
637342a8  4000001        4         System.Int32  1 instance       10 a0:000> dp 0019f4e8 L1
0019f4e8  02472358
0:000> !do 02472358
Free Object
Size:        24(0x18) bytes

大家可以仔细看看输出内容,虽然也是两个 栈位置 存放着 test1,但GC做了不同的处理,它无视 ebp-14 还牵引着 test1 的事实 ,直接将它标记为 free,这就有点意思了。

  1. Release 模式

我们直接用 !dumpheap -type Test 看托管堆。

0:006> !dumpheap -type TestAddress       MT     SizeStatistics:MT    Count    TotalSize Class Name
Total 0 objects

居然发现,不仅 test1 没有了,test2,test3 都没有了。。。这就是所谓的 激进式回收

三:结论

1.  .NET5 平台下

Release 和 Debug 模式下设置 test1=null 没有任何效果。

2. .NET Framework 4.5 平台下

Debug 模式下有效果,可以起到 提前回收 的目的。

Release模式下无效果,GC会自动激进的回收所有后续未使用到的引用对象。

3. 个人结论

总的来说,为了更好的平台兼容性,如果想提前回收,设置 test1 = null; 是有一定效果的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/287495.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[python opencv 计算机视觉零基础到实战] 十五 直方图反向投影

一、学习目标 了解了直方图反向投影的一般流程了解2D直方图的使用 如有错误欢迎指出~ 二、了解直方图反向投影 2.1 了解2D直方图 需要对直方图进行反向投影,需要使用2D直方图。2D直方图需要使用calcHist方法。calcHist方法在前两节中已经有了解,现在…

关联规则java代码_重量挖掘关联规则挖掘方法,哪个大神可以将以下伪代码转换为Java代码?...

重量挖掘关联规则挖掘方法,哪个大神可以将以下伪代码转换为Java代码? 10改进的加权关联规则算法的基本步骤与Apriori算法相似: 首先找到加权支持度不小于用户指定的最小加权支持度的所有频繁项集加权关联规则,然后使用频繁项集生成所有满足最…

Boostrap ZURB Foundation —— Web开发前端框架

webflow:Webflow 允许设计师通过自由的拖拉拽与 CSS 类互动,而定义它们的过程无需写任何一行代码。用户在完成从设计到 CSS 构架之后,甚至可以在线直接将建好的网页发布,而不需要导出代码到其他发布工具上。类似的这些 B2D 市场&a…

Git之HEAD和origin

1 问题 我们经常看见git相关操作里面看到HEAD和origin这些专业名称,它娘的到底什么意思。 2 解释 1)HEAD git 中的分支,本质上仅仅是个指向 commit 对象的可变指针, HEAD 是一个特别指针,它是一个指向你正在工作中的…

如何离线安装chrome插件

如何离线安装chrome插件 本文转自Work Hard Work Smart博客园博客,原文链接:http://www.cnblogs.com/linlf03/p/6838852.html,如需转载请自行联系原作者

多种语言《九九乘法表》荟萃:C、C++、C#、JavaScript、SQL、VB、VBA、Python

九九乘法表对于我们学习循环结构,尤其是双重循环特别有帮助,本文演示用C、C++、C#、HTML、SQL、VB、VBA、Python等多种语九九乘法表。 一、C语言 #include<stdio.h> main() {int i,j;for(i=1;i<=9;i++){for(j=1;j<=i;j++){printf("%d*%d=%d\t",j,i,i*j…

Git之删除远程分支

1 问题 在工作区间删除远程分支 2 删除命令 git push origin --delete name

iptables (2) 基本配置

iptables 基本命令使用举例 一、链的基本操作 1、清除所有的规则。1&#xff09;清除预设表filter中所有规则链中的规则。# iptables -F -F, --flush [chain]Flush the selected chain (all the chains in the table if none is given). This is equivalent to deleting all …

[python opencv 计算机视觉零基础到实战] 十六、用opencv画画

一、学习目标 了解如何使用line方法了解如何使用rectangle方法了解如何使用ellipse方法 如有错误欢迎指出~ 二、了解OpenCV的绘图方法 2.1 了解直线绘图方法 我们在前两节中有了解使用OpenCV中的矩形绘制&#xff0c;接下来我们了解一下更多的图形绘制方法。我们在OpenCV中…

基于.NetCore开发博客项目 StarBlog - (5) 开始搭建Web项目

系列文章基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客&#xff1f;基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目基于.NetCore开发博客项目 StarBlog - (3) 模型设计基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入基于.N…

java计算每个元素出现的百分比_Java程序计算百分比

百分数表示百分数(百)&#xff0c;即百分数与100之比。百分数的符号为&#xff05;。我们通常会计算获得的商标&#xff0c;投资回报率等百分比。该百分比也可以超过100&#xff05;。例如&#xff0c;假设我们有总数和一部分。所以我们说那一部分占总数的百分之几&#xff0c;…

ECShop 前台用户中心调整左侧栏目及中心部分呈现页面

1、首先调整用户中心左侧栏目&#xff0c;在 .dwt 里面我修改了半天&#xff0c;页面没改动。。后来某大神说这个是在另一个文件里&#xff1a;/themes/你用的模板目录/library/user_menu.lbi 文件改一个试试吧~ 不出意外的成功了吧2、中心部分呈现内容的调整。&#xff08;今天…

【测绘程序设计】高斯克吕格投影:带号及中央经度计算神器V1.0(附源程序)

【问题描述】:很多情况下,我们知道某一地点的坐标(经纬度),需要计算其在高斯克吕格投影中的带号及中央经度。关于该问题,有具体的公式可言,只是计算过程稍微繁琐一些,当然啦,我们可以写程序来解决,谁叫我们是名副其实的GISer呢?另外,这些复杂的计算过程还是交给计算…

外部中断0(含知识点)

1 #include "stm32f10x.h" // 相当于51单片机中的 #include <reg51.h>2 #include "stm32f10x_gpio.h"3 #include "stm32f10x_exti.h"4 #include "misc.h"5 6 /*外部中断配置*/7 8 9 int main(void)10 {11 /*************…

Git之tag

1 tag相关命令 1) 打上tag git tag v1.0.0 git tag -a v1.0.0 -m info 2) 查看tag git tag 3) 切换tag git checkout tagname 4) 删除tag git tag -d v1.0.0 5) 推送远程 推送单个tag git push origin v1.0.0 推送所有tag git push origin --tags

[python opencv 计算机视觉零基础到实战] 十七、用鼠标画画

一、学习目标 了解长轴和短轴参数了解旋转角度参数了解起始角度参数了解终止角度参数 二、深入了解OpenCV的ellipse方法 2.1 纵横 椭圆的绘制方法在上一节中我们已经知道了是使用ellipse&#xff0c;ellipse的函数原型如下&#xff0c;为了清晰认识ellipse方法&#xff0c;…

Microsoft Build 2022 到来,开发者们做好准备了吗?

Microsoft Build 2022 将会在5月24日 - 5月26日正式举行&#xff0c;作为开发者的你是否和我一样期待本次的 Build&#xff0c;它会带来什么的技术革新以及黑科技呢&#xff1f;下面我就针对今年 Build 的几个主题结合相关技术来谈谈我对 Build 的期待。开发技术和相关工具今年…

【测绘程序设计】视距测量神器V1.0(附源程序)

神器预览: 一、视距测量概念 视距测量是利用水准仪的望远镜内十字丝分划板上的视距丝在视距尺(水准尺)上读数,根据光学和几何学原理,同时测定仪器到地面点的水平距离和高差的一种方法。 视距测量具有操作简便、速度快、不受地面起伏变化的影响的优点,被广泛应用于碎部测…

补作业:随机生成二元四则运算

四则运算代码如下&#xff0c;基础不好&#xff0c;刚完成作业&#xff0c;希望老师多给点平时成绩&#xff0c;谢谢老师&#xff01;#include<stdio.h>#include<stdlib.h>#include<time.h>int right0,wrong0;int a,b,c,d;/*加法运算*/void add(){int i,j;fo…

java 从数据库取值_java 连接数据库取值问题

publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsIOException,ServletException{//TODOAuto-generatedmethodstubXzdatasqlToolnewXzdata();//初始化并...public void doGet(HttpServletRequest request,HttpServletResponse response) throws I…