Reputation: 734
[Edit0] some good suggestion was provided by peoples for optimize instance creation(by using array buffer) and thus lower down the GC cycles, but please also explain why ONLY this thread marked as 'pre-emptive GC disabled', and is my case a deadlock?
Recently I'm doing some testing on our application (on Windows XP) which was written in .NET 3.5, I noticed in some cases the application performance is really bad, the memory shown in TaskManager is times than normal situation, but not hang. I did a Memory dump and simply put into Microsoft DebugDiag tool which is a good tool for people not very good at WinDbg.
I known there must have some memory leak issue in application, but the question here is about the GC.
There's one line in generated report:
Thread 6 triggered the garbage collection. The garbage collector thread wont start doing its work till the time the threads which have pre-emptive GC disabled have finished executing. The following threads have pre-emptive GC disabled: Thread 6
From the description, I understand it as a deadlock from the GC and 'pre-emptive GC disabled' threads, (but why not hang??) then I was wonder why that thread marked as 'pre-emptive GC disabled'.
Then I checked the Thread 6's call stack and find out the source code, it's a thread created explicitly by:
//Create the thread.
thread = new Thread(Execute) { Priority = ThreadPriority.Lowest };
thread.Start();
What this thread doing is thread-safely de-queue a log text line from a queue, and flush to a disk file:
// 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);
}
The call stack for Thread 6 stopped at this line, means this line kicked off the GC:
byte[] textBytes = Encoding.Default.GetBytes(text);
Then please help to explain why ONLY the Thread 6 (in nearly 50 threads) have this 'pre-emptive GC disabled', and how to avoid this situation:
Upvotes: 8
Views: 2965
Reputation: 283733
This is not your problem specifically, but you can significantly reduce the amount of garbage that thread generates by following this advice concerning the Encoding
class:
If your app must convert a large amount of data, it should reuse the output buffer. In this case, the
GetBytes
version that supports byte arrays is the best choice.
You can do that easily, like this:
/* 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);
Also, I suggest you use a ConcurrentQueue
instead of manually locking.
Upvotes: 1