Warum bekomme ich eine Out of Memory Exception trotz freiem Arbeitsspeicher?



  • Guten Tag

    Ich muss wieder einmal alte Datensätze konvertieren und in eine Datenbank übergeben.

    Ein solcher Datensatz besteht aus einer bis zu 850MB großen Datei.

    Wenn ich jetzt versuche die Datei zu laden, bekomme ich eine Out of Memory Exception beim Festlegen der Array-Größe (maximal 850MB) obwohl ich laut Taskmanager noch 6GB Arbeitsspeicher frei habe.

    Weis jemand woran das liegen kann?

    Wenn ich dann Programme schließe und damit ich wieder 7,5GB frei habe, bekomme ich diese Excetion merkwürdigerweise nicht mehr.

    Ich arbeite unter:
    Winows 7
    Visual Studio2005 (Ich weis, das ist antik aber besser als garnichts)

    Der Codeabschnitt sieht folgendermaßen aus:

    .
    .
    .
    // Datensatz Öffnen
    CFile cFile;
    CFileException cFe;
    if(!cFile.Open(strDataFilePath,CFile::modeRead,&cFe))
    {
    	return CError::MessageFileOpen(strDataFilePath,cFe,__LINE__,DEF_CLASSNAME_PARSE_TWDB);
    }
    
    // Datensatzgröße ermitteln und sichergehen dass keine zu großen Datensätze bearbeitet werden
    ULONGLONG ullFileSize = cFile.GetLength();
    if(ullFileSize > DEF_DATA_MAX_SIZE) 	// Bis jetzt war die maximale Datensatzgröße 850MB
    {					// Warnung bei 1024MB -> DEF_DATA_MAX_SIZE = 1073741824 Byte
    	if(!CError::QuestionFileSize(strDataFilePath,ullFileSize ,__LINE__,DEF_CLASSNAME_PARSE_TWDB)){return false;}
    }
    
    size_t siBufferSize = size_t(ullFileSize) +1; // Der Parser braucht einen Null terminierten String
    BYTE *byBuffer = new BYTE[siBufferSize]; // -> Hier bekomme ich die Memory Exception wenn ich weniger als 7GB Arbeitsspeicher frei habe
    ZeroMemory(byBuffer,siBufferSize);
    .
    .
    .
    
    // Daten laden & parsen
    
    .
    .
    .
    delete [] byBuffer;
    byBuffer = NULL;
    .
    .
    .
    

    Ich bekomme die Memory Exception schon im ersten Durchlauf.
    Es kann also eigentlich kein Speicherleck oder dergleichen sein.
    Der Task-Manager zeigt auch keine Veränderung in der Speicherbelegung.

    Kann mir bitte jemand einen Tipp geben wo ich den Fehler finden kann?

    Das Ganze ist zwar nur ein Programm das für den internen Gebrauch gedacht ist und warscheinlich nur dieses eine Mal für das Einlesen der Daten verwendet wird.
    Ich würde aber trotzdem gerne wissen wo der Fehler ist damit ich ihn nicht mehr mache.

    mfg Bernhard



  • 32 oder 64 bit?

    Falls du keinen 64 bit Compiler bisher eingesetzt hast: Compiling 64-bit applications in Visual Studio 2005



  • Sehr elegant ist deine Methode nicht und vor allem sehr Fehleranfällig. Sei es das du keinen Speicher bekommst oder der nicht mehr frei gegeben wird oder was auch immer passieren kann, ganz zu schweigen vomn der geschwindigkeit des ganzen. Besser ist es doch gleich das ganze als File Mapping zu öffnen, dann wird dir das File in einen Adressbereich eingeblendet, du kannst ganz normal damit arbeiten als würde es im Speicher stehen ohne es erst in den speicher laden zu müssen und ich denke 850 MB reinladen über CFile wird sicher nicht nur ein Augenzwinkern an Zeit benötigen. 😉



  • @Th69

    Danke für den Link für die Umstellung auf 64Bit.
    Ich arbeite tatsächlich noch mit 32Bit.

    Aber eigentlich müssten die 850MB noch weit im 32Bit Bereich liegen.
    Oder gibt es da Kompatibilitätsprobleme?

    @CTecS
    Speicherlecks sind möglich.
    Aber ich achte eigentlich immer darauf das der Speicher so bald wie möglich freigegeben wird, es wenn möglich keinen return zwischen new & delete gibt und wenn es doch einen return gibt, achte ich peinlich darauf das auch dort der Speicher freigegeben wird.
    Bei einem Speicherleck hätte aber eigentlich der Verbrauch im TaskManager angezeigt werden müssen.
    Und GetProcessMemoryInfo zeigte auch nichts Auffälliges beim Vergleich vor und nach dem Funktionsaufruf.

    Aber du hast recht.
    Memory mapping währe die bessere Lösung.
    Ich hatte auch daran gedacht.
    Aber in diesem Fall hätte es bedeutet das ich die Dateien zuerst kopieren müsste da die Daten beim Parsvorgang verändert werden.
    (Aufbereiten der Daten damit der Parsaufwand reduziert wird,...)

    Da die mögliche Lesegeschwindigkeit der Daten relativ niedrig ist, (ca. 1MB/s) bringt das Memory Mapping keinen erkennbaren Geschwindigkeitsvorteil.

    Ich hatte mich nur gefragt warum ich die Exception bekommen habe obwohl noch genuch Speicher vorhanden war.

    Es wird auch zeit den alten Parser in Rente zu schicken und etwas neues zu schreiben (und diesmal ordentlich).



  • Es kommt darauf an, wie der Speicher fragmentiert ist, denn du hast ja 850MB am Stück reservieren wollen.



  • Danke für den Hiweis.
    Daran hatte ich garnicht gedacht.

    Da ich in naher Zukunft alle meine Parser auf Memory Mapping tauglich umschreibe, wird dieses Problem hoffentlich nicht mehr auftreten.



  • .Bernhard. schrieb:

    Da ich in naher Zukunft alle meine Parser auf Memory Mapping tauglich umschreibe, wird dieses Problem hoffentlich nicht mehr auftreten.

    Das Problem mit 32 Bit hast du dann genau so.
    Bei 32 Bit ist es ja nicht so dass ihm der Speicher ausgehen würde, sondern der Adressraum. Und der wird bei Memory Mapping genau so benötigt.


Anmelden zum Antworten