Критические разделы утечки памяти на Vista / Win2008? - PullRequest
5 голосов
/ 30 апреля 2009

Кажется, что использование критических разделов в Vista / Windows Server 2008 довольно часто приводит к тому, что ОС не полностью восстанавливает память. Мы обнаружили эту проблему с приложением Delphi, и это явно из-за использования CS API. (см. этот ТАК * вопрос )

Кто-нибудь еще видел это с приложениями, разработанными на других языках (C ++, ...)?

Пример кода просто инициализировал 10000000 CS, а затем удалял их. Это нормально работает в XP / Win2003, но не освобождает всю пиковую память в Vista / Win2008 до тех пор, пока приложение не завершится. Чем больше вы используете CS, тем больше ваше приложение хранит память даром.

Ответы [ 3 ]

7 голосов
/ 20 мая 2009

Microsoft действительно изменила способ работы InitializeCriticalSection в Vista, Windows Server 2008 и, вероятно, также в Windows 7.
Они добавили «функцию» для сохранения некоторой памяти, используемой для отладочной информации, когда вы выделяете кучу CS. Чем больше вы выделяете, тем больше памяти сохраняется. Это может быть асимптотическим и в конечном итоге расплющенным (не полностью купленным к этому).
Чтобы избежать этой «функции», вы должны использовать новый API InitalizeCriticalSectionEx и передать флаг CRITICAL_SECTION_NO_DEBUG_INFO.
Преимущество этого состоит в том, что это может быть быстрее, поскольку очень часто будет использоваться только spincount без необходимости ждать.
Недостатки в том, что ваши старые приложения могут быть несовместимы , вам нужно изменить код, и теперь он зависит от платформы (вы должны проверить версию, чтобы определить, какую из них использовать). А также вы теряете возможность отладки, если вам нужно.

Тестовый набор для замораживания 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; 
}

-... Запустить этот пакетный файл (требуется файл sleep.exe с сервера SDK)

@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

-... и увидите сервер Win2008 с 8 ГБ и четырехъядерным процессором, который зависает до достижения 300 запущенных экземпляров.
-... повторите на сервере Windows 2003 и убедитесь, что он обрабатывает его как очарование.

2 голосов
/ 30 апреля 2009

Ваш тест, скорее всего, не является представителем проблемы. Критические секции считаются «облегченными мьютексами», потому что настоящий мьютекс ядра не создается при инициализации критической секции. Это означает, что ваши критические разделы 10M просто структуры с несколькими простыми членами. Однако, когда два потока обращаются к CS одновременно, для их синхронизации действительно создается мьютекс - и это другая история.

Я предполагаю, что в вашем реальном приложении потоки сталкиваются, в отличие от вашего тестового приложения. Теперь, если вы действительно рассматриваете критические секции как легкие мьютексы и создаете их много, ваше приложение может выделять большое количество реальных мьютексов ядра, которые намного тяжелее, чем объект легких критических секций. А поскольку мьютексы являются объектами ядра, их чрезмерное количество может действительно повредить ОС.

Если это действительно так, вам следует сократить использование критических участков, где вы ожидаете много столкновений. Это не имеет ничего общего с версией для Windows, так что я могу ошибаться, но это все же нужно учитывать. Попробуйте отслеживать количество дескрипторов ОС и посмотрите, как работает ваше приложение.

1 голос
/ 30 апреля 2009

Вы видите что-то еще.

Я только что собрал и запустил этот тестовый код. Каждый показатель использования памяти постоянен - ​​частные байты, рабочий набор, фиксация и т. Д.

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;
}
...