Arbeitsspeicher wird zugemüllt



  • Hallo,

    habe Programm das diese beiden Funktionen aufruft. Das Programm stürzt nach ca . 15 Minuten mit der Fehlermeldung "out of memory" immer ab. Woran kann das liegen ?

    void __fastcall TForm1::Execute()
    {
    	//---- Thread-Code hier einfügen ----
    	Form1->Button1->Enabled = false;
    	Form1->Button2->Enabled = false;
    
       TStringList *file = new TStringList();
       file->LoadFromFile(filename);
    
       TStringList *auktions = new TStringList();
       for(int i = 0; i < file->Count; i++) {
    	   if(file->Strings[i].SubString(0,4) == "http" ||
    	   file->Strings[i].SubString(0,4) == " htt")
    	   auktions->Add(file->Strings[i]);
       }
       file->~TStringList();
    
       for(int i = 0; i< auktions->Count; i += 2) {
    
    		if(!FileExists(auktions->Strings[i].SubString(82,7)+".csv")) {
    		Form1->Memo1->Clear();
    		int status = downloadAuktion(auktions->Strings[i],auktions->Strings[i+1],0);
    		Form1->weine->SaveToFile(auktions->Strings[i].SubString(82,7)+".csv");
    		Form1->weine->Clear();
    
    		if(status) {
    		downloadAuktion(auktions->Strings[i],auktions->Strings[i+1],1);
    		Form1->weine->SaveToFile(auktions->Strings[i].SubString(82,7)+"_2.csv");
    		Form1->weine->Clear();
    		}
            }
    
       }
    
    	Form1->Button1->Enabled = true;
    	Form1->Button2->Enabled = true;
    
    }
    //---------------------------------------------------------------------------
    int TForm1::downloadAuktion(AnsiString startstr, AnsiString endestr, int anzahl){
    
    	 startstr = trimAnfang(startstr);
    	 endestr = trimAnfang(endestr);
    
    	 //Nummer von bis rausfinden     (start,ende:int)
    	int pos_start = startstr.Pos("intObjectID=") + 12;
    	int pos_start2 = PosStringAfterPos(pos_start,"&sid",startstr);
    	int start = startstr.SubString(pos_start,pos_start2-pos_start).ToInt();
    	int pos_ende = endestr.Pos("intObjectID=") + 12;
    	int pos_ende2 = PosStringAfterPos(pos_start,"&sid",endestr);
    	int ende = endestr.SubString(pos_ende,pos_ende2-pos_ende).ToInt();
    
    	//replaceString erstellen (
    	AnsiString replace = startstr.SubString(0,pos_start-1)+"###R###"+
    	startstr.SubString(pos_start2,startstr.Length() - pos_start);
    
    	replace = explode(replace," ",0);
    
    	int n = 0;
    	Form1->Memo1->Clear();
    	if(anzahl == 1) start += 500;
    	for(int i = start; i < ende + 1; i++, n++) {
    
            if(n == 500) return 1;
    
    		Wein *wein = new Wein(Form1->IdHTTP1->Get(StrReplace(replace, "###R###", IntToStr(i))),StrReplace(replace, "###R###", IntToStr(i)));
    
    		if(wein->GetWerte() == "Preis nicht vorhanden") {
    			Form1->Memo1->Lines->Add("Preis nicht vorhanden "+
    			StrReplace(replace, "###R###", IntToStr(i)));
    		}
    		else {
    			Form1->Memo1->Lines->Add(wein->GetWerte()+";"+IntToStr(i));
    			Form1->weine->Add(wein->GetWerte()+";"+IntToStr(i));
    		}
    
    		delete wein;
    		//Synchronize(&UpdateCaption);
    	}
    
        return 0;
    }
    


  • Hallo

    Beim schnellen Überblick fällt mir auf, das du zwar Speicher für die Instanzen file und aktions reservierst, aber nie wieder freigibst.

    Und

    file->~TStringList();
    

    ist auch nicht gültig. Destruktoren sollen nicht manuell aufgerufen werden, sondern dürfen nur vom delete-Operator verwendet werden.

    bis bald
    akari



  • Danke, aber daran lags leider auch nicht 😞



  • Hallo

    So ein Speicherüberlauf auf dem Heap (Stack-Überlauf hat mw. andere Fehlermeldung) kann fast nur zwei Gründe haben :
    - Du reserviert irgendwo mit new Speicher, ohne es mit delete freizugeben
    - Du verwendest irgendwo ein großes Array, für den aber kein freier zusammenhängender Speicherblock mehr reserviert werden kann, obwohl noch kleinere Stücke frei wären. Das kann zum Beispiel auch ein wirklich langer String sein

    Ich sehe in deinem Quellcodeauszug keine weiteren Hinweise. Schau dir mal an was in der Instans Wein passiert. möglicherweise hast du dort ein Leck.

    bis bald
    akari



  • Habe in Wein.cpp keine dynamischen Speicherzuweisungen oder malloc Anweisungen.

    Wenn der Speicher voll ist bleibt er hier hängen:

    procedure TStringList.SetCapacity(NewCapacity: Integer);
    begin
      if (NewCapacity < FCount) or (NewCapacity > MaxListSize) then
        Error(@SListCapacityError, NewCapacity);
      if NewCapacity <> FCapacity then
      begin
        ReallocMem(FList, NewCapacity * SizeOf(TStringItem));
        FCapacity := NewCapacity;
      end;
    end;
    

    Kann es sein das es am TStringList "weine" liegen kann ?



  • Hallo

    Theoretisch schon, wenn es um wirklich viele Einträge geht. ReallocMem versucht ein altes, kleineres Array in ein neues, größeres Array umzukopieren. Dazu muß zuerst das neue Array dynamisch auf dem Heap erzeugt werden. Und wenn kein zusammenhängender Platz mehr dafür frei ist, gibts die Fehlermeldung. Wie groß wird den deine Liste?

    bis bald
    akari



  • 500 - 1000 Strings. Danach leere ich das Array mit weine->Clear();



  • Hallo

    Diese Anzahl ist sicherlich nicht das Problem an sich.
    Wobei ich in deinem Code zwar sehe, wie du den Inhalt von Form1->Memo1 löscht, aber nicht von Form1->weine...

    Ich kann dir nur noch zwei allgemeine Tips geben :
    - Reduziere zum Testen dein Programm indem du einzelne Aufrufe auskommentierst. Versuche herauszufinden, welche Aufrufe du minimal deaktivieren mußt, bis der Fehler nicht mehr auftritt.
    - Benutzt den Debugger, um ungewöhnliche Werte zur Laufzeit zu finden.

    bis bald
    akari



  • Hallo Gerhard,

    void __fastcall TForm1::Execute()
    {
    //---- Thread-Code hier einfügen ----
    ...
    }

    du arbeitest nicht zufällig mit einem Thread? Und rufst dann die Zugriffe auf die Form-Komponenten unsynchronisiert auf?

    P.S. Die "Form1->" Anweisungen kannst du dir sparen (da die Funktionen ja schon innerhalb von Form1 deklariert sind).

    Und sofern "Wein" keine VCL-Klasse ist, so brauchst du diese auch nicht dynamisch zu reservieren:

    Wein wein(/*Parameter*/);
    

    reicht dann völlig.



  • HalloTh69,

    hab die Funktionen nun in einem Thread ausgelagert, aus dem Grund hab ich auch die Form1-> Anweisungen stehen lassen. Wein hab ich nun so erstellt wie du gesagt hast, allerdings ist mir nicht ganz klar wie ich das Objekt dann lösche.

    @akari
    Ich denke ich habe nun die Ursache gefunden was mir den Arbeitsspeicher frisst.
    Ersetze ich

    Form1->IdHTTP1->Get
    

    mit einem beliebigen String stürzt das Programm nicht mehr ab. Form1->IdHTTP1 liefert mir eine ganze HTML Seite die ziemlich groß ist, kann es daran liegen, wenn ja was mach ich am besten dagegen ?



  • habe den Übeltäter gefunden, eine Funktion die aufgerufen wurde und jedes mal ein TStringList anlegt, hatte diese völlig übersehen. Möchte mich für eure Mühen trotzdem bedanken!



  • Hallo

    Um alle Unklarheiten zu beseitigen :

    Wein hab ich nun so erstellt wie du gesagt hast, allerdings ist mir nicht ganz klar wie ich das Objekt dann lösche.

    Weil du es statisch deklarierst und nicht dynamisch wie vorher, wird die Instanz automatisch gelöscht wenn der Gültigkeitsbereich der Variable verlassen wird. Du must es nicht selbst löschen.

    bis bald
    akari



  • Die VCL schreit geradezu nach smart pointern. Wenn du die boost Bibliotheken installiert hast oder das RAD Studio 2009 solltest du einen Blick auf shared_ptr oder scoped_ptr werfen. Zur Not ginge auch sowas:

    template<typename T>
    class scoped_ptr
    {
       T* object_;
    
    public:
    	scoped_ptr() : object_( 0 )
    	{
    	}
    
    	scoped_ptr( T* obj ) : object_( obj )
    	{
    	}
    
    	~scoped_ptr()
    	{
    		reset();
    	}
    
    	void reset()
    	{
    		if( object_ )
    		{
    			delete object_;
    			object_ = 0;
    		}
    	}
    
    	void reset( T* object )
    	{
    		reset();
    		object_ = object;
    	}
    
    	T& operator*()
    	{
    		assert( object_ );
    		return *object_;
    	}
    
    	T* operator->()
    	{
    		assert( object_ );
    		return object_;
    	}
    
    	T* get()
    	{
    		return object_;
    	}
    
    	operator bool()
    	{
    		return object_;
    	}
    
    private:
    	// keine Kopien zulassen
    	scoped_ptr( const scoped_ptr& lhs );
    	scoped_ptr& operator=( const scoped_ptr& lhs );
    };
    
    int main()
    {
       scoped_ptr<TStringList> StringList( new TStringList() );
    
       // Verwendung wie gehabt
       StringList->Add( "Zeile 1" );
    
       // Verwendung des Rohzeigers in der VCL API
       ExtractStrings( TSysCharSet() << ';', TSysCharSet(), "Key;Value", StringList.get() );
    }
    


  • Hallo,

    Solange man nur scoped_ptr verwendet, kann man den auch durch auto_ptr ersetzen und braucht kein boost mehr.



  • Ja schon,

    aber den scoped_ptr kann man nicht zweckentfremden, den auto_ptr schon. Schon diese Zeile Code lässt´s krachen.

    typedef auto_ptr<TMyForm> FormPtr;
    
    void show_form( FormPtr Form )
    {
       Form->ShowModal();
    }
    


  • Das ist natürlich richtig. Bei dem von dir gezeigten Beispiel macht das aber kein Problem.
    Ich habe eigentlich auch immer lieber die boost Varianten genommen. Der Begriff scoped_ptr drückt meiner meinung nach gut aus was er macht.
    auto_ptr ist da schon leichter zu missverstehen. Ich bin selber bei klassen- bzw. funktionsinternen Zeigern wieder auf auto_ptr umgestiegen weil mein Chef das so wünscht. Er mag boost irgendwie nicht. 😞


Anmelden zum Antworten