5

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

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

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

+0

Привет, Франсуа. У вас есть новости по этому вопросу? Мне любопытно :) – Alex

+0

См. Мой собственный ответ. На самом деле произошли изменения в категории «it-a-feature-not-a-bug» .... –

+0

Привет, Франсуа. Спасибо, что поделился. BTW, вы можете принять свой собственный ответ;) – Alex

ответ

7

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

-... Запустите этот пакетный файл (необходим сон.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 с 8GB и ядра процессора Quad замораживания до достижения 300 экземпляров запущен.
-... повторить на сервере Windows 2003 и увидеть, как он обрабатывает его как шарм.

+0

Привет, Франсуа, спасибо за информацию. Не могли бы вы вставить код раздела инициализации Delphi для автоматического использования InitializeCriticalSectionEx при работе в Vista/Windows2008? – carlmon

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; 
} 
+0

спасибо Майкл. Наблюдали ли вы за память для своего приложения, когда он запущен и простаивает, при запуске теста CS и после его выполнения, когда он простаивает и все еще не завершен. Память всегда полностью восстанавливается после того, как приложение закончилось, проблема в том, что она по-прежнему активна после многократного использования CS. (надеюсь, что это ясно) –

+0

Обратите внимание на бесконечный цикл (while (true)). Я контролировал его, пока приложение активно запускало создание и удаление критических разделов. Использование памяти было постоянным, как и ожидалось. – Michael

+0

Я бы ожидал, что он будет расти вверх и вниз, когда вы создадите и удалите критические разделы, показывающие хорошие пильные зубы в Process Explorer (частные байты). Кстати, я сделал это с 10 000 000, если это имеет значение. –

2

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

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

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

+0

Главное, что тот же самый код (просто вызывающий API) хорошо работает на XP/2003, но не на Vista/2008. –

+0

Я понятия не имею, как работает ваш код, но, по крайней мере, для тестового приложения, которое периодически выделяет и освобождает память, возможно, что диспетчер памяти пытается кэшировать свободную память, а не возвращать ее в ОС, предполагая, что это будет необходимо скоро. У Vista и XP, вероятно, есть разные менеджеры памяти, поэтому разница. То же самое происходит при распределении какой-либо другой произвольной структуры с таким же размером вместо реальной CS? Вы действительно видите много ручек, которые создаются? – eran