AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

MemoryLeaks mit s := I.ToString

Ein Thema von Kraisel · begonnen am 7. Nov 2014 · letzter Beitrag vom 12. Nov 2014
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von Kraisel
Kraisel

Registriert seit: 19. Mär 2012
Ort: Bochum-Linden
64 Beiträge
 
Delphi 12 Athens
 
#1

MemoryLeaks mit s := I.ToString

  Alt 7. Nov 2014, 18:49
Delphi-Version: XE5
Alles mit XE7 Pro
Delphi-Quellcode:
procedure Test;
var
  s1,s2: String;
  i: Integer;
begin
  i := 123;
  s1 := IntToStr(I); // der Referenzzähler von s1 ist hier 1 :-)
  GetDynVarPrms(s1);
  s2 := I.ToString; // der Referenzzähler von s2 ist hier 2 :-(
  GetDynVarPrms(s2);
end;
Delphi-Quellcode:
function GetDynVarPrms (const v): String; // zum einfachen Anzeigen der DynVarParameter auf dem Heap
var p: PInteger;
begin
  p := Pointer((@v)^);
  Result:= 'VarPtr: $' + IntToHex(Integer(@v), 8) + ', BufPtr: ';
  if p <> NIL then begin
    Result := Result + '$' + IntToHex(Integer(p), 8);
    Dec(p);
    Result := Result + ', Len: ' + IntToStr(Integer(p^));
    Dec(p);
    Result := Result + ', RefCntr: ' + IntToStr(Integer(p^));
  end else Result := Result + 'NIL';
end;
Es hat lange gedauert, bis ich wusste, wieso ich beim Beenden des Programms MemoryLeaks angezeigt bekam.
Alle I.ToString mit IntToStr(I) ausgewechselt und alles war wieder OK.

So wie ich das im Moment sehe, wird der Heap bei jedem Aufruf von I.ToString weiter zugemüllt.

Ein "normales" Programm räumt beim Beenden die ganzen verwaisten Variablen wohl einfach wieder auf, und es werden keine MemoryLeaks angezeigt. Wenn man aber z.B. in Threads mit Windows.Fiber arbeitet, bleiben die MemoryLeaks erhalten, und man sieht sie (mit ReportMemoryLeaksOnShutDown := TRUE).

Hat da jemand eine Idee, wieso dass so ist.
Oder habe ich nur Leaks im Hirn?

Danke
Peter Kaisler
Das einzig Komplizierte ist zu begreifen wie einfach es ist.

