(0) exportieren Drucken
Alle erweitern
Erweitern Minimieren
Dieser Artikel wurde noch nicht bewertet - Dieses Thema bewerten.

Belastungstests – ein erwünschter Speicherfresser

Veröffentlicht: 13. Jul 2006

Von James McCaffrey

Belastungstests sind ein wichtiger Bestandteil der Qualitätssicherung, die bei umfangreicheren Softwaretests nicht fehlen sollten. Der Sinn von Belastungstests ist klar: Anstelle von manuellen oder automatisierten Tests unter normalen Bedingungen werden Tests bei verringerten Computer- oder Systemressourcen durchgeführt. Die zu belastenden Ressourcen sind normalerweise der Arbeitsspeicher, die CPU-Verfügbarkeit, der Festplattenspeicher und die Netzwerkbandbreite. Zum Verringern dieser Ressourcen beim Testen wird ein Tool ausgeführt, dass als Stressor bezeichnet wird.

Laden Sie den Code zu diesem Artikel herunter: TestRun05.exe (116 KB)

Auf dieser Seite

Das Tool im Detail
Gestaltung der Konsolenausgabe
Toolerweiterungen
Zusammenfassung
Der Autor

In Abbildung 1 wird das Belastungstool "EatMem" während der Ausführung dargestellt. Es ist üblich, Stressoren mit dem Präfix "Eat" zu versehen. Daher heißen Stressoren, die den Arbeitsspeicher, die CPU-Verfügbarkeit oder den Speicherplatz belasten, beispielsweise "EatMem", "EatCpu" und "EatDisk". Der "EatMem"-Stressor ist ein Befehlszeilenprogramm, das nur das Argument der Ausführungslänge in Minuten erfordert. Etwa alle drei Sekunden versucht EatMem, eine zufällige Menge des verfügbaren Speichers zu reservieren. Eine Speicheranforderung kann auch fehlschlagen, wie aus Abbildung 1 ersichtlich ist. In diesem Fall fordert EatMem sooft Speicher an, bis die Anforderung erfolgreich ist. Das Stressor-Tool selbst führt keine Tests durch. Es stellt nur einen Computerstatus zum Testen bereit. Wenn dieses Programm gleichzeitig mit dem zu testenden Programm ausgeführt wird, können Bedingungen für die Anwendung simuliert werden, die in normalen Testumgebungen nicht leicht getestet werden können.

EatMem-Stressor in Aktion
Abbildung 1: EatMem-Stressor in Aktion

Die allgemeine Struktur des EatMem-Belastungstools in C# wird in Abbildung 2 dargestellt. Zuerst wird dem InteropServices-Namespace eine using-Anweisung hinzugefügt, damit systemeigene Win32®-Funktionen aufgerufen werden können. Die GlobalAlloc- und GlobalFree-Funktionen werden zum Zuordnen und Freigeben des Arbeitsspeichers aufgerufen, und die GlobalMemoryStatus-Funktion wird benötigt, um zu ermitteln, wie viel Speicher zugeordnet werden soll. Außerdem werden einige Win32-API-Funktionen zum Ändern der Textfarben an der Konsole verwendet.

Die GlobalAlloc- und GlobalFree-Funktionen sind in der Bibliothek "kernel32.dll" definiert. Mit dem DllImport-Attribut können sie vom C#-Belastungsprogramm aufgerufen werden. Die ursprüngliche C++-Signatur für GlobalFree lautet:

      HGLOBAL GlobalAlloc(UINT uFlags,
      SIZE_T dwBytes);
    

