Italian community of Lazarus and Free Pascal

Programmazione => Generale => Topic aperto da: bonmario - Novembre 27, 2018, 08:24:04 pm

Titolo: Problemi di memoria ... RAM
Inserito da: bonmario - Novembre 27, 2018, 08:24:04 pm
Ciao a tutti,
ho un programma che mi sta creando dei problemi di memoria ... RAM.

Provo a spiegare il giro:
- il programma lavora con i thread (8 in contemporanea)
- in ogni thread, usando l'oggetto "TUnZipper" del sorgente "zipper.pp" di FPC, in particolare la proprietà "OnDoneStream" estraggo da un file compresso un file e lo salvo in uno stream

il codice incriminato è questo:
Codice: [Seleziona]
  procedure TUnZipSingoloFile.DoDoneOutZipStreamSuStream(Sender: TObject; var aStream: TStream; AItem: TFullZipFileEntry);
  begin
    aStream.Position:=0;

    WrkStreamOut.LoadFromStream(aStream);

    //Se arriva fin  qui, l'estrazione è andata a buon fine
    VFUnZipOk:=True;

    //Adesso posso pulire lo stream
    FreeAndNil(aStream);
  end;


Se quando lancio il programma, arriva ad estrarre in contemporanea su più thread dei files di grosse dimensioni (circa 100 mega l'uno), ho degli errori di "out of memory".

Ora, tenendo conto del codice che ho postato qui sopra, credo che da quando ha appena finito di eseguire la "LoadFromStream", fino a quando non ha eseguito la "FreeAndNil(aStream);", ho in memoria 2 copie dello stesso stream (di circa 100 mega), è corretto?

Se è così, c'è modo di ottimizzare la cosa, per evitare di fare una "copia", anche se per pochi millisecondi, ma di passare direttamente il contenuto da uno stream all'altro ??

Spero di essermi spiegato ...

Grazie in anticipo, Mario
Titolo: Re:Problemi di memoria ... RAM
Inserito da: xinyiman - Novembre 28, 2018, 01:24:40 pm
Ciao bonmario, non penso che il problema sia la memoria sovraccaricata dai file letti, perchè quella se la gestisce il sistema operativo ricorrendo anche all'harddisk quando serve.

La prima cosa che mi sento di suggerirti è di sostituire il

    FreeAndNil(aStream);

con

aStream.Free;
aStream:=nil;

Questo perchè se dentro il destructor della classe hai del codice da eseguire il FreeAndNil non lo esegue. Se questo non aiuta sposta il FreeAndNil fuori dalla funzione. In secondo luogo in casi un po' fumosi come questo conviene sempre creare un esempio che riproduce l'errore in modo che possiamo aiutarti correttamente senza buttare via tanto tempo.
Titolo: Re:Problemi di memoria ... RAM
Inserito da: bonmario - Novembre 28, 2018, 01:30:28 pm
La prima cosa che mi sento di suggerirti è di sostituire il

    FreeAndNil(aStream);

Ho sempre usato FreeAndNil, perché il suo sorgente è questo:

Codice: [Seleziona]
    procedure FreeAndNil(var obj);
      var
        temp: tobject;
      begin
        temp:=tobject(obj);
        pointer(obj):=nil;
        temp.free;
      end;

ed ho sempre pensato che fosse la stessa cosa ... anzi meglio, perché con un'unica istruzione gli facevo eseguire le 2 cose contemporaneamente.  Sbaglio io ?


In secondo luogo in casi un po' fumosi come questo conviene sempre creare un esempio che riproduce l'errore in modo che possiamo aiutarti correttamente senza buttare via tanto tempo.

In realtà, di tutta la pappardella scritta nel primo post, l'unica cosa che mi interessa al momento è questa:
Citazione
Ora, tenendo conto del codice che ho postato qui sopra, credo che da quando ha appena finito di eseguire la "LoadFromStream", fino a quando non ha eseguito la "FreeAndNil(aStream);", ho in memoria 2 copie dello stesso stream (di circa 100 mega), è corretto?

Se è così, c'è modo di ottimizzare la cosa, per evitare di fare una "copia", anche se per pochi millisecondi, ma di passare direttamente il contenuto da uno stream all'altro ??


Ciao, Mario
Titolo: Re:Problemi di memoria ... RAM
Inserito da: xinyiman - Novembre 28, 2018, 01:42:27 pm
Funzionare funziona, però se hai una classe dove nel destructor gestisci la liberazione di altra memoria allora può creare problemi. Ipotizza che la tua classe faccia riferimento ad altra memoria attraverso i puntatori usando il FreeAndNil liberi la porzione di memoria occupata dalla tua classe ma non viene eseguito il codice del destructor e quindi la memoria occupata a cui facevi riferimento con i puntatori rimane occupata.

Per quanto riguarda la tua domanda non penso si possa ottimizzare più di così.
Titolo: Re:Problemi di memoria ... RAM
Inserito da: bonmario - Novembre 28, 2018, 06:20:38 pm
Mi stai dando una bruttissima notizia ... penso di avere centinaia di FreeAndNil sparse nei miei sorgenti !!!

Ciao, Mario
Titolo: Re:Problemi di memoria ... RAM
Inserito da: xinyiman - Novembre 29, 2018, 08:10:22 am
Mi dispiace, ma l'ho imparato sulla mia pelle.
Titolo: Re:Problemi di memoria ... RAM
Inserito da: Stilgar - Novembre 29, 2018, 03:32:36 pm
Bonmario,hai rimosso il "override" dai tuoi distruttori?
Codice: [Seleziona]
procedure TObject.Free;
begin
  // the call via self avoids a warning
  if self<>nil then
    self.destroy;
end; 
In tal caso occhio che non usi il destroy della tua classe, ma quello di object.In quel caso ti ritrovi nei guai per il motivo che indicava xinyiman (http://www.lazaruspascal.it/index.php?action=profile;u=1).
La "out of memory" potrebbe essere generata anche per altri motivi. La free and nil dovrebbe darti un SIGSEG in caso di rogne.

Hai provato a mettere degli Writeln per"loggare" i passaggi?Numero del thread,dimensione file, bla bla.Le informazioni che ti possono servire per capire in che momento scatta lo sfondamento (apparente) di memoria?
http://wiki.freepascal.org/paszlib#Unzip_file_to_a_stream (http://wiki.freepascal.org/paszlib#Unzip_file_to_a_stream)
Il fatto che qui faccia la copia del testo dentro la memo è un caso simile al tuo?Altrimenti al posto della loadFromStream, non puoi usare direttamente lo stream?La distruzione viene delegata all'utilizzatore. Ergo puoi farne quello che vuoi.
Codice: [Seleziona]
Procedure TUnZipper.CloseOutput(Item : TFullZipFileEntry; var OutStream: TStream);

Begin
  if Assigned(FOnDoneStream) then
  begin
    FOnDoneStream(Self, OutStream, Item);
    OutStream := nil;
  end
  else
    FreeAndNil(OutStream);
  DoEndOfFile;
end;
Perchè in altrernativa, potresti costruirti tu lo stream da usare come destinazione dell'unzip. Frutti la callback OnCreateStream.In questo modo non hai duplicazione, spari direttamente il dato decompesso dove vuoi tu.Dai che quando ha finito ti chiama la Done. ;)Insomma, rivisitando la logica del giro, poresti toglierti il dubbio della duplicazione.
 :)

Stilgar
Titolo: Re:Problemi di memoria ... RAM
Inserito da: bonmario - Novembre 29, 2018, 04:29:48 pm
Alla fine ho risolto.
I problemi erano 2:
- uno mio che non liberavo la memoria di un oggetto (trovato tramite "heaptrc")
- l'altro era dovuto al fatto che, per facilitarmi le operazioni di lettura, leggevo lo stream (che è un XML), con "TXMLDocument" della unit Laz2Dom.

TXMLDocument, a fronte di un XML che su disco occupa 50 mega, mi occupa circa 400 mega, per un totale di circa 450 mega.
Quando più thread in contemporanea gestivano 3 o 4 di questi files, avevo il problema.

Ora ho risolto leggendo lo stream XML con SAX invece che con DOM.

Grazie a tutti, Mario