Avatar billede rozh Nybegynder
16. oktober 2007 - 00:34 Der er 14 kommentarer og
3 løsninger

leak med dynamic array

Hej

Mit spørgsmål er i to dele:

1) Hvis jeg har et dynamic array som indeholder fx 1000 string værdier. Når jeg afslutter mit program så kan jeg sætte arrayet til nil, og så ved jeg at der ikke er noget leak. fx

var A: Array of string;
    I: Integer;
begin
  SetLength(A, 1000);
  for I:=0 to 999 do A[I]:='et eller andet';
  A:=nil;
end;

Men hvad sker der hvis jeg IKKE sætter arrayet til nil til allersidst ? Vil jeg da ikke få en leak ? fx

var A: Array of string;
    I: Integer;
begin
  SetLength(A, 1000);
  for I:=0 to 999 do A[I]:='et eller andet';
end;

Det gør jeg nemlig ikke ifølge EurekaLog, men det undrer mig meget, for hvad bliver der af al hukommelsen som blev allokeret? Delphi free'er jo ikke automatisk dynamic arrays så vidt jeg ved.


2) Så har jeg et andet spørgsmål
Jeg har en timer. I denne timers OnTimer procedure har jeg et dynamic array som indeholder tråd-objekter. Disse tråde kører indtil de er færdige, og så bliver de free'et ved afslutning (FreeOnTerminate=True).

procedure OnTimer
  var A: Array of TThread;
      I, X: Integer;
begin
  SetLength(A, X)
  for I:=0 to X-1 do
    A[I]:= TThraed.Create
end;

Når timer eventen næste gang bliver aktiveret, kommer jeg jo igen ind til den samme procedure, hvor jeg har mit dynamiske array. På dette tidspunkt ved jeg ikke om trådene fra "sidste gang" jeg var her, er færdige eller ej. Men kan jeg nu tillade at initiere arrayet og create trådene igen?
Det jeg er bange for, er hvis jeg havner i samme hukommelses-adresse som sidst, og dermed kommer til at overskrive trådende fra sidste kørsel, som muligvis stadig er igang med at eksekvere.

Et andet problem er: vil arrayet blive free'et når alle tråde er færdige med at køre, eller får jeg en leak ? Når alle tråde er færdige med at køre, så vil arrayet indeholde "nil" på alle pladser. Men burde jeg så ikke sætte arrayet selv til nil? :

A:=nil.

Og hvis ikke: hvordan og hvornår bliver hukommelsen til selve arrayet (ikke de tråd-objekter det indeholder) free'et ? Dette kan  jo ikke forekomme i slutningen at OnTimer proceduren, da denne procedure er afsluttet før trådene er færdige med at køre...

vh
Avatar billede arne_v Ekspert
16. oktober 2007 - 02:21 #1
re 1)

Hvis du bruger New bør du også bruge Dispose for at undgå en memory leak.

Men bemærk at al memory som programmet har brugt bliver frigjordt ved program exit
uanset om man husker at Free og Dispose.
Avatar billede arne_v Ekspert
16. oktober 2007 - 02:22 #2
Da din kode ikke bruger New, så skal du heller ikke kalde Dispose.
Avatar billede hrc Mester
16. oktober 2007 - 08:11 #3
Hvis du har 1000 strengværdier, har du for mange i et dynamisk array. Jeg har eksperimenteret en del med den og det er noget lort. Hvorfor bruger du ikke en TStringList? Den er hurtig, dynamisk og skræddersyet til strenge (som navnet antyder).

Du nulstiller array'en ved SetLength(A,0)

Du kan lade være med at FreeOnTerminate og så løbe listen igennem efter tråde der er Terminated. De kan du pille ud af listen og frigive.

Alternativt har en tråd en OnTerminate-event du kan koble dig på, så når den vælger at dø kan du vha denne fiske det frigivne objekt ud af listen.

Tread.OnTerminate := OnTerminateEvent;

...

procedure TForm1.OnTerminateEvent(Sender: TObject);
var
  index: integer;
begin
  index := fThreadList.IndexOfObject(Sender);
  if Index >= 0 then
    fThreadList.Delete(index);
end;
Avatar billede rozh Nybegynder
16. oktober 2007 - 10:37 #4
Det som det hele egentligt går ud på, er at jeg har X antal klienter som har forbindelse til en server. Serveren skal pinge klineterne med jævne mellemrum. Ping implementeringen er sat ind i en tråd (det skal den, fordi jeg blocking forbindelser).

Jeg vil så lave et array over tråde, som hver især får til ansvar at pinge én klinet. Og disse tråde-objekter (fra arrayen) vil blive created og kørt ind fra en timer, som tidligtere vist:

procedure OnTimer
  var A: Array of TThread;
      I, X: Integer;
begin
  SetLength(A, X)
  for I:=0 to X-1 do
    A[I]:= TThraed.Create
end;

Hvis jeg venter på at alle tråde en "terminated", så halter programmet jo imens (hvis nogle af trådene er langsomme).

Hvis jeg assigner en OnTerminateEvent, så er jeg sådanset ligevidt, fordi hvornår og hvordan vil arraey'et/Threadlist-objektet blive free'et ?

Jeg kunne jo selvfølgelig lave en global TThreadList, men det betyder jo at jeg hvergang skal tjekke for hvilke klinter-pingere der stadig kører, og starte tråde kun for de klinter, hvis ping blev færdig under forrige kørsel. Jeg vil nødigt bruge denne løsning, da det ikke vil være den mest "elegante" løsning :-) Men jeg kan jo selvfølgelig blive nødt til det hvis der opstår en leak, og der ikke er andre muligheder...

Men hele spørgsmålet går ud på om der overhovedet ER en leak. Hvis jeg har FreeOnTerminate til true, så vil jeg ende med et array som indeholder X pladser. Hvert plads er af Typen TThread, men er ligemed nil (når trådene er afsluttede). Så ender jeg med et array af længden X med nil på alle pladser. Skal sådan en free'es eksplicit, eller sørger Delphi for det ? Og hvis Delphi sørger for det, hvornår sker det og hvordan ?

Og er der nogen af jer som ved hvor sådan noget info står ? Jeg kan ihvertfald ikke finde noget i Delphis manual...
Avatar billede rozh Nybegynder
16. oktober 2007 - 10:41 #5
For øvrigt. I hjalp med et spørgsmål tidligere, men har ikke smidt et svar. Gør det, så er der point til alle :-)

http://www.eksperten.dk/spm/792474
Avatar billede hrc Mester
16. oktober 2007 - 11:09 #6
Jeg kan ikke se hvordan du kan undgå at vente på dine tråde afslutter - medmindre du kan sætte FreeOnTerminate = true når du lukker hovedprogrammet. Det må du afprøve.

I din mainforms destructor eller OnClose bør du nulstille den vha SetLength. Som Arne skriver frigives den under alle omstændigheder når programmet afsluttes, men Delphi har nu engang kun meget begrænset "Garbage Collector" funktionalitet (ved interfaces) og så frigiver man altså hvad man har allokeret; det er god praksis.
Avatar billede rozh Nybegynder
16. oktober 2007 - 11:23 #7
Array'et er lokal (dvs ikke global) og jeg kan ikke sættes length til noget fra OnClose i hovedprogrammet.

Når jeg har startet mine tråde, så behøver jeg jo ikke at vente på dem til de afslutter. Det er jo netop derfor jeg kører tråde, og ikke bare kalder en ping proceudre. I princippet så kan jeg nå at komme ind i OnTimer eventen igen og starte nye tråde, før de gamle tråde er færdige med at køre..

Mht til Delphi's garbage collection, så kan jeg ikke være afhængig af den. Mit server program kommer til at køre langt tid ad gangen, og derfor er det vigtigt at undgå leaks
Avatar billede martinlind Nybegynder
16. oktober 2007 - 13:27 #8
Hvis du har nogle tråde som er fre on terminate, kan du create dem og execute dem, du kan derefter create de samme VAR's og execute dem, og det kan du gøre alle de gange du vil, det vil ikke gi' problemer :)

Du vil ikke komme til at overskrive noget, din tråd-create allokere en ny "klump" hukommelse til dit obj., det problem man kan få ved at gøre sådan er at din reference til de tidligere tråd-obj. den er væk, fordi du lige har lagt en ny ref i din var :)

Håber det hjalp på din forståelse
Avatar billede hrc Mester
16. oktober 2007 - 14:58 #9
Som jeg skriver er der kun garbage collector på interfaces og com-objekter. Det bruger du ikke.

Allokering og deallokering kan klares sådan her (men hvorfor bruge dynamiske arrays? Jeg har spurgt om det flere gange før og gør det igen. Der findes TList, TObjectList og sågar TStringList der er bedre):

procedure EtEllerAndet(Sender: TObject);
var
  A: array of TThread;
begin
  SetLength(A,1000);
  try
  finally
    SetLength(A,0);
  end;
end;

Jeg vil påstå at dynamiske arrays, pga. den sære kode der ligger bag, er mere risikable fremfor nævnte TList varianter. Det er ikke det rigtige at bruge hvis man har et alternativ!

------------ o ------------

I øvrigt: Hvis din DynArray er lokal hvad skal du så bruge den til? Så er det lettere at bruge Martins FireAndForget tip, men du bør sikre dig at tråden faktisk afslutter for ellers er der en lækage der da, mener jeg, tråden ikke afsluttes sammen med mainformen, men lever sit eget liv.

------------ o ------------

Hvad vil der ske om du fyrede trådene af oveni hinanden? Ville den så ikke bare pinge samme maskine flere gange i træk? Hvad ville der ske ved det?
Avatar billede rozh Nybegynder
16. oktober 2007 - 15:00 #10
Hej MartinLind

Det er faktisk meget fornuftigt:-) Jeg skal jo ikke bruge referencen til noget.
Tråden creates, sendes afsted og jeg glemmer den derefter.

Hvis jeg har forstået det rigtigt, så har jeg slet ikke brug for et array i det hele taget. Jeg skal bare med den samme variable create alle  mine tråde. Når jeg er færdig skal jeg bare sætte variablen til nil:


var T: Thread
begin 
  for I:=0 to X do
  begin
    T:=TThread.Create(True); //suspenderet
    T.KlientID:= X; //(her sætter jeg det specifikke formål med tråden)
    T.FreeOnTerminate:=True;
    T.Resume;
  end;
T:= nil;
end;


Har jeg forstået det rigtigt ?
Avatar billede rozh Nybegynder
16. oktober 2007 - 15:04 #11
Hej HRC

Jeg vidste ikke man kunne bruge den FireAndForget metode, derfor bøvlede jeg med arrays. Er heller ikke selv vild med dem.

Det er en god pointe med flere tråde der pinger samtidigt. Det har jeg selv overvejet, og sådan som tingene er opbygget, så gør det ikke så meget ( andet end en smule overhead)
Avatar billede rozh Nybegynder
16. oktober 2007 - 15:05 #12
Tusind tak for hjælpen allesammen

I har alle hjulpet delvis, derfor synes jeg at I alle skal smide et svar, så får alle point :-)
Avatar billede martinlind Nybegynder
16. oktober 2007 - 16:01 #13
Jep, helt rigtig forstået, bare lav din kode så du er sikker på at din tråd afslutter, dvs. ordenlig try..finally / except
Avatar billede martinlind Nybegynder
16. oktober 2007 - 16:01 #14
hrc >> TxxList styrer for vildt :) ( bruger dem selv i stor stil )
Avatar billede rozh Nybegynder
17. oktober 2007 - 00:35 #15
Hej arne V

vil du ikke lave et svar på dette spørgsmål og det andet (linket) ?

Jeg synes det er fair at du får nogle af pointene :-)
Avatar billede arne_v Ekspert
17. oktober 2007 - 00:45 #16
svar her
Avatar billede arne_v Ekspert
17. oktober 2007 - 00:46 #17
jeg skal ikke have noget i det andet spm.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester