5

似乎在 Vista/Windows Server 2008 中大量使用关键部分会导致操作系统无法完全恢复内存。我们在 Delphi 应用程序中发现了这个问题,这显然是因为使用了 CS API。(见这个问题

有没有其他人在使用其他语言(C++,...)开发的应用程序中看到它?

示例代码只是初始化 10000000 CS,然后删除它们。这在 XP/Win2003 中运行良好,但在应用程序结束之前不会释放 Vista/Win2008 中的所有峰值内存。
您使用 CS 的次数越多,您的应用程序就会越多地保留内存。

4

3 回答 3

7

Microsoft 确实改变了InitializeCriticalSection在 Vista、Windows Server 2008 和可能还有 Windows 7 上的工作方式。
他们添加了一个“功能”,以便在分配一堆 CS 时保留一些用于调试信息的内存。您分配的越多,保留的内存就越多。它可能是渐近的并最终变平(没有完全购买这个)。
为了避免这个“特性”,你必须使用新的 API InitalizeCriticalSectionEx并传递 flag CRITICAL_SECTION_NO_DEBUG_INFO
这样做的好处是它可能会更快,因为通常只使用旋转计数而无需实际等待。
缺点是您的旧应用程序可能不兼容,您需要更改您的代码,并且它现在依赖于平台(您必须检查版本以确定使用哪个版本)。如果需要,您也会失去调试能力。

冻结 Windows Server 2008 的测试工具包:
- 将此 C++ 示例构建为 CSTest.exe

#include "stdafx.h" 
#include "windows.h" 
#include <iostream> 

using namespace std; 

void TestCriticalSections() 
{ 
  const unsigned int CS_MAX = 5000000; 
  CRITICAL_SECTION* csArray = new CRITICAL_SECTION[CS_MAX];  

  for (unsigned int i = 0; i < CS_MAX; ++i)  
    InitializeCriticalSection(&csArray[i]);  

  for (unsigned int i = 0; i < CS_MAX; ++i)  
    EnterCriticalSection(&csArray[i]);  

  for (unsigned int i = 0; i < CS_MAX; ++i)  
    LeaveCriticalSection(&csArray[i]);  

  for (unsigned int i = 0; i < CS_MAX; ++i)  
    DeleteCriticalSection(&csArray[i]); 

  delete [] csArray; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
  TestCriticalSections(); 

  cout << "just hanging around..."; 
  cin.get(); 

  return 0; 
}

-...运行这个批处理文件(需要来自服务器 SDK 的 sleep.exe)

@rem you may adapt the sleep delay depending on speed and # of CPUs 
@rem sleep 2 on a duo-core 4GB. sleep 1 on a 4CPU 8GB. 

@for /L %%i in (1,1,300) do @echo %%i & @start /min CSTest.exe & @sleep 1 
@echo still alive? 
@pause 
@taskkill /im cstest.* /f

-...在启动 300 个实例之前看到一个 8GB 和四核 CPU 的 Win2008 服务器冻结。
-...在 Windows 2003 服务器上重复,看看它处理它就像一个魅力。

于 2009-05-20T19:42:25.690 回答
2

您的测试很可能不能代表问题。临界区被认为是“轻量级互斥体”,因为在初始化临界区时不会创建真正的内核互斥体。这意味着您的 10M 关键部分只是具有几个简单成员的结构。然而,当两个线程同时访问一个 CS 时,为了使它们同步,确实创建了一个互斥锁——这是另一回事。

我假设在您的真实应用程序线程中确实发生了冲突,而不是您的测试应用程序。现在,如果您真的将临界区视为轻量级互斥体并创建大量它们,那么您的应用程序可能会分配大量真正的内核互斥体,它们比轻量级临界区对象重得多。而且由于互斥体是内核对象,因此创建过多的互斥体确实会损害操作系统。

如果确实如此,您应该减少预期会发生大量冲突的关键部分的使用。这与Windows版本无关,所以我的猜测可能是错误的,但仍然需要考虑。尝试监控操作系统句柄数,看看你的应用程序是如何运行的。

于 2009-04-30T08:31:20.033 回答
1

你看到的是别的东西。

我刚刚构建并运行了这个测试代码。每个内存使用统计都是恒定的——私有字节、工作集、提交等等。

int _tmain(int argc, _TCHAR* argv[])
{
    while (true)
    {
        CRITICAL_SECTION* cs = new CRITICAL_SECTION[1000000];
        for (int i = 0; i < 1000000; i++) InitializeCriticalSection(&cs[i]);
        for (int i = 0; i < 1000000; i++) DeleteCriticalSection(&cs[i]);
        delete [] cs;
    }

    return 0;
}
于 2009-04-30T00:51:10.200 回答