過早的給方法中 引用對象 設為 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.pswp.cn/news/287495.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/287495.shtml
英文地址,請注明出處:http://en.pswp.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…