Geändert von Kraisel ( 7. Nov 2014 um 19:03 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.071 Beiträge
 
Delphi 12 Athens
 
#2

AW: MemoryLeaks mit s := I.ToString

  Alt 7. Nov 2014, 19:11
Dieses komische Rumgepointere solltest du besser lassen

Delphi-Referenz durchsuchenLength
Delphi-Referenz durchsuchenStringElementSize
Delphi-Referenz durchsuchenStringCodePage
Delphi-Referenz durchsuchenStringRefCount

Ansonsten ist es ganz nett (so hatte ich es vor Einführung dieser Funktionen gemacht), wenn man sich einen Record erstellt (kannst dir bei System.StrRec abgucken) und darüber geht.
P := PStringRec(Pointer(S) - SizeOf(PStringRec)); -> P.RefCount


Da gibt es wohl eine Unpässligkeit beim Erzeugen des InlineCodes für TIntegerHelper.ToString.

Code:
Unit2.pas.31: s1 := IntToStr(I); // der Referenzzähler von s1 ist hier 1 :-)
005DB646 8D55F8           lea edx,[ebp-$08] // out s1
005DB649 8B45F0           mov eax,[ebp-$10] // in i
005DB64C E85B61E4FF      call IntToStr

Unit2.pas.33: s2 := I.ToString; // der Referenzzähler von s2 ist hier 2 :-(
005DB67C 8D55E8           lea edx,[ebp-$18] // out intern_s3 (intern generierte StringVariable)
005DB67F 8B45F0           mov eax,[ebp-$10] // in i
005DB682 E82561E4FF      call IntToStr
005DB687 8D45F4           lea eax,[ebp-$0c] // out s2
005DB68A 8B55E8           mov edx,[ebp-$18] // in intern_s3
005DB68D E886E0E2FF      call @UStrLAsg
Aber nein, das ergibt kein Speicherleck, denn am Ende der Prozedur (im end; ) werden alle 3 String-Variablen freigegeben.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu ( 7. Nov 2014 um 19:19 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Kraisel
Kraisel

Registriert seit: 19. Mär 2012
Ort: Bochum-Linden
64 Beiträge
 
Delphi 12 Athens
 
#3

AW: MemoryLeaks mit s := I.ToString

  Alt 7. Nov 2014, 21:33
Meine Pointermystik war nur für diese Tests ein Ersatz, um nicht ständig in der CPU-Ansicht die Speicherbereiche einzutippen! Ich wollte sicher sein, dass wirklich der Speicher so aussieht. Deshalb habe ich keine Methoden aus dem System genommen. Wird ansonsten nicht genutzt.

Des Rätsels Lösung ist dann wohl, dass in einer Fibermethode das "end" ja nie erreicht wird, und somit die Aufräumarbeiten nicht erledigt werden. Das Aufräumen lokaler dynamischer Variablen wird vor der Fiberumschaltung per Finalize erledigt. Und hier gibt es dann wohl Probleme mit der zusätzlichen String - Referenz. Aber OK, im Moment klingt zumindest alles wieder logisch.

Danke.
Peter Kaisler
Das einzig Komplizierte ist zu begreifen wie einfach es ist.
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#4

AW: MemoryLeaks mit s := I.ToString

  Alt 7. Nov 2014, 23:47
Fibers und Windows? Soweit ich mich erinnere hat Microsoft die nur sehr rudimentär implementiert als Gegenstück zu den Fibers unter Unix. Und es wird empfohlen unter Windows stattdessen Threads zu nutzen. oder hat sich da jetzt was geändert?
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Benutzerbild von Kraisel
Kraisel

Registriert seit: 19. Mär 2012
Ort: Bochum-Linden
64 Beiträge
 
Delphi 12 Athens
 
#5

AW: MemoryLeaks mit s := I.ToString

  Alt 8. Nov 2014, 01:43
Threads sind ein präemptives Multitasking. Fiber ist ein kooperatives Multitasking und läuft in EINEM Thread. Die Umschaltung der Fiber kostet keine Verwaltung (wenige ns) und die einzelnen Fiber müssen auch nicht gegeneinander verriegelt werden. Der gesamte Betriebssystem Overhead ist dadurch weg. Das hat für viele technische Probleme enorme Vorteile.

Ich automatisiere z.B. riesige Prüfstände für Verdichter bis 500kW mit vielen Aggregaten, hunderten von analogen und digitalen IOs, die alle ihre eigene Logik haben, aber trotzdem auf eine Hardware (meist Interbus und viele Schaltschränke) zugreifen. Mit Threads würde das System nur noch sich selber verwalten, und die gegenseitige Verriegelung und gleichzeitige Synchronisierung würde einem Stehhaare bescheren. Mit Fiber oder ähnlichen Techniken kann man die einzelnen Aggregate sehr komfortabel unabhängig voneinander programmieren, und alles ist wahnsinnig schnell. Und es gibt auch keine Deadlocks.

Echte Parallelität (Threads) machen nur dann Sinn, wenn durch den Einsatz mehrerer CPU-Core ein deutlicher Performancegewinn erzielt werden kann. Wenn es aber einen Flaschenhals gibt, (bei mir z.B. die Synchronisation mit der Hardware alle paar ms) ist eine kooperative Parallelität auf einem Core ohne Overhead wesentlich schneller.

Viele nehmen dazu eine SoftSPS, aber diese Art der Programmierung empfinde ich als komplett veraltet und auch sehr umständlich. Das alte Step5 oder auch die S7 habe ich schon vor über 20 Jahren beisete gelegt, und bin froh, mit Pascal sauberen und lesbaren Code für angewandte Prozessautomation zu bauen.
Peter Kaisler
Das einzig Komplizierte ist zu begreifen wie einfach es ist.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.071 Beiträge
 
Delphi 12 Athens
 
#6

AW: MemoryLeaks mit s := I.ToString

  Alt 8. Nov 2014, 11:35
Mehrere in Fibern ausgeführte Prozeduren müssten doch langsamer sein, als einfach mehrere Prozeduren in einem Thread nacheinander aufzurufen?

Der einzige Vorteil, den ich da seh, daß man den Fiber einfach mal so mittendrin anhalten könnte, aber einen Thread kann man ja genauso gut anhalten. (auch wenn man das eigentlich nicht machen sollte, was dabei allerdings auch für die Fiber gilt)

Nachteil, man zerschießt sich die automatische Speicherverwaltung.
Das fängt im Delphi schon bei den Strings, dyn. Arrays, Interfaces und Variants an, und geht im NextGen bei allen Objekten weiter. (hatte hier gestern gelesen, daß man versucht Fiber auch in anderen Platformen haben will)



Die winzige Lösung, welche ich sehe, daß sich Fiber nicht einfach so selber beenden dürfen.
Man könnte die aufgerufenen Prozeduren in eine leere Dummy-Procedure legen, wo nur der Aufruf der Funktion drin ist und sonst nichts, was irgendwie Speicher oder die Exceptionbehandlung beinhalten sollte (außer eventuell genau an Fiber angepasste Funktionen). Also genauso wie beim TThread.Execute.



PS: Sobald auch nur eine lokale String-Variable in einer Funktion deklariert wurde, egal ob manuell oder durch soeine interne Variable, wird auch noch ein Try-Finally dort eingebaut.

Dieser Code
Delphi-Quellcode:
procedure Test;
var
  s1,s2: String;
  i: Integer;
begin
  i := 123;
  s1 := IntToStr(I);
  GetDynVarPrms(s1);
  s2 := I.ToString;
  GetDynVarPrms(s2);
end;
wird, so in etwa, in Folgendes übersetzt (grob in Pure-Pascal übersetzt)
Delphi-Quellcode:
procedure Test;
var
  intern : record
    s1, s2, intern_s3: String;
    i: Integer;
  end;
begin
  {$REGION 'begin'}
  PushVariablesToStack(intern); // inkl. Initialize(intern); , indem die String-Variablen mit 0 beschrieben werden
  try
    with intern do begin
  {$ENDREGION}
      i := 123;
      IntToStr(I, s1); // procedure IntToStr(i: Integer; var Result: string);
      GetDynVarPrms(s1);
      TIntegerHelper.ToString(i, intern_s3); //intern_s3 := I.ToString; // procedure TIntegerHelper.ToString(Value: Integer; var Result: string); inline;
      UStrLAsg(s2, intern_s3); //s2 := intern_s3;
      GetDynVarPrms(s2);
      // und hier hat dein Fiber alles weggeknallt
  {$REGION 'end'}
    end;
  finally
    Finalize(intern);
  end;
  PopVariablesFromStack(intern);
  {$ENDREGION}
end;
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu ( 8. Nov 2014 um 11:54 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Kraisel
Kraisel

Registriert seit: 19. Mär 2012
Ort: Bochum-Linden
64 Beiträge
 
Delphi 12 Athens
 
#7

AW: MemoryLeaks mit s := I.ToString

  Alt 8. Nov 2014, 19:57
VORSICHT: Wenn die Fiberumschaltung irgendwo in einer aufgerufenen Methode vor dem finally geschieht, und der Fiber dann beendet wird, wird finally niemals aufgerufen. Dieses Problem hat man auch beim Thread, wenn er mit Suspend angehalten und dann verworfen wird.

Allgemein: Die erste Entscheidung bei paralleler Programmierung ist, ob man dadurch einen Performancegewinn oder deutlich besser trennbaren Code haben will. Beides ist leider nur selten möglich.

Ob Threads, Fiber oder einfach parallele Abläufe durch geschicktes orthogonales Coding muss sorgfältig abgewägt werden.

Threads machen z.B. Sinn, wenn durch den Einsatz mehrere CPUs echte Parallelität gewährleistet wird, z.B beim Suchen eines Elementes in einer unsortierten riesigen Liste. Einfach nur mehrere Threads aufmachen reicht aber auch da nicht. Das Betriebssystem könnte nämlich mehrere oder sogar alle Threads in einer CPU laufen lassen. Dann wirds sogar deutlich langsamer als eine Iteration über alle Elemente in einem Thread. Die Umschaltung eines Threads ist nicht deterministisch und kostet inkl. der internen völlig intransparenten Optimierung des Betriebssystems 1..100µs. Bei Threads vergisst man gerne, dass das Betriebssystem jeden Thread nicht nur umschaltet, sondern dass die CPU-Ressourcen für die verdeckte interne Optimierung mit steigender Anzahl überproportional ansteigt. Und selbst ein Assemblerbefehl wie ein normales Increment einer Speicherstelle MUSS synchronisiert werden, wenn auch andere Threads darauf zugreifen können. Es gibt aber vier intrinsische Atomic-Assemblerbefehle, die auch in Delphi zur Verfügung stehen.

Fazit: Mehr Threads als CPUs machen NUR für besser trennbaren Code Sinn. Die Performance wird nämlich je zusätzlichem Thread (mehr als CPUs) zwangsläufig immer schlechter. Das ist leider, bis auf ganz wenig Ausnahmen, allgemeingültig. Wenn möglich, ist klassisches Pollen eben fast immer wesentlich schneller als komplexe Strukturen.

Und genau da werden Fiber spannend. Die machen nämlich Sinn, wenn die Laufzeit des Codes bis zur nächsten notwendigen Umschaltung kurz ist, und man davon viele (gerne auch mehrere 1.000) hat. Die Umschaltung eines Fiber ist mit unter 50ns ca. 20..2.000 mal schneller als beim Thread. Wenn man also viele Objekte oder Methoden hat, die alle parallel etwas tun müssen, und man auch keine Verriegelung untereinander im Code haben darf oder nicht möchte, dann machen Fiber absolut Sinn. Threads wären da viel zu langsam, viel zu aufwändig und auch viel zu fehleranfällig.

Eigenes geschicktes Coding ist die schnellste Variante, birgt aber den Nachteil, diese Tools, die man dafür benötigt, immer zu pflegen, und an die neuen Compiler anzupassen. Und da kein Dispatcher (Stackumschaltung) vorhanden ist, sieht der Code zwangsweise an manchen Stellen einfach etwas unübersichtlich aus. Diese Variante habe ich erfolgreich seit über 10 Jahren weltweit in vielen Anlagen (365x24) im Einsatz. Da diese Projekte aber immer komplexer werden, möchte ich nun besser lesbaren und wartbaren Code in den parallelen Prozessen haben. Das sind im Moment meine Entscheidungsprobleme.
Peter Kaisler
Das einzig Komplizierte ist zu begreifen wie einfach es ist.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.071 Beiträge
 
Delphi 12 Athens
 
#8

AW: MemoryLeaks mit s := I.ToString

  Alt 8. Nov 2014, 21:59
Dieses Problem hat man auch beim Thread, wenn er mit Suspend angehalten und dann verworfen wird.
Dieses Problem gibt es nicht, da man Threads niemals von außen anhält.
Man sagt einenm Thread er solle sich bitte anhalten und er macht das dan selber, an definierter/sicherer Stelle.

Das Problem liegt nicht nur beim Beenden, sondern auch beim ausführenden Code.
Beispiel: Du hälst den an, während er grade Speicher reserviert/freigibt, oder auf irgendeine Resource zugreift ... wenn der Thread dabei etwas lockt, dann wird das nie wieder entsperrt und kann womöglich auch andere Dinge blockieren, wie z.B. alles was im selben Speicherblock liegt und das dann nie wieder freigegeben werden kann.
Schlimmer noch, wenn du versuchst das freizugeben, landet man entweder in einem Deadlock oder es gibt zumindestens eine Exception (falls der Zugriff über ein Timeout verfügt.


Genauso das beenden eines Threads ... sowas hat der Thread gefälligst selber zu machen.
Alles andere sind maximal notfallmaßnahmen, bei welchen man damit rechnen muß, daß irgendwas kaputt geht.

Das ist genauso, also wenn man zum PC-Runterfahren den Stecker zieht.


Nicht umsonst gibt der Compiler nun endlich eine Warnmeldung aus, wenn man Suspend verwendet.
Delphi-Quellcode:
// This function is not intended to be used for thread synchronization.
procedure Suspend; deprecated;
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu ( 8. Nov 2014 um 22:08 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Kraisel
Kraisel

Registriert seit: 19. Mär 2012
Ort: Bochum-Linden
64 Beiträge
 
Delphi 12 Athens
 
#9

AW: MemoryLeaks mit s := I.ToString

  Alt 8. Nov 2014, 22:58
Für den Thread hast Du völlig recht. Da habe ich mich vielleicht nicht klar genug ausgedrückt, denn ich wollte sicher nicht anregen, dass man Suspend benutzen soll. Ich habe lediglich versucht, die Analogie zum Fiber hergestellt, denn da besteht leider immer das Problem, dass das Ende der Methoden in denen umgeschaltet wird niemals aufgerufen werden, wenn der Fiber gerade beendet wird. Er kann sich leider auch nicht selber beenden. Wenn er das tut, indem er einfach die Methode verlässt, wird der Thread, indem er läuft, beendet, und alle anderen Fiber stehen unkontrolliert in ihrer letzten Umschaltung. Und das ist wohl der großer Nachteil bei Fiber. Man muss alle Spielregeln beachten und genau wissen, was man da darf und was nicht. Wenn man das ignoriert, müllt man sich langfristig den Speicher zu. Ansonsten sind Fiber aber eben wesentlich schneller als Threads. Man kann leider nicht alles haben.

Vielen Dank für alle Hinweise.
Peter Kaisler
Das einzig Komplizierte ist zu begreifen wie einfach es ist.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.071 Beiträge
 
Delphi 12 Athens
 
#10

AW: MemoryLeaks mit s := I.ToString

  Alt 9. Nov 2014, 11:19
OK, der Thread des Fiber darf nicht vom Fiber beendet werden, aber auch der Fiber muß sich selber beenden, indem er selber am Ende eine bestimmte Funktion aufruft.
Von außen darf auch ein Fiber nicht einfach so unkontrolliert angehalten/beendet werden, damit er nicht in einem "ungünstigen" Zustand stehen bleibt.

Egal ob Thread oder Fiber, sobald etwas globales verwendet wird, muß man aufpassen, daß dieses im Moment des Anhaltens nicht benutzt wird,
außer es gibt atomare Locking-/Zugriffsfunktionen und man könnte die Locks von außen freigeben.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu ( 9. Nov 2014 um 11:23 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:16 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz