Is it OK to run GC.Collect in a background thread? Is it OK to run GC.Collect in a background thread? multithreading multithreading

Is it OK to run GC.Collect in a background thread?


You are throwing away the option to have garbage collection performed on the background when you do this. Or in other words, your UI thread is going to get suspended anyway, regardless if you do this from a worker thread. The only possible way to be ahead is when GC.WaitForPendingFinalizers() is taking a substantial amount of time. It is not actually something you should ever be waiting for, there is no point, and if it takes more than the blink of an eye then you are hiding pretty serious bugs in your code.

Another significant wrinkle is that the workstation version of Windows gives any thread that owns the foreground window a larger quantum. In other words, it is allowed to burn core longer than a background thread. A simple hack to make Windows more responsive to the user.

Too many moving parts, it is really rather best to test your theory so you can be sure that running a collection on a worker is actually something you are ahead with. Measuring UI thread suspensions is pretty simple, you can use a Timer to do this. Its Tick event cannot run when the thread is suspended. Start a new Winforms project, drop a Timer on the form, set its Interval to 1 and Enabled to True, add a Label and use this code to measure delays:

    int prevtick = 0;    int maxtick = -1;    private void timer1_Tick(object sender, EventArgs e) {        int tick = Environment.TickCount;        if (prevtick > 0) {            int thistick = tick - prevtick;            if (thistick > maxtick) {                maxtick = thistick;                label1.Text = maxtick.ToString();            }        }        prevtick = tick;    }

Run your program, you should be seeing 16 in the label. If you get less then you ought to get your machine fixed, not otherwise anything that affects this test. Add a button to reset the measurement:

    private void button1_Click(object sender, EventArgs e) {        maxtick = -1;    }

Add a checkbox and another button. We'll have it perform the actual collection:

    private void button2_Click(object sender, EventArgs e) {        var useworker = checkBox1.Checked;        System.Threading.ThreadPool.QueueUserWorkItem((_) => {            var lst = new List<object>();            for (int ix = 0; ix < 500 * 1024 * 1024 / (IntPtr.Size * 3); ++ix) {                lst.Add(new object());            }            lst.Clear();            if (useworker) {                GC.Collect();                GC.WaitForPendingFinalizers();            }            else {                this.BeginInvoke(new Action(() => {                    GC.Collect();                    GC.WaitForPendingFinalizers();                }));            }        });    }

Play with this, hit button2 to start the collection and pay attention to the value in the Label. Turn on the checkbox so it runs on the worker and compare. Use button1 to reset the maximum in between. And modify the allocation code, you probably want to do something with bitmaps, whatever you do to require this hack.

What I see: ~220 msec delay when performing the collection on the UI thread, ~340 msec delay when running on the worker. Clearly, this is not an improvement at all. From where I sit, your theory is dead in the water. Please try this yourself, I've got only one datapoint. Do beware that it is going to look very different on a server version of Windows or with <gcServer=true> in the .config file. Something else you can play with.


Update: The reasoning in this answer seems pretty sound but the answer by Hans Passant below shows that the conclusion does not hold. Don't jump to conclusions based on this answer.


This is a good idea. All CLR GC algorithms pause each thread at least once but the pauses are smaller than the total GC time. The call to GC.Collect takes as long as the total GC time takes. It has the maximum latency possible for any GC cycle. That's why it is a good idea to not call it on the UI thread.

Your UI thread will be paused during the GC at least once but not for the whole duration. It depends on the CLR version and GC settings how long and how many pauses there will be.

Summary: This reduces UI pause time but does not entirely avoid it. I recommend doing this since there is no harm being done.

Alternatively, dispose all unmanaged resources. This question seems to a assume a situation where that is not possible or too onerous.


Calling the GC directly is generally a bad thing.The Forms class implements the Dispose Pattern so why don't you use it.