Der Rückgabetyp HGLOBAL ist einer der vielen symbolischen Win32-Typen. Im Wesentlichen ist er ein Zeiger auf die erste Speicheradresse des von GlobalAlloc zugeordneten Speichers, daher wird dieser Typ in einen System.IntPtr-Typ übersetzt. IntPtr ist ein Plattform-spezifischer Typ, der entweder einen Zeiger oder ein Handle darstellen kann. GlobalAlloc und das Gegenstück GlobalFree werden im nächsten Abschnitt genauer erklärt. Mit der GlobalMemoryStatus-Funktion wird die Größe des momentan verfügbaren Speichers abgerufen, um zu ermitteln, wie viel Speicher durch die GlobalAlloc-Funktion zugeordnet werden soll. Die Win32-Signatur für GlobalMemoryStatus lautet:

      void GlobalMemoryStatus(LPMEMORYSTATUS lpBuffer);
    

Es wird ein Zeiger/Verweis auf eine Struktur an GlobalMemoryStatus übergeben und diese Funktion füllt die Struktur mit Speicherinformationen. Die Struktur ist in Abbildung 2 definiert. Das wichtigste Feld in der Struktur ist das dwAvailVirtual-Feld, das die Größe des verfügbaren Speichers in Bytes enthält, nachdem die GlobalMemoryStatus-Funktion aufgerufen wurde.

Das EatMem-Tool ist in erster Linie als while-Schleife strukturiert, die so lange durchlaufen wird, bis die aktuelle Systemzeit die Zeit überschreitet, die sich aus der Systemzeit plus der Dauer in Minuten ergibt, die beim Programmstart an der Befehlszeile angegeben wird. Innerhalb der großen while-Schleife befindet sich eine for-Schleife, die achtmalig durchlaufen wird. Diese sekundäre Schleife ist für die eigentliche Programmfunktion nicht notwendig. Sie bietet jedoch eine gute Möglichkeit, ständig den aktuellen Status visuell auszugeben.

Das Tool im Detail

Im Folgenden wird jeder Abschnitt des EatMem-Tools ausführlich beschrieben. Die grafische Konsolenausgabe wird im nächsten Abschnitt und Alternativen sowie Änderungen am Code werden später beschrieben. In Abbildung 3 wird der Code dargestellt, der zum Starten von EatMem erforderlich ist.

Zuerst wird eine Konstante mit der Größe 1024 deklariert, da alle EatMem-Anzeigen in Kilobytes ausgegeben werden. Die Berechnungen werden jedoch in Bytes ausgeführt. Als Nächstes wird ein Random-Objekt instanziert, damit später Pseudo-Zufallszahlen generiert werden können.

Anschließend wird mit einer einfachen Methode die Dauer (in Minuten) erfasst, über die hinweg EatMem ausgeführt werden soll. In diesem Code wird davon ausgegangen, dass genau ein Befehlszeilenargument in der Form -dN vorhanden ist, wobei N eine Zahl darstellt. Diese Zahl kann abgerufen werden, da args[0] eine Zeichenfolge wie "-d123" repräsentiert und Substring(2) den Zahlenteil "123" darstellt. Mit einer Standardmethode wird die Endzeit festgelegt: Es wird das TimeSpan-Objekt mit der Anzahl der angegebenen Minuten erstellt, und dann wird dieses TimeSpan-Objekt mit dem Überladungsoperator "+" auf die Startzeit von EatMem angewendet.

Schließlich muss noch vor dem Einstieg in die while-Steuerschleife eine kurze Kopfzeile für die Ausgabe durch den folgenden Code angezeigt werden:

// print output headers
WriteLine("Begin EatMem for " + duration + " minutes");
WriteLine("Consuming random 10%-70% of virtual memory");
WriteLine("Launch test harness while EatMem stressor runs");

WriteLine("      Available  Available    Current");
WriteLine("       Physical    Virtual     Memory");
WriteLine(" Time    Memory     Memory  Allocation   Pct.");
WriteLine("---------------------------------------------");

Die aktuelle Anweisung wurde aus Gründen der Übersichtlichkeit leicht abgeändert. Die exakten Anweisungen finden Sie im Codedownload zu diesem Artikel.

Weiter unten in diesem Artikel finden Sie noch viele zusätzliche Informationen. Die meisten Ausgaben des Stressors sind eigentlich überflüssig, da Belastungstools nur den Status des Computers beeinflussen sollen, auf dem sie ausgeführt werden. Die Größe des verfügbaren virtuellen Speichers zu einem bestimmten Zeitpunkt ist die wichtigste zu überwachende Information.

Innerhalb der while-Hauptschleife und der untergeordneten for-Schleife wird die verstrichene Zeit in ein TimeSpan-Objekt aufgenommen und eine Zufallsprozentzahl berechnet, wie im Folgenden dargestellt:

while (DateTime.UtcNow < stopTime) // main loop
{
  for (int cycle = 0; cycle < 8; ++cycle)
  {
    // get the elapsed time so far
    TimeSpan elapsedTime = DateTime.UtcNow - startTime;

    // compute a random percentage
    double randomPct = (r.NextDouble() * (0.70 - 0.10)) + 0.10;
    if (randomPct < 0.10 || randomPct > 0.70)
      throw new Exception("Bad random pct");

Hier generiert EatMem eine Zufallszahl zwischen 0,10 und 0,70 mit einer Standardzuordnungsmethode. Der Methodenaufruf Random.NextDouble gibt eine Zahl zwischen 0,0 und 1,0 zurück, d. h. größer oder gleich 0,0 und kleiner als 1,0. Wenn dieses Intervall mit (0,70 - 0,10) = 0,60 multipliziert wird, entspricht das Intervall einer Zufallszahl zwischen 0,0 und 0,6. Wenn dann 0,10 addiert wird, liegt das Intervall zwischen 0,10 und 0,70.

Als Nächstes muss die Anzahl der Bytes berechnet werden, die zugeordnet (dem Computer entzogen) werden sollen:


// compute number of bytes to eat
GlobalMemoryStatus(ref memStatusBuffer);
uint numBytesToEatInBytes =
  (uint)(randomPct * memStatusBuffer.dwAvailVirtual);
uint numBytesToEatInKB =
  numBytesToEatInBytes / basicUnit;

Die GlobalMemoryStatus-Methode von Win32 wird mit dem C#-Interoperabilitätsmechanismus aufgerufen, um die aktuell verfügbare Anzahl der Bytes des virtuellen Speichers zu ermitteln. Es wurde ein Verweis (ref) auf die MEMORYSTATUS-Struktur eingefügt, da die ursprüngliche C++-Signatur einen Zeiger auf das Strukturargument erfordert. In der Sprache des verwalteten Codes ausgedrückt: Es wird ein Verweis auf eine Struktur eingefügt, da sich die Struktur ändert. Wenn die Speicherstatusinformationen im memStatusBuffer-Objekt abgelegt sind, können sie abgerufen werden. Die Anzahl der zu reservierenden Bytes wird durch die Multiplikation der verfügbaren Bytes mit der Zufallsprozentzahl aus dem vorhergehenden Schritt festgelegt. Außerdem wird das Ergebnis für die Anzeige in Kilobytes konvertiert.

Jetzt kann die Zuordnung von Speicher mit der GlobalAlloc-Funktion beginnen (siehe Abbildung 4). Die GlobalAlloc-Funktion benötigt zwei Argumente. Das erste Argument ist ein Flag, das eine der verschiedenen Methoden für die Zuordnung angibt:

GMEM_FIXED    = 0x0000 Allocates fixed memory.
GMEM_MOVEABLE = 0x0002 Allocates movable memory.
GMEM_ZEROINIT = 0x0040 Initializes memory contents to 0.

GPTR = 0x0040 Combines GMEM_FIXED and GMEM_ZEROINIT.
GHND = 0x0042 Combines GMEM_MOVEABLE and GMEM_ZEROINIT.

Hier wird GPTR = 0x0040 verwendet, um festen Speicher zuzuordnen, der auf Null initialisiert ist.

Das zweite Argument für GlobalAlloc ist die Anzahl der Bytes, die zugeordnet werden sollen. Wenn die Zuordnung erfolgreich ist, gibt GlobalAlloc einen IntPtr zurück, der auf die erste Adresse des zugeordneten Speichers zeigt. Wenn die Zuordnung fehlschlägt, ist der Rückgabewert IntPtr.Zero (etwa äquivalent zu Null in nicht verwaltetem Code). Die Zuordnung kann aus unterschiedlichen Gründen fehlschlagen. Natürlich ist zu erwarten, dass die Zuordnung unter Belastungsbedingungen manchmal fehlschlägt. Daher wird keine Ausnahme ausgelöst, sondern nur eine Warnungsmeldung ausgegeben und ein neuer Versuch gestartet. Die GlobalAlloc-Funktion ordnet die festgelegte Anzahl von Bytes vom Heap zu.

Bei Speicherzuordnungen muss auf drei Komponenten geachtet werden: den physischen Speicher, die Auslagerungsdatei und den virtuellen Speicher. Ein Programm kann eine Anweisung nur dann ausführen, wenn sich die Anweisung im Arbeitsspeicher (RAM) befindet. Der Arbeitsspeicher hat eine feste Größe. 512 MB ist beispielsweise eine übliche RAM-Größe in derzeit verbreiteten Desktopcomputern. Wenn ein Programm kleiner als der Arbeitsspeicher ist, kann das gesamte Programm in den Speicher geladen werden. Virtueller Speicher ist dafür gedacht, Programme ausführen zu können, die größer als der Arbeitsspeicher sind. Das Betriebssystem verwendet eine Auslagerungsdatei, um Code bei Bedarf in den Arbeitsspeicher zu laden oder wieder auszulagern. Die Standardgröße der Auslagerungsdatei beträgt das 1,5-fache des Arbeitsspeichers, kann jedoch bei Bedarf auch größer werden. Die maximale Größe des virtuellen Speichers kann unterschiedlich sein, 2 GB sind jedoch bei einer 32-Bit-Version von Windows® typisch.

Nach der Zuordnung von Speicher muss ein aktualisierter Speicherstatus abgerufen werden, um verschiedene Diagnosedaten anzuzeigen:

// get current memory status
GlobalMemoryStatus(ref memStatusBuffer);
uint availablePhysicalInKB =
 memStatusBuffer.dwAvailPhys / (uint)basicUnit;
uint availableVirtualInKB =
 memStatusBuffer.dwAvailVirtual / (uint)basicUnit;
numBytesToEatInBytes =
 (uint)(randomPct * memStatusBuffer.dwAvailVirtual);
numBytesToEatInKB = numBytesToEatInBytes / basicUnit;

GlobalMemoryStatus wird erneut aufgerufen. Dieses Mal wird der verfügbare physische Speicher wie beim virtuellen Speicher in Kilobytes berechnet. Anschließend wird der zu reservierende Speicher in Bytes und in Kilobytes (für die Anzeige) berechnet.

Die meisten Codezeilen in EatMem dienen der Anzeigeausgabe. Zuerst wird die verstrichene Zeit angezeigt:

// print time
string time = elapsedTime.Hours.ToString("00") + ":" +
       elapsedTime.Minutes.ToString("00") + ":" +
       elapsedTime.Seconds.ToString("00");
Console.Write("{0,8}", time);

Anschließend werden die verfügbaren Größen des physischen und virtuellen Speichers sowie die zuzuordnenden Bytes angezeigt:

// print the memory numbers
Console.Write("{0,12} {1,12} {2,12}",
              availablePhysicalInKB + " KB",
              availableVirtualInKB + " KB",
              numBytesToEatInKB + " KB");

Aus Gründen der Einfachheit wird die Größe des zugeordneten Speichers anhand des aktuellen Speicherstatus angezeigt und nicht anhand des Speicherstatus beim Aufruf von GlobalAlloc. Als Nächstes werden der Prozentsatz des durch EatMem verbrauchten Speichers und ein senkrechter Strich als linker Rand der Statusleiste angezeigt:

// print the current alooc percent number
Console.Write("   " +  (int)(randomPct*100.0));
Console.Write("|");

Für die Statusleiste wird ein alter Trick angewendet:

// do the bars
uint totalNumBars = 20;
uint eachBarIs = availableVirtualInKB / totalNumBars;
uint barsToPrint = numBytesToEatInKB / eachBarIs;

string bars = new string('#', (int)barsToPrint);
string spaces = new string('_', 20-(int)barsToPrint);

Console.Write(bars);
Console.Write(spaces);
Console.WriteLine("|");

Hier werden die Statusleisten mit 20 Zeichen dargestellt. Wenn 20 Zeichen zur Verfügung stehen, kann die Breite, die jedes Zeichen repräsentiert, durch die Größe des verfügbaren Speichers geteilt durch 20 ausgedrückt werden. Die Statusleiste besteht aus Zeichen und auffüllenden Leerzeichen. Die Anzahl der Zeichen ist die Anzahl der zugeordneten Kilobytes geteilt durch die Breite jeder Leiste. Die Anzahl der Leerzeichen ist 20 minus der Anzahl der Zeichen. Im Code wird das Nummernzeichen "#" verwendet. Im nächsten Abschnitt wird die Ausgabe von farbigen Leisten erläutert.

Nach dem Anzeigecode muss die Ausführung eine Weile lang angehalten werden, damit das zu testende System durch die ausgeführten Tests überprüft werden kann. (Schließlich wird ein Stressor dazu eingesetzt, einen Computerstatus zu erzeugen, bei dem Tests unter verringerten Ressourcen ausgeführt werden.) In diesem Fall wird eine Pause von drei Sekunden eingelegt und dann der zugeordnete Speicher freigegeben:

// pause to give app under test stress time 
System.Threading.Thread.Sleep(3000);

// free memory
IntPtr freeResult = GlobalFree(p);
if (freeResult != IntPtr.Zero)
  throw new Exception("GlobalFree() failed");

Die GlobalFree-Funktion benötigt einen Zeiger auf den Speicher, der von einem Aufruf an die GlobalAlloc-Funktion zurückgegeben wurde. Die Funktion versucht, diesen Speicher freizugeben. Wenn der Freigabevorgang erfolgreich ist, gibt die GlobalFree-Funktion null/IntPtr.Zero zurück. Wenn der Vorgang nicht erfolgreich ist, gibt GlobalFree das Eingabeargument zurück. Für den Stressor bedeutet das: Wenn GlobalFree fehlschlägt, muss ein schwerwiegender Ausnahmefehler ausgelöst werden.

Das EatMem-Tool wird beendet, indem eine Informationsleiste am Ende von jeweils acht Sekundärschleifen und eine "Done"-Meldung nach Beendigung der while-Hauptschleife angezeigt werden, wenn die aktuelle Zeit die angegebene Laufzeit überschreitet.

Gestaltung der Konsolenausgabe

Beim Erstellen von Befehlszeilen-Testtools kann durch eine angemessene Verwendung von Textfarben die Ausgabe übersichtlicher gestaltet werden. EatMem verwendet beispielsweise Grün für Speicherzahlen, Rot für Fehlermeldungen und weiße Leerzeichen für die Speicherzuordnungs-Statusleisten. Möglicherweise wurden hier auch zu viele Farben verwendet. Die Ausgabe von farbigem Konsolentext in einer Microsoft® .NET Framework 1.1-Umgebung wird mit den Win32-Funktionen GetStdHndle und SetConsoleTextAttribute erreicht. Sie werden folgendermaßen deklariert:

[DllImport("kernel32.dll")]
extern static IntPtr GetStdHandle (int nStdHandle);

[DllImport("kernel32.dll")]
extern static bool SetConsoleTextAttribute(
    IntPtr hConsoleOutput, int wAttributes);

Zum Festlegen einer Textfarbe wird zuerst die GetStdHandle-Funktion aufgerufen, die ein Handle an die Standardausgabe zurückgibt. Anschließend wird SetConsoleTextAttribute aufgerufen, um die Textfarbe festzulegen. Mit dem folgenden Code wird "Hello" in dunklem Gelb und "Bye" in dunklem Rot ausgegeben:

const int STD_OUTPUT_HANDLE = -11;
IntPtr hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

SetConsoleTextAttribute(hStdOut, 0x0006);
Console.WriteLine("Hello");
SetConsoleTextAttribute(hStdOut, 0x0004);
Console.WriteLine("Bye");

Der Wert -11 ist eine feste Konstante, die die Standardausgabe angibt. Die Konstanten 0x0006 und 0x0004 stehen für dunkles Gelb und Rot. Die angegebene Textfarbe bleibt so lange gültig, bis SetConsoleTextAttribute erneut aufgerufen wird. Für die Konsolentextausgabe sind die "dunklen" Versionen der Farbkonstanten normalerweise zu schwach. Alle Textfarben können durch die logische Verknüpfung der Konstanten über die OR-Bedingung mit dem Wert 0x0008 intensiviert werden. Dieser Code gibt beispielsweise "Howdy Dowdy" in hellem Gelb aus:

SetConsoleTextAttribute(hStdOut, 0x0006 | 0x0008);
Console.Write("Howdy Dowdy");

Da hexadezimale Konstanten nicht besonders beliebt sind, werden üblicherweise symbolische Konstanten verwendet. Die folgenden Konstanten sind für Befehlszeilenprogramme gut geeignet:

const int BLACK = 0x0000;
const int BLUE = 0x0001 | 0x0008; // bright blue
const int GREEN = 0x0002 | 0x0008; // bright green
const int CYAN = 0x0003 | 0x0008; // etc.
const int RED = 0x0004 | 0x0008;
const int MAGENTA = 0x0005 | 0x0008;
const int YELLOW = 0x0006 | 0x0008;
const int WHITE = 0x0007 | 0x0008;

Neben den Textfarben können auch die Hintergrundfarben festgelegt werden. Hierzu werden die Textfarben mit einer Konstante für die Hintergrundfarbe über die logische OR-Bedingung verknüpft. Im folgenden Code wird der Text beispielsweise hellgelb (0x0006 | 0x0008) auf einem dunkelroten Hintergrund (0x0040) ausgegeben:

SetConsoleTextAttribute(hStdOut, 0x0006 | 0x0008 | 0x0040);

Als Hintergrundfarben sind im Allgemeinen Weiß, Grau (dunkles Weiß) und Schwarz am besten geeignet. Im Folgenden sind die Konstanten von EatMem aufgeführt:

const int BGWHITE = 0x0070 | 0x0080;
const int BGBLACK = 0x0000 | 0x0000;
const int BGGRAY = 0x0070;

Mit diesem Code kann eine gute Farbformatierung erreicht werden. Die Speicherstatusleisten in EatMem werden folgendermaßen ausgegeben:

string bars = new string('_', (int)barsToPrint);
string spaces = new string('_', 20-(int)barsToPrint);
SetConsoleTextAttribute(hStdOut, BLACK | BGWHITE);
Console.Write(bars);

SetConsoleTextAttribute(hStdOut, WHITE | BGBLACK);
Console.Write(spaces);

SetConsoleTextAttribute(hStdOut, GREEN | BGBLACK);
Console.WriteLine("|");

Durch schwarze Leerzeichen auf weißem Hintergrund kann eine weiße Leiste suggeriert werden. Durch das Umschalten auf weißen Text auf schwarzem Hintergrund wird ein schwarzer Hintergrund erzeugt. Mit dem Unterstrich wird eine Linie unter jeder Leiste angezeigt.

Die Console-Klasse in .NET Framework 2.0 bietet eine breite Unterstützung für die Ausgabeformatierung. Wenn Sie in einer .NET Framework 2.0-Umgebung arbeiten, können Sie mit der neuen Console-Klasse wesentlich mehr erreichen als mit dem P/Invoke-Mechanismus. Die Console-Klasse enthält Methoden und Eigenschaften zum Abrufen und Festlegen der Größe des Bildschirmpuffers, des Konsolenfensters und des Cursors, zum Festlegen der Position des Konsolenfensters und des Cursors, zum Schreiben und Löschen von Daten in den Bildschirmpuffer, zum Ändern der Vordergrund- und Hintergrundfarben, zum Ändern von Text in der Titelleiste der Konsole und zum Wiedergeben eines akustischen Signals.

Toolerweiterungen

Das vorgestellte EatMem-Tool kann in der vorliegenden Form verwendet werden. Es wurde jedoch so entwickelt, dass Sie leicht Änderungen daran vornehmen können. Im Folgenden werden ein paar Ideen zur Erweiterung oder Verbesserung von EatMem vorgestellt.

Die wichtigste Änderungsmöglichkeit besteht in der Speicherzuordnungsmethode. EatMem verwendet die Win32-Funktionen GlobalAlloc und GlobalFree, es gibt jedoch verschiedene andere Speicherzuordnungsfunktionen. Eine Möglichkeit bieten die verwalteten Methoden Marshal.AllocHGlobal und Marshal.FreeHGlobal, die verwaltete Wrapper um LocalAlloc und LocalFree darstellen. Die AllocHGlobal-Methode ist leichter zu handhaben als GlobalFree, jedoch nicht so flexibel. Wenn Sie jedoch den P/Invoke-Mechanismus direkt verwenden möchten, ist AllocHGlobal die Lösung.

Eine andere Alternative zu GlobalAlloc und GlobalFree sind die Win32-Funktionen VirtualAlloc und VirtualFree. Die VirtualAlloc-Funktion reserviert oder übergibt einen Seitenbereich im virtuellen Adressbereich des aufrufenden Prozesses. Ein Beispiel für VirtualAlloc finden Sie in Abbildung 5.

Eine Verbesserung der Grundversion von EatMem wäre ein Schutz gegen fehlgeschlagene Speicherzuordnungsversuche. Wie bereits erwähnt, gibt EatMem einfach eine Fehlermeldung aus und startet einen neuen Versuch, wenn die Speicherzuordnung fehlschlägt. Dadurch könnte eine Serie fehlgeschlagener Versuche auftreten, die nicht bemerkt werden. Sie könnten einfach einen Zähler hinzufügen, der die Anzahl der aufeinander folgenden Zuordnungsfehler überwacht, und bei Überschreiten eines bestimmten Zählerschwellenwertes eine Ausnahme auslösen oder die maximale Zufallsprozentzahl (in dieser Version auf 70 Prozent hartcodiert) dynamisch reduzieren. Eine mögliche Abwandlung dieser Verbesserung wäre, den Prozentsatz der fehlgeschlagenen Speicherzuordnungen zu überwachen und die Stressor-Parameter entsprechend anzupassen.

Wenn Sie in einer .NET Framework 2.0-Umgebung arbeiten und die verwaltete AllocHGlobal-Funktion nicht verwenden möchten, weil Sie die Flexibilität von Win32-GlobalAlloc benötigen, können Sie von dem neuen SafeHandles-Namespace profitieren, der Klassen für Datei- und Betriebssystemhandles bereithält. Sie könnten eine von SafeHandle abgeleitete Klasse für GlobalAlloc erstellen, die wie in Abbildung 6 aussehen könnte.

Diese Vorgehensweise hat einige Vorteile: Es kann eine using-Anweisung verwendet werden, um den Speicher bei einem Ausnahmefehler leichter zu löschen. Ein Beispiel:

using(SafeGlobalAllocHandle mem =
   SafeGlobalAllocHandle.GlobalAlloc(0, numBytes))
{
  if (mem.IsInvalid) { ... }
  else { ... }
}

Mit dieser Methode wird das System wesentlich zuverlässiger. Dies kann besonders in Situationen wichtig sein, in denen der EatMem-Code direkt in eine Anwendung oder ein Testprogramm kopiert wird, das anfällig für asynchrone Fehler ist.

Andere Entwickler haben die Ruhezeit und das Speicherzuordnungsmuster zufallsgesteuert programmiert. Das vorliegende EatMem pausiert bei jeder sekundären for-Schleife genau 3000 ms lang. Durch eine zufällig gesteuerte Ruhezeit kann in einigen Testsituationen ein realistischerer Verbrauch des Hintergrundspeichers erzielt werden. Anstelle eines einmaligen Speicherblocks kann auch stufenweise Speicher zugeordnet werden. Bei diesem Ansatz ist es wichtig, eine ArrayList-Auflistung zu verwalten, die die inkrementellen IntPtr-Zuordnungsobjekte enthält, damit der Speicher nach Bedarf auch wieder inkrementell freigegeben werden kann, indem die Auflistung durchlaufen wird.

Außer diesen größeren Änderungen sind natürlich viele weitere Änderungen denkbar, wie z. B. das Parameterisieren der Speicherzuordnung, das Abrufen von zusätzlichen Speicherstatusinformationen mithilfe der GlobalMemoryStatus-Funktion und das Einfügen weiterer Fehlerüberprüfungen. In diesem Artikel wurde zugunsten der Kürze auf die Fehlerbehandlung weitgehend verzichtet.

Zusammenfassung

Zusätzlich zur Belastung durch den hier behandelten Arbeitsspeicher-Stressor sollten Sie das System auch durch Verringern der CPU-Verfügbarkeit und des Festplattenspeicherplatzes belasten. Solche Stressoren funktionieren ähnlich wie die Arbeitsspeicherreduzierung, es gibt jedoch dabei ein paar weitere Tricks, die in einem späteren Testlauf-Artikel des Autors behandelt werden.

Andere Arten von Belastungstests, die von Interesse sein könnten, sind beispielsweise Tests, bei denen eine Testautomatisierung über einen längeren Zeitraum unter normalen Bedingungen ausgeführt wird. Eine andere Variante ist die Belastungsanalyse oder einer Testanwendung immer höhere Verringerungen an Arbeitsspeicher, CPU-Verfügbarkeit oder Festplattenspeicher zuzumuten. Hierbei werden die funktionalen Grenzen des zu testenden Systems ausgelotet. Insbesondere bei Webanwendungen können weiterhin Belastungstests ausgeführt werden, die auch als "Auslastungstests" bezeichnet werden. Dabei werden Webanwendungen und/oder Webserver hohen Benutzerauslastungen ausgesetzt. Im Bereich der Belastungstests gibt es also noch viele interessante Themen. Beachten Sie daher auch zukünftige Artikel mit weiterführenden Themen.

Senden Sie Ihre Fragen und Kommentare für James McCaffrey an  testrun@microsoft.com.

Der Autor

James McCaffrey arbeitet für Volt Information Sciences Inc. und organisiert technische Schulungen für Softwareentwickler von Microsoft. Er hat an verschiedenen Microsoft-Produkten mitgearbeitet, unter anderem Internet Explorer und MSN Search. James McCaffrey kann unter jmccaffrey@volt.com oder v-jammc@microsoft.com kontaktiert werden. Dank gilt Lutz Roeder für seine Mithilfe bei diesem Artikel.

Aus der Ausgabe Mai 2006 des MSDN Magazine.

Anzeigen:
© 2014 Microsoft. Alle Rechte vorbehalten.