[Edit0]人们为优化实例创建(通过使用数组缓冲区)提供了一些好的建议,从而降低了GC周期,但也请解释为什么只有这个线程被标记为'先发制人GC禁用',我的情况是僵局吗?
最近我正在对我们在.NET 3.5中编写的应用程序(在Windows XP上)进行一些测试,我注意到在某些情况下应用程序性能非常糟糕,TaskManager中显示的内存比正常情况多,但不挂。我做了一个内存转储,只需输入Microsoft DebugDiag tool,对于不太擅长WinDbg的人来说,这是一个很好的工具。
我知道应用程序中一定存在内存泄漏问题,但这里的问题是关于GC。
生成的报告中有一行:
线程6 触发了垃圾回收。垃圾收集器线程不会开始做它的工作,直到有线程的时候 先发制人GC禁用已完成执行。以下主题禁用了先发制人GC:主题6
从描述中,我将其理解为GC中的死锁和“先发制人GC禁用”主题,(但为什么不挂?)然后我很奇怪为什么那个帖子被标记为'先发制人GC禁用'。
然后我检查了 Thread 6 的调用堆栈,找出了源代码,这是一个明确创建的线程:
//Create the thread.
thread = new Thread(Execute) { Priority = ThreadPriority.Lowest };
thread.Start();
这个线程做的是线程安全地从队列中取消日志文本行的队列,并刷新到磁盘文件:
// Wait for an event from the thread that en-queue
waitEvent.WaitOne();
if (!disposed)
{
// Clear the entry queue.
bool queueEmpty;
do
{
// Pick the next logEntry in the queue safely in a locked way.
LogEntry logEntry = null;
lock (entryQueueLock)
{
logEntry = entryQueue.Dequeue();
}
var text = logEntry.Text;
byte[] textBytes = Encoding.Default.GetBytes(text);
fileStream.Write(textBytes, 0, textBytes.Length);
fileStream.Flush();
}
while (!queueEmpty);
}
线程6的调用堆栈在此行停止,表示该行启动了GC:
byte[] textBytes = Encoding.Default.GetBytes(text);
然后请帮助解释为什么只有线程6(在近50个线程中)有'先发制人GC禁用',以及如何避免这种情况:
答案 0 :(得分:1)
这不是您的具体问题,但您可以通过this advice concerning the Encoding
class来显着减少线程生成的垃圾量:
如果您的应用必须转换大量数据,则应重用输出缓冲区。在这种情况下,支持字节数组的
GetBytes
版本是最佳选择。
你可以轻松地做到这一点,如下:
/* helper extension method */
public static void Grow(ref byte[] buffer, int minSize)
{ if (minSize > buffer.Length) buffer = new byte[minSize]; }
/* outside the loop */
byte conversionBuffer[] = new byte[2048];
Encoding fileEncoding = Encoding.Default;
/* replace your GetBytes call with */
Grow(ref conversionBuffer, fileEncoding.GetMaxBytes(text));
int bytesUsed = fileEncoding.Get(text, 0, text.Count, conversionBuffer, 0);
/* and your Write call with */
fileStream.Write(conversionBuffer, 0, bytesUsed);
另外,我建议您使用ConcurrentQueue
而不是手动锁定。