垃圾收集器无法回收内存资源
Garbage collector not able to reclaim memory resources
我正在尝试测试如何在 .net 中处理重物 BlockingCollection
。
我正在使用一些 API 获取重对象,并希望在多线程中处理它。为了做到这一点,我有一个线程读取重对象并将它们推送到多个线程中,其中每个线程都有自己的阻塞集合,并且每个线程都将对象从集合和进程中取出。我期望当对象从所有集合中删除时,无论它位于何处,GC 都应该能够清理它。清理没有发生,我的程序内存不足。
中间调用 GC.Collect()
帮助我完成了这个过程,但它对性能造成了重大影响,我无法承受。
我唯一的问题是为什么即使对象超出范围,垃圾收集器也无法释放此处的资源。
public class DummyProcessor
{
List<BlockingCollection<object>> listOfBlockingCollection = null;
void ProcessCollection(object blockingCollection)
{
BlockingCollection<object> collection = (BlockingCollection<object>)blockingCollection;
while (collection.IsCompleted == false)
{
object heavyObject = collection.Take();
CallExternalProcess(heavyObject);
}
}
private void CallExternalProcess(object heavyObject)
{
throw new NotImplementedException();
}
public void Analyze(object heavyObject)
{
if (listOfBlockingCollection == null)
{
listOfBlockingCollection = new List<BlockingCollection<object>>();
for (int i = 0; i < 25; i++)
{
BlockingCollection<object> coll = new BlockingCollection<object>();
listOfBlockingCollection.Add(coll);
Thread pt = new Thread(new ParameterizedThreadStart(ProcessCollection));
pt.Start(coll);
}
}
for (int i = 0; i < 25; i++)
{
listOfBlockingCollection[i].Add(heavyObject);
}
}
}
如果 Analyze
是以线程安全的方式被调用的话,现在我在这段代码中看不到任何威胁。如果有dotMemory的分析,请添加到问题中。
但是,我想指出您调用 Analyze
方法的方式。首先,你以非线程安全的方式初始化 listOfBlockingCollection
变量,因为如果你有两个线程 运行 Analyze
方法,你可以获得比赛 and 一种情况,其中多个线程运行代码中的 if
子句。
在这种情况下,您正在创建至少 25 个幽灵线程,更重要的是,您可以了解 listOfBlockingCollection
有 超过 25 个项目的情况:如果某个线程已经超出 if
子句,而另一个仍然创建线程并将集合添加到列表中(例如,您可以查看 listOfBlockingCollection.Count
)。一个线程至少有 2 MB 的内存,并且作为 IDisposable
对象,它的收集速度没有您希望的那么快。
你的代码的另一个问题是 25
threads 是一个非有效的线程数(只有当你有一个 32 核的超级计算机时,这可能是一个选项)因为上下文切换.最好使用与系统内核数相等的线程数,或者更好的方法是将代码切换为面向 Task
(您可以轻松创建 25 个处理任务,并在其中循环)。
另一种选择,如果您需要一些额外的数据工作流程,您可以使用 TPL DataFlow library with 25 ActionBlocks
in your application starting 25 different flows. After finishing the heavy object queue, you can easily send the Complete
command all across the blocks 并完成执行。
通过dotMemory运行应用程序后,我发现重物产生的速度太快,以至于它们堆积成各种BlockingCollection
大量超出计算机内存。
我对 GC.Collect
的显式调用之所以有效,并不是因为它释放了使用过的重对象,而是为重对象生产增加了一种暂停,从而为消费者线程提供了更多时间来清除 BlockingCollection
中已有的内容。
所以我不得不在重对象生产者和消费者之间引入等待,这样我就不会过度使用可用内存。为此,我正在使用 AutoResetEvent
。我在集合大小达到某个范围后调用 AutoResetEvent.WaitOne
,一旦低于该范围,我将再次调用 AutoResetEvent.Set
给 运行 生产者。
感谢大家对此的意见。
我正在尝试测试如何在 .net 中处理重物 BlockingCollection
。
我正在使用一些 API 获取重对象,并希望在多线程中处理它。为了做到这一点,我有一个线程读取重对象并将它们推送到多个线程中,其中每个线程都有自己的阻塞集合,并且每个线程都将对象从集合和进程中取出。我期望当对象从所有集合中删除时,无论它位于何处,GC 都应该能够清理它。清理没有发生,我的程序内存不足。
中间调用 GC.Collect()
帮助我完成了这个过程,但它对性能造成了重大影响,我无法承受。
我唯一的问题是为什么即使对象超出范围,垃圾收集器也无法释放此处的资源。
public class DummyProcessor
{
List<BlockingCollection<object>> listOfBlockingCollection = null;
void ProcessCollection(object blockingCollection)
{
BlockingCollection<object> collection = (BlockingCollection<object>)blockingCollection;
while (collection.IsCompleted == false)
{
object heavyObject = collection.Take();
CallExternalProcess(heavyObject);
}
}
private void CallExternalProcess(object heavyObject)
{
throw new NotImplementedException();
}
public void Analyze(object heavyObject)
{
if (listOfBlockingCollection == null)
{
listOfBlockingCollection = new List<BlockingCollection<object>>();
for (int i = 0; i < 25; i++)
{
BlockingCollection<object> coll = new BlockingCollection<object>();
listOfBlockingCollection.Add(coll);
Thread pt = new Thread(new ParameterizedThreadStart(ProcessCollection));
pt.Start(coll);
}
}
for (int i = 0; i < 25; i++)
{
listOfBlockingCollection[i].Add(heavyObject);
}
}
}
如果 Analyze
是以线程安全的方式被调用的话,现在我在这段代码中看不到任何威胁。如果有dotMemory的分析,请添加到问题中。
但是,我想指出您调用 Analyze
方法的方式。首先,你以非线程安全的方式初始化 listOfBlockingCollection
变量,因为如果你有两个线程 运行 Analyze
方法,你可以获得比赛 and 一种情况,其中多个线程运行代码中的 if
子句。
在这种情况下,您正在创建至少 25 个幽灵线程,更重要的是,您可以了解 listOfBlockingCollection
有 超过 25 个项目的情况:如果某个线程已经超出 if
子句,而另一个仍然创建线程并将集合添加到列表中(例如,您可以查看 listOfBlockingCollection.Count
)。一个线程至少有 2 MB 的内存,并且作为 IDisposable
对象,它的收集速度没有您希望的那么快。
你的代码的另一个问题是 25
threads 是一个非有效的线程数(只有当你有一个 32 核的超级计算机时,这可能是一个选项)因为上下文切换.最好使用与系统内核数相等的线程数,或者更好的方法是将代码切换为面向 Task
(您可以轻松创建 25 个处理任务,并在其中循环)。
另一种选择,如果您需要一些额外的数据工作流程,您可以使用 TPL DataFlow library with 25 ActionBlocks
in your application starting 25 different flows. After finishing the heavy object queue, you can easily send the Complete
command all across the blocks 并完成执行。
通过dotMemory运行应用程序后,我发现重物产生的速度太快,以至于它们堆积成各种BlockingCollection
大量超出计算机内存。
我对 GC.Collect
的显式调用之所以有效,并不是因为它释放了使用过的重对象,而是为重对象生产增加了一种暂停,从而为消费者线程提供了更多时间来清除 BlockingCollection
中已有的内容。
所以我不得不在重对象生产者和消费者之间引入等待,这样我就不会过度使用可用内存。为此,我正在使用 AutoResetEvent
。我在集合大小达到某个范围后调用 AutoResetEvent.WaitOne
,一旦低于该范围,我将再次调用 AutoResetEvent.Set
给 运行 生产者。
感谢大家对此的意见。