ThreadLocal<>和内存泄漏
.Net 4.ThreadLocal<>实现 IDisposable。但似乎调用 Dispose() 实际上并没有释放对所持有的线程本地对象的引用。
此代码重现了问题:
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<> 中的一个缺陷。设计。存储所有分配的对象以供进一步清理的技巧是丑陋的。关于如何解决这个问题有什么想法吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
内存可能已被垃圾回收,但 CLR 进程尚未释放它。它倾向于保留分配的内存一段时间,以备以后需要时使用,因此不必进行昂贵的内存分配。
The memory has probably been garbage collected but the CLR process hasn't let go of it yet. It tends to hold onto allocated memory for a bit in case it needs it later so it doesn't have to do an expensive memory allocation.
在 .Net 4.5 DP 上运行,我认为在应用程序中按 R 与不按 R 之间没有任何区别。如果 4.0 中确实存在内存泄漏,那么它似乎已得到修复。
(4.5是就地更新,所以我无法在同一台计算机上测试4.0,抱歉。)
Running on .Net 4.5 DP, I don't see any difference between pressing R or not in your application. If there actually was a memory leak in 4.0, it seems it was fixed.
(4.5 is a in-place update, so I can't test 4.0 on the same computer, sorry.)