我在 Stack Overflow 上遇到了这个答案:
当您分配的速度超过垃圾收集的速度时,您将遇到 OOM。如果您进行大量分配,CLR 将插入 Sleep(xx) 来限制分配,但在极端情况下这还不够。
因此,我还没有通过插入睡眠语句来在内存不足时减慢分配速度来阅读有关 CLR 限制分配的任何内容。谁能确认这是真的还是假的?如果是真的,那么是否有任何文件讨论了细节?我曾尝试进行谷歌搜索,但找不到任何支持这一说法的东西。
我在 Stack Overflow 上遇到了这个答案:
当您分配的速度超过垃圾收集的速度时,您将遇到 OOM。如果您进行大量分配,CLR 将插入 Sleep(xx) 来限制分配,但在极端情况下这还不够。
因此,我还没有通过插入睡眠语句来在内存不足时减慢分配速度来阅读有关 CLR 限制分配的任何内容。谁能确认这是真的还是假的?如果是真的,那么是否有任何文件讨论了细节?我曾尝试进行谷歌搜索,但找不到任何支持这一说法的东西。
非常感谢@AloisKraus 的研究、耐心和指向 GC.cpp 中代码的链接,该链接在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
关键线是:
GCToOSInterface::Sleep (bgc_alloc_spin);
bgc_alloc_spin 初始化为 2,因此这会导致线程休眠 2 毫秒 (ms)。该代码仅在每 140 次被调用时执行一次,并且仅在发生后台 GC 时执行。但是,这仍然足以导致 14,000 多个线程在 1 秒内休眠 2 毫秒,这将对性能产生重大影响(有关简单数学,请参阅 Alois Kraus 讨论)。
编辑 1
作为对@Enigmativity 的回答,GCToOSInterfaceSleep 方法定义为:
void GCToOSInterface::Sleep(uint32_t sleepMSec)
{
::Sleep(sleepMSec);
}
它位于gcenv.windows.cpp中。
总之,我提出的问题的答案是肯定的,当后台 GC 运行时,CLR 会限制分配。限制分配的基本原理似乎是允许后台 GC 更快、更有效地完成其工作。