.Net 4. ThreadLocal <>實現IDisposable。但似乎調用Dispose()實際上並不釋放對線程本地對象的引用。ThreadLocal <>和內存泄漏
此代碼重新產生問題:
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
namespace ConsoleApplication2
{
class Program
{
class ThreadLocalData
{
// Allocate object in LOH
public int[] data = new int[10 * 1024 * 1024];
};
static void Main(string[] args)
{
// Stores references to all thread local object that have been created
var threadLocalInstances = new List<ThreadLocalData>();
ThreadLocal<ThreadLocalData> threadLocal = new ThreadLocal<ThreadLocalData>(() =>
{
var ret = new ThreadLocalData();
lock (threadLocalInstances)
threadLocalInstances.Add(ret);
return ret;
});
// Do some multithreaded stuff
int sum = Enumerable.Range(0, 100).AsParallel().Select(
i => threadLocal.Value.data.Sum() + i).Sum();
Console.WriteLine("Sum: {0}", sum);
Console.WriteLine("Thread local instances: {0}", threadLocalInstances.Count);
// Do our best to release ThreadLocal<> object
threadLocal.Dispose();
threadLocal = null;
Console.Write("Press R to release memory blocks manually or another key to proceed: ");
if (char.ToUpper(Console.ReadKey().KeyChar) == 'R')
{
foreach (var i in threadLocalInstances)
i.data = null;
}
// Make sure we don't keep the references to LOH objects
threadLocalInstances = null;
Console.WriteLine();
// Collect the garbage
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("Garbage collected. Open Task Manager to see memory consumption.");
Console.Write("Press any key to exit.");
Console.ReadKey();
}
}
}
線程本地數據存儲到一個大的對象的引用。如果不手動刪除引用,則GC不會收集這些大對象。我使用任務管理器觀察內存消耗。我也運行內存分析器。收集垃圾後我做了一個快照。探查表明,泄漏的對象是由根深蒂固的GCHandle,並在這裏被分配:
mscorlib!System.Threading.ThreadLocal<T>.GenericHolder<U,V,W>.get_Boxed()
mscorlib!System.Threading.ThreadLocal<T>.get_Value()
ConsoleApplication2!ConsoleApplication2.Program.<>c__DisplayClass3.<Main>b__2(int) Program.cs
這似乎是在ThreadLocal中<瑕疵>設計。存儲所有分配的對象進行進一步清理的技巧很難看。有關如何解決該問題的任何想法?
你在調試或發佈此?另外,任務管理器對於你正在測量的內容並不是非常有用 –
你最好使用'GC.GetTotalMemory(true)'來測量內存,但是這也不能保證所有內容都會被收集。 – Ray
打印GC.GetTotalMemory()。當我不把零數據**字段和63268數字零字段給出時,它會給出335607644。 – SergeyS