2009-10-19 4 views
8

Ich entwickle einen VC++ NT-Dienst, der für viele Monate ununterbrochen arbeiten soll. Es verwendet VC++ Runtime Heap intensiv. Offensichtlich kann die Heap-Fragmentierung zu irgendeinem Zeitpunkt eine Fehlfunktion verursachen (weil sie denkt, dass sie nicht genügend Speicher hat).Wie erkennen und schätzen Sie die Heapfragmentierung in meinem C++ - Programm?

Welche Tests kann ich auf meinem Dienst ausführen, um abzuschätzen, in welchem ​​Maße die Heap-Fragmentierung auftritt?

Antwort

2

Ich denke, der beste Weg wäre, Ihren eigenen Speichermanager zu schreiben (oder einen zu kaufen), der diese Daten anbietet. Jeder andere Weg würde den Heap selbst verändern und somit das Ergebnis ungültig machen.

Eine Strategie, die einfacher zu implementieren ist, Speicherblöcke unterschiedlicher Größe zuzuweisen und auf einen Fehler zu warten - aber ich denke nicht, dass das ein guter Weg ist. Wie auch immer - je größer die Blockgröße, desto weniger die Fragmentierung. Je nach Speichermanager kann die Zuweisung des Blocks jedoch das Ergebnis ändern.


Edit: Ich fand einen Link über den Brammenverteiler (thx für den Kommentar) mit den Statistiken. Es ist aber in Deutsch und die englische Version des Artikels enthält nicht so viele Informationen. Verwenden Sie Babelfish für die Übersetzung.

http://de.wikipedia.org/wiki/Slab_allocator (babelfish version)

http://www.usenix.org/event/usenix01/full_papers/bonwick/bonwick.pdf

+0

+1 Brammenzuteiler. – user7116

+0

Das Ersetzen des Speichermanagers, nur um ihn zu messen, ist jedoch nutzlos, es sei denn, Sie verwenden diesen Speichermanager in der Produktion. Ich denke, eine bessere Lösung wäre, diesen tatsächlichen Speicherzuordner zu instrumentieren und seine Fragmentierung zu messen. Sie können dies tun, indem Sie die Alloc/Free-Anrufe umbrechen, oder Sie können sehen, ob der Speichermanager über Hooks verfügt, die Sie verwenden können. –

0

ich mit Tobias zustimmen - Ihren eigenen Speicher-Manager machen, ist eine hervorragende Möglichkeit, dies zu tun. Ich kenne nur ein paar Entwickler, denen ich vertrauen würde, um diese Art von Code zu schreiben, obwohl ...

Eine andere Möglichkeit ist, Ihre eigene Art von Müllsammlung/Konsolidierung auf Ihre Objekte hin und wieder tun - bei geringer Belastung ... dh Ihr Dienst kann für eine Weile inaktiv sein, während er den von ihm verwendeten Speicher "defragmentiert", aber ich bin nicht sicher, ob Sie das gewünschte Verhalten ohne Ihre eigene Speicherverwaltung garantieren können.

5

Sie haben ein paar Antworten erhalten, in denen es darum geht, Probleme bei der Heapfragmentierung zu vermeiden, aber Ihre Frage wurde nicht direkt angesprochen. Nahezu die einzige Möglichkeit, abzuschätzen, wie wahrscheinlich es ist, dass Probleme bei der Fragmentierung auftreten, ist die Simulation einer großen Anzahl von Anwendungen und die Messung der Fragmentierung, die Sie erhalten.

Da es sich um einen NT-Dienst handelt, besteht die Simulation monatelanger Verwendung meist darin, viele Anfragen in Eile zu stellen. Es ist sehr wahrscheinlich, dass Sie Anfragen schneller als erwartet stellen können, so dass Sie wahrscheinlich Anfragen für mehrere Monate in nur wenigen Stunden und möglicherweise sogar noch weniger simulieren können (abhängig von der Rate, mit der Sie normalerweise Anfragen erhalten)).

Sobald Sie monatelang arbeiten (oder sogar während Sie das tun) müssen Sie dann auf den Heap schauen, um zu sehen, wie viel Fragmentierung Sie bekommen. Das ist nicht einfach, aber normalerweise ist es möglich. Sie beginnen mit der Eingabe eines Threads in den Service-Prozess (Googeln auf "Thread-Injektion" oder etwas auf dieser Bestellung sollte eine ziemlich große Menge an Informationen erhalten). Dann müssen Sie den Haufen laufen und (insbesondere) nach Blöcken suchen, die frei sind, aber zu klein, um die meisten Anforderungen zu erfüllen. Angenommen, Sie verwenden MS VC++, führen Sie den Heap mit _heapwalk, und es wird durch den Heap laufen und Ihnen die Adresse, Größe und den Status (frei oder in Verwendung) jedes Blocks im Heap mitteilen.

Ein letztes Detail: um aussagekräftige Ergebnisse zu erhalten, müssen sowohl die ausführbare UND-Datei als auch die DLL, die den injizierten Thread enthält, mit der Laufzeitbibliothek in einer DLL verknüpft werden. Das bedeutet, dass es für den gesamten Prozess einen Heap geben wird, sodass Ihr injizierter Thread den Heap durchlaufen wird, der von Ihrem Service verwendet wird.Wenn Sie die Standardbibliothek statisch verknüpfen, haben die DLL und der Dienst jeweils einen eigenen Heap. Die DLL wird ihren eigenen Heap durchlaufen, der Ihnen nichts über den Heap mitteilen wird, der von dem Dienstprozess verwandt wird.

0

Ich bin sicher, es gibt Tools für Windows, die Ihnen den Status eines Speichers geben können, aber dennoch sollten Sie Ihren Dienst mit diesem Problem im Hinterkopf entwickeln.

Zuerst sollten Sie verstehen, was sind die Zuweisungen, die Sie vorformulieren. Ich denke, der einfache Weg besteht darin, die neuen und delete-Operatoren zu überschreiben, und von diesen neuen Operatoren sollten Sie einige Statistiken Ihrer Zuordnungen zählen und dann die Standard-new- und delete-Operatoren Ihres Compilers aufrufen.

Die Mindeststatistik, die Sie meiner Meinung nach zählen sollten, sind die Anzahl der Zuweisungen von gemeinsamen Blockgrößenbereichen.

z.B. Blöcke zwischen 0 Bytes bis 15 Bytes, Blöcke zwischen 16 Bytes bis 32 Bytes, Blöcke zwischen 32 Bytes bis 48 Bytes, ...

Sie können auch die Anzahl der sequentiellen Zuordnung jeden Bereichsblockes Größe hinzufügen

Nachdem Sie diese Daten gesammelt haben, können Sie das Fragmentierungsproblem um reduzieren, indem Sie Ihre Blöcke auf die üblichen Größen ausrichten.

Die beste und einfache Technik für die Ausrichtung ist einen Block zu verwenden, die Potenz von 2

zum Beispiel ist eine Nummer am nächsten Zahl, das durch 16 dividieren auszurichten, können Sie die folgende Funktion verwenden:

int align(int size) 
{ 
    return ((size + 15) & ~0x0000000F); 
} 

Natürlich sollten Sie Ihre Statistiken verwenden, um die beste Potenz von 2 zum Ausrichten auszuwählen. Ziel ist es, eine Zahl zu erreichen, die die meisten Ihrer Zuweisungen in wenigen Blöcken erreichen, und gleichzeitig den Overhead der Ausrichtung angemessen zu halten.

Viel Glück ...

1

Switchin auf dem Low-Fragmentierung Heap für Windows kann auf älteren Systemen machen die Arbeit helfen. auf neuen Systemen ihre auf Standard geschaltet (Vista, Server 2008)

HANDLE heaps[1025]; 
    DWORD nheaps = GetProcessHeaps((sizeof(heaps)/sizeof(HANDLE)) - 1, heaps); 
    for (DWORD i = 0; i < nheaps; ++i) { 
    ULONG enableLFH = 2; 
    HeapSetInformation(heaps[i], HeapCompatibilityInformation, &enableLFH, sizeof(enableLFH)); 
    } 

Es ist ein Werkzeug VMMap von Sysinternals (jetzt Microsoft), die auf der Speicherfragmentierung einen guten Überblick gibt.

1

Der einfachste Weg, die Fragmentierung zu erkennen, besteht darin, die größte Zuteilung zu ermitteln, die Ihr Programm jemals ausführen wird, und dann immer wieder mindestens den doppelten Betrag zuzuweisen. wenn die Zuordnung nicht heißt NULL zurück und Ihre Heap-Nutzung als durch den Code bestimmt - so etwas wie dies auf Windows

PROCESS_MEMORY_COUNTERS counters; 
if(GetProcessMemoryInfo(process, &counters, sizeof(counters))){ 
    result = counters.WorkingSetSize; 
} 

ist weniger als einen gewissen Prozentsatz des Systemspeichers in der Regel 75%, dann haben Sie auf jeden Fall eine Fragmentierung Problem.

Verwandte Themen