5

Es scheint, dass die Verwendung kritischer Abschnitte in Vista/Windows Server 2008 dazu führt, dass das Betriebssystem den Speicher nicht vollständig wiedererlangt. Wir haben dieses Problem mit einer Delphi-Anwendung gefunden und es liegt eindeutig an der Verwendung der CS-API. (SO question)Kritische Abschnitte Speicher unter Vista/Win2008 undicht?

Hat jemand andere es mit Anwendungen gesehen, die mit anderen Sprachen (C++, ...) entwickelt wurden?

Der Beispielcode initialisierte gerade 10000000 CS und löschte sie dann. Dies funktioniert in XP/Win2003 einwandfrei, jedoch wird nicht der gesamte Hauptspeicher in Vista/Win2008 freigegeben, bis die Anwendung beendet ist.
Je mehr Sie CS verwenden, desto mehr behält Ihre Anwendung Speicher für nichts.

+0

Hallo, François. Hast du Neuigkeiten zu diesem Thema? Ich bin neugierig :) – Alex

+0

Siehe meine eigene Antwort. Es gab tatsächlich eine Veränderung, in der Kategorie "Es ist ein Feature-kein-Bug" .... –

+0

Hallo, François. Danke für das Teilen. Übrigens kannst du deine eigene Antwort annehmen;) – Alex

Antwort

7

Microsoft hat in der Tat die Art und Weise InitializeCriticalSection Arbeiten auf Vista, Windows Server 2008, geändert und wahrscheinlich auch Windows 7.
Sie fügten hinzu, ein „Feature“ etwas Speicher für Debug-Informationen verwendet zu halten, wenn Sie eine Reihe von CS zuordnen. Je mehr Sie zuweisen, desto mehr Speicher bleibt erhalten. Es könnte asymptotisch und schließlich flach sein (nicht vollständig gekauft zu diesem).
Um dieses "Feature" zu vermeiden, müssen Sie die neue API InitalizeCriticalSectionEx verwenden und das Flag CRITICAL_SECTION_NO_DEBUG_INFO übergeben.
Der Vorteil davon ist, dass es schneller sein kann, da sehr oft nur der Spincount verwendet wird, ohne tatsächlich warten zu müssen.
Die Nachteile sind, dass Ihre alten Anwendungen inkompatibel sein können, müssen Sie Ihren Code ändern und es ist jetzt plattformabhängig (Sie müssen für die Version zu bestimmen, welche zu verwenden). Und Sie verlieren auch die Fähigkeit zu debuggen, wenn Sie brauchen.

Testkit einen Windows Server 2008 einzufrieren:
- bauen Sie dieses C++ Beispiel als 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; 
} 

-... Führen Sie diese Batchdatei (braucht den Schlaf.exe vom Server 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 

-... und sehen einen Win2008 Server mit 8 GB und Kern Einfrieren Quad CPU bevor die 300-Instanzen gestartet erreichen.
-... wiederholen Sie auf einem Windows 2003-Server und sehen Sie es wie ein Charme behandeln.

+0

Hallo Francois, danke für die Info. Könnten Sie möglicherweise Ihren Delphi-Code für die Initialisierung einfügen, um InitializeCriticalSectionEx automatisch zu verwenden, wenn er unter Vista/Windows2008 ausgeführt wird? – carlmon

1

Sie sehen etwas anderes.

Ich habe gerade gebaut & lief diesen Test-Code. Jede Statistik zur Speicherauslastung ist konstant - private Bytes, Working Set, Commit und so weiter.

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

danke Michael. Haben Sie den Speicher für Ihre Anwendung überwacht, während sie gestartet und inaktiv ist, während der CS-Test ausgeführt wurde, und nachdem sie ausgeführt wurde, während sie inaktiv ist und immer noch nicht beendet ist? Der Speicher ist immer vollständig wiederhergestellt, nachdem die App beendet wurde, das Problem ist, wenn es noch aktiv ist, nachdem es die CS viel verwendet hat. (hoffe, es ist klar) –

+0

Beachten Sie die Endlosschleife (while (true)). Ich habe es überwacht, während die Anwendung aktiv war, um kritische Abschnitte zu erstellen und zu löschen. Die Speicherauslastung war wie erwartet konstant. – Michael

+0

Ich würde erwarten, dass es nach oben und unten geht, wenn Sie Ihre kritischen Abschnitte erstellen und löschen, die im Process Explorer (private Bytes) schöne Sägezähne zeigen. BTW Ich tat es mit 10.000.000, wenn es einen Unterschied macht. –

2

Ihr Test ist höchstwahrscheinlich nicht repräsentativ für das Problem. Kritische Abschnitte gelten als "leichte Mutexe", da bei der Initialisierung des kritischen Abschnitts kein echter Kernel-Mutex erstellt wird. Das bedeutet, dass Ihre 10M kritischen Abschnitte nur Strukturen mit ein paar einfachen Mitgliedern sind. Wenn jedoch zwei Threads gleichzeitig auf einen CS zugreifen, wird tatsächlich ein Mutex erstellt, um sie zu synchronisieren - und das ist eine andere Geschichte.

Ich gehe davon aus, dass in Ihrer realen App Threads kollidieren, im Gegensatz zu Ihrer Test-App. Wenn Sie jetzt kritische Abschnitte als leichtgewichtige Mutexe behandeln und viele davon erstellen, weist Ihre App möglicherweise eine große Anzahl echter Kernel-Mutexe zu, die viel schwerer sind als das lichtkritische Abschnittsobjekt. Und da Mutexe ein Kernel-Objekt sind, kann das Erstellen einer übermäßigen Anzahl von ihnen das Betriebssystem wirklich verletzen.

Wenn dies tatsächlich der Fall ist, sollten Sie die Verwendung kritischer Abschnitte reduzieren, bei denen Sie viele Kollisionen erwarten. Das hat nichts mit der Windows-Version zu tun, daher mag meine Vermutung falsch sein, aber es ist immer noch etwas zu beachten. Versuchen Sie, die Zählung der Betriebssystemhandgriffe zu überwachen und zu sehen, wie es Ihrer App geht.

+0

Der Hauptpunkt ist, dass der gleiche Code (nur Aufruf der API) funktioniert gut auf XP/2003, aber nicht auf Vista/2008. –

+0

Ich habe keine Ahnung, wie Ihr Code funktioniert, aber zumindest für eine Testanwendung, die wiederholt Speicher zuweist und freigibt, ist es möglich, dass der Speichermanager versucht, freien Speicher im Cache zu speichern, anstatt ihn an das Betriebssystem zurückzugeben bald. Vista und XP haben wahrscheinlich unterschiedliche Speichermanager, daher der Unterschied. Tritt das gleiche bei der Zuweisung einer anderen beliebigen Struktur mit der gleichen Größe anstelle der realen CS vor? Siehst du tatsächlich, dass viele Griffe erstellt werden? – eran

Verwandte Themen