Bob Bryan
Bob Bryan

Reputation: 3837

When allocating with the new keyword, does the CLR ever throttle via a Sleep statement when memory is low?

I came across this answer here on Stack Overflow:

When you allocate faster than you can garbage collect you will run into OOM. If you do heavy allocations the CLR will insert a Sleep(xx) to throttle allocation but this is not enough in your extreme case.

So, I have not read anything about the CLR throttling allocations by inserting a Sleep statement to slow down allocations when memory is low. Can anyone confirm if this is true or not? If it is true, then is there any documentation that talks about the details? I have tried doing Google searches, but could not find anything to support this claim.

Upvotes: 0

Views: 180

Answers (1)

Bob Bryan
Bob Bryan

Reputation: 3837

Many thanks to @AloisKraus for his research, patience, and links to the code in GC.cpp that shows the following code in the method gc_heap::allocate_small:

#if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS)
    if (recursive_gc_sync::background_running_p())
    {
        background_soh_alloc_count++;
        if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0)
        {
            add_saved_spinlock_info (false, me_release, mt_alloc_small);
            leave_spin_lock (&more_space_lock_soh);
            bool cooperative_mode = enable_preemptive();
            GCToOSInterface::Sleep (bgc_alloc_spin);
            disable_preemptive (cooperative_mode);
            enter_spin_lock (&more_space_lock_soh);
            add_saved_spinlock_info (false, me_acquire, mt_alloc_small);
        }
        else
        {
            //GCToOSInterface::YieldThread (0);
        }
    }
#endif //BACKGROUND_GC && !MULTIPLE_HEAPS

The key line is:

GCToOSInterface::Sleep (bgc_alloc_spin);

bgc_alloc_spin is initialized to 2, so this causes the thread to sleep for 2 milliseconds (ms). The code is only executed once every 140 times it is called, and only when there is a background GC taking place. But, this is still enough to cause 14,000+ threads to sleep for 2 ms over the course of 1 second, which will have a significant impact on performance (see the Alois Kraus discussion for the simple math).

Edit 1

In answer to @Enigmativity, the GCToOSInterfaceSleep method is defined as:

void GCToOSInterface::Sleep(uint32_t sleepMSec)
{
    ::Sleep(sleepMSec);
}        

This is located in gcenv.windows.cpp.

In conclusion, the answer to the question I posed is yes, the CLR does throttle allocations when a background GC is running. The rationale to throttling allocations appears to be to allow the background GC to complete its job more quickly and efficiently.

Upvotes: 0

Related Questions