.net ThreadLocal<&燃气轮机;和内存泄漏
.Net 4。ThreadLocal实现IDisposable。但调用Dispose()似乎实际上并没有释放对所持有的线程本地对象的引用 此代码再现了以下问题:.net ThreadLocal<&燃气轮机;和内存泄漏,.net,memory-leaks,thread-local,.net,Memory Leaks,Thread Local,.Net 4。ThreadLocal实现IDisposable。但调用Dispose()似乎实际上并没有释放对所持有的线程本地对象的引用 此代码再现了以下问题: using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; using System.Threading; namespace ConsoleApplication2 { clas
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();
}
}
}
使用系统;
使用System.Collections.Generic;
使用System.Collections.Concurrent;
使用System.Linq;
使用系统线程;
命名空间控制台应用程序2
{
班级计划
{
类ThreadLocalData
{
//在LOH中分配对象
公共整数[]数据=新整数[10*1024*1024];
};
静态void Main(字符串[]参数)
{
//存储对已创建的所有线程本地对象的引用
var threadLocationInstances=new List();
ThreadLocal ThreadLocal=新的ThreadLocal(()=>
{
var ret=new ThreadLocalData();
锁定(螺纹定位装置)
threadLocationInstances.Add(ret);
返回ret;
});
//做一些多线程的事情
int sum=Enumerable.Range(0,100).aspallel().Select(
i=>threadLocal.Value.data.Sum()+i.Sum();
WriteLine(“Sum:{0}”,Sum);
WriteLine(“线程本地实例:{0}”,threadLocalInstances.Count);
//尽最大努力释放ThreadLocal对象
threadLocal.Dispose();
threadLocal=null;
控制台。写入(“按R键手动释放内存块,或按另一个键继续:”);
if(char.ToUpper(Console.ReadKey().KeyChar)='R')
{
foreach(ThreadLocationInstances中的变量i)
i、 数据=空;
}
//确保我们没有保留对LOH对象的引用
ThreadLocationInstances=null;
Console.WriteLine();
//收集垃圾
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
WriteLine(“垃圾收集。打开任务管理器查看内存消耗。”);
控制台。写入(“按任意键退出”);
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
mscorlib!System.Threading.ThreadLocal.GenericHolder.get_Boxed()
mscorlib!System.Threading.ThreadLocal.get_值()
控制台应用程序2!控制台应用程序2.Program.c__显示类3.b___2(int)Program.cs
这似乎是ThreadLocal设计中的一个缺陷。存储所有分配的对象以进行进一步清理的技巧很难看。关于如何解决这个问题有什么想法吗?内存可能已经被垃圾收集,但CLR进程还没有释放它。它倾向于保留分配的内存一段时间,以备以后需要,这样就不必进行昂贵的内存分配。在.Net 4.5 DP上运行,我看不出在应用程序中按R与不按R之间有什么区别。如果4.0中确实存在内存泄漏,那么它似乎已经修复
(4.5是就地更新,因此我无法在同一台计算机上测试4.0,抱歉。)您是在调试还是发布此版本?另外,任务管理器对于您正在测量的内容并不是非常有用。最好使用
GC.GetTotalMemory(true)
来测量内存,但这也不能保证收集所有内容。Printed GC.GetTotalMemory()。当我不将数据字段归零时,它会给出335607644,当我归零时,它会给出63268。没有显著差异。@SergeyS您应该使用新发现更新您的问题,并使用GC.GetTotalMemory,这样人们就不会被任务管理器方面拒绝。如果“数据”字段为零,GC的行为会有所不同。Plus Memory Profiler显示ThreadLocalData对象实际上是从ThreadLocal内部的某个地方扎根的。