GCHandle Leak
剛剛在園子裡看到一篇文章《.net中的遊魂現象》,正好這兩天關心這個問題,就打算再寫篇文章和大家討論一下,先給大家提兩個問題:
問題1:點選button1後,Timer會被GC回收嗎?點選button2後呢?為什麼?(這個問題來自《.net中的遊魂現象》這篇文章中,不過便有分析,Timer現在是System.Windows.Forms.Timer)。
private void button1_Click(object sender, EventArgs e)
{
System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
t.Interval = 1000 * 2;
t.Tick+=delegate{
Console.WriteLine(DateTime.Now.ToString());
};
t.Start();
}
private void button2_Click(object sender, EventArgs e)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
{
System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
t.Interval = 1000 * 2;
t.Tick+=delegate{
Console.WriteLine(DateTime.Now.ToString());
};
t.Start();
}
private void button2_Click(object sender, EventArgs e)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
問題2:在點選button1後,Form2會被GC回收嗎?點選button2後呢?為什麼?
Code
我們分析一下問題1,實際上問題2也是類似的問題,只不過更難分析罷了,有興趣你也除錯一下吧。
附件:TestApp.rar
以下是我的除錯過程:
一,使用SOS分析記憶體中的狀態。
1,點選button1, 執行完成後點選幾次button2,執行垃圾回收,然後在button2_Click開始處下斷點,使用SOS分析一下現在記憶體狀態。
2,載入sos.dll
.load C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll
extension C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded
3,dump堆中System.Windows.Forms.Timer型別的物件
!dumpheap -type System.Windows.Forms.Timer
PDB symbol for mscorwks.dll not loaded
Address MT Size
01ec25cc 602ccc44 48
01ec2628 602d7b9c 52
total 2 objects
Statistics:
MT Count TotalSize Class Name
602ccc44 1 48 System.Windows.Forms.Timer
602d7b9c 1 52 System.Windows.Forms.Timer+TimerNativeWindow
Total 2 objects
發現有一個System.Windows.Forms.Timer存活,可見,Timer並沒有被垃圾回收。
4,使用GCHandle檢視當前程式存活的GCHandles。
!GCHandles
GC Handle Statistics:
Strong Handles: 51
Pinned Handles: 7
Async Pinned Handles: 0
Ref Count Handles: 0
Weak Long Handles: 15
Weak Short Handles: 68
Other Handles: 0
Statistics:
MT Count TotalSize Class Name
617f0770 1 12 System.Object
60f926d8 1 12 System.Diagnostics.TraceListenerCollection
617f1cd4 1 20 System.RuntimeType
617f1220 1 28 System.SharedStatics
60f8246c 1 32 Microsoft.Win32.NativeMethods+WndProc
60f822e4 1 32 Microsoft.Win32.NativeMethods+ConHndlr
00126f3c 1 32 Microsoft.VisualStudio.Debugger.Runtime.Main+_ThrowCrossThreadMessageException
60f9ccdc 1 40 System.Diagnostics.BooleanSwitch
602ccc44 1 48 System.Windows.Forms.Timer
...
Total 141 objects
當我在使用GCHandles檢視程式中存活的GCHandles後,發現Timer居然被GCHandle抓著,在.NET的垃圾回收機制中,強GCHandle是不會被垃圾回收器收集的,它們被認為是垃圾收集中引用的root物件,由這些物件抓著的子物件也不會被收集,只有弱GCHandle才能被垃圾回收器收集。關於GCHandle,可以檢視MSDN-GCHandleType列舉。現在問題明白了,問題1中的程式碼存在GCHandle洩露,在呼叫GCHandle.Alloc方法後,沒有呼叫GCHandle.Free方法釋放它。
二,使用Reflector看看System.Windows.Forms.Timer的原始碼吧,看看原始碼中到底是怎麼回事
public class Timer : Component
{
public virtual bool Enabled
{
get
{
if (this.timerWindow == null)
{
return this.enabled;
}
return this.timerWindow.IsTimerRunning;
}
set
{
lock (this.syncObj)
{
if (this.enabled != value)
{
this.enabled = value;
if (!base.DesignMode)
{
if (value)
{
if (this.timerWindow == null)
{
this.timerWindow = new TimerNativeWindow(this);
}
this.timerRoot = GCHandle.Alloc(this);
this.timerWindow.StartTimer(this.interval);
}
else
{
if (this.timerWindow != null)
{
this.timerWindow.StopTimer();
}
if (this.timerRoot.IsAllocated)
{
this.timerRoot.Free();
}
}
}
}
}
}
}
public void Start()
{
this.Enabled = true;
}
public void Stop()
{
this.Enabled = false;
}
}
在System.Windows.Forms.Timer的Enable屬性的程式碼中,我們可以清楚的看到如果設定Enable=true,則Timer會被強GCHandle抓著,如果設成false,才能夠呼叫到GChandle.Free()方法釋放它。
所以在例一的程式碼中,點選button1後,Timer不會被垃圾回收,點選button2後,Timer也不會不垃圾回收。如果想要Timer被垃圾回收,必須呼叫Enable=false或等價程式碼,如呼叫Stop方法,Dispose方法等。
例二其實也是同樣的問題,有興趣的朋友可以分析一下。《.net中的遊魂現象》這篇文章中的程式碼也是同樣的問題,可以通過GCHandle看到System.Threading._TimerCallback被GCHandle抓著沒有釋放,只不過這是在Unmanaged程式碼中做的,不便於除錯。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-611887/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 如何理解Memory leak
- [Memory Leak] 1. console.log cause memory leak
- .net sql connection pool leakSQL
- Android memory leak using MATAndroid
- [Memory leak] 3. Garbage collection in Closure
- 使用awr來分析session leak問題Session
- 關於session leak的問題分析Session
- Linux C/C++ Memory Leak Detection ToolLinuxC++
- 【Visual Leak Detector】核心原始碼剖析(VLD 2.5.1)原始碼
- 【Visual Leak Detector】核心原始碼剖析(VLD 1.0)原始碼
- 【Visual Leak Detector】原始碼檔案概覽原始碼
- 【Visual Leak Detector】原始碼編譯 VLD 庫原始碼編譯
- 【Visual Leak Detector】原始碼除錯 VLD 庫原始碼除錯
- 【Visual Leak Detector】QT 中 VLD 輸出解析(一)QT
- 【Visual Leak Detector】QT 中 VLD 輸出解析(二)QT
- 【Visual Leak Detector】QT 中 VLD 輸出解析(四)QT
- 【Visual Leak Detector】在 VS 高版本中使用 VLD
- Android WebView Memory Leak WebView記憶體洩漏AndroidWebView記憶體
- Android: Unknown “Bitmap” cause by 'mGlow' causing memory leakAndroid
- Angular 18+ 高階教程 – Memory leak, unsubscribe, onDestroyAngular
- Oracle DBLink bug引發的故障(Session Hang Memory leak)OracleSession
- 【Visual Leak Detector】庫的 22 個 API 使用說明API
- ORA-00600: internal error code, arguments: [729], [4280], [space leak]Error
- Fastbin attack&&Double free和Unsortbin leak的綜合使用AST
- Threads are going to be renewed over time to try and avoid a probable memory leak.threadGo
- return to dl_resolve無需leak記憶體實現利用記憶體
- objective-c "performSelector may cause a leak because its selector is unknown".ObjectperformSelector
- Understanding and Diagnosing ORA-600 [729] Space Leak Errors_403584.1Error
- [翻譯]CVE-2012-0769, the case of the perfect info leak
- 處理Oracle記憶體洩露 ORA-00600 [729] [space leak]Oracle記憶體洩露
- 在Android 專案時的防止Memory leak 要注意的事項Android
- 一行程式碼教你解決FlutterPlatformViews記憶體洩露(memory leak)行程FlutterPlatformView記憶體洩露
- SQL Server 記憶體洩露(memory leak)——遊標導致的記憶體問題SQLServer記憶體洩露
- SQL Server 記憶體洩露”(memory leak)的案例—遊標導致的記憶體問題SQLServer記憶體洩露
- 記憶體洩露檢測工具 Visual Leak Detector for Visual C++ 2008/2010記憶體洩露C++