Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Record New/Dispose resultiert in MemoryLeaks (https://www.delphipraxis.net/194310-record-new-dispose-resultiert-memoryleaks.html)

Glados 7. Nov 2017 21:34

Record New/Dispose resultiert in MemoryLeaks
 
Ich glaube ich habe irgendwo einen Fehler, finde ihn aber nicht :shock:

Ich rufe eine Testprozedur auf
Delphi-Quellcode:
 for i := 0 to 50000 do
  TTestUnit.Test(TTest.tdmPostMessage, Form1.Handle, WM_THREAD_MSG, 0, 0, 'TEST STR');
Delphi-Quellcode:
class procedure TTestUnit.Test(Method: TTest; hWnd: Winapi.Windows.hWnd; Msg: UINT; wParam: Winapi.Windows.wParam; lParam: Winapi.Windows.lParam; aString: PChar);
var
 msgData: PTestData;
begin
 New(msgData);

 msgData.Msg := Msg;
 msgData.wParam := wParam;
 msgData.lParam := lParam;
 msgData.aString := aString;

 case Method of
  tdmSendMessage:
   SendMessage(hWnd, msgData.Msg, 0, Winapi.Windows.lParam(msgData));
  tdmPostMessage:
   PostMessage(hWnd, msgData.Msg, 0, Winapi.Windows.lParam(msgData));
  tdmQueue:
   ;
 end;
end;

{*
 TTestData = record
  Msg: UINT;
  wParam: Winapi.Windows.wParam;
  lParam: Winapi.Windows.lParam;
  aString: PChar;
 end;

 PTestData = ^TTestData ;
*}
Delphi-Quellcode:
type
 TForm1...

 public
  procedure TTestMessage(var msg: TMessage); message WM_THREAD_MSG; // WM_THREAD_MSG ist WM_USER + 991;

implementation

procedure TForm1.TTestMessage(var msg: TMessage);
var
 msgData: PTestData;
begin
 msgData := PTestData(msg.lParam);
 try
  Caption := PTestData(msg.lParam).aString;
 finally
  Dispose(msgData);
 end;
end;
Beim Ausschalten des Programms erhalte ich exakt 40001 MemoryLeaks.

Der schöne Günther 7. Nov 2017 21:55

AW: Record New/Dispose resultiert in MemoryLeaks
 
// Edit 2:
Hah, jetzt habe ich glaube ich die Ursache: 50.000 mal PostMessage ist zu viel für die Windows Message queue (ist ja auch völlig praxisfern):

Zitat:

There is a limit of 10,000 posted messages per message queue.
Quelle: MSDN zu PostMessasge


// Edit: Nö, ich lag falsch.
Siehe: https://stackoverflow.com/q/4285892/2298252

Ab hier bitte nicht weiterlesen -.-



Da ich in meiner jahrelangen, glänzenden Karriere als Delphi-Entwickler noch kein einziges mal New oder Dispose verwendet habe bin ich mir nicht sicher, aber:

Dispose gibt einfach den Speicher für den Record frei. Dein Record scheint aber mindestens einen gemanagten Typen (String namens "aString") zu enthalten, der wird denke ich nicht vernünftig abgeräumt. Einfach nur der Platz für den Zeiger im Record wird freigegeben, der auf dem Heap angelegte Speicher für den String verbleibt auf ewig als Leiche dort.

Ich habe keine Ahnung wie man es richtig macht, spontan hätte ich gedacht statt Dispose einfach Finalize verwenden. Oder nicht stattdessen, sondern davor? Keine Ahnung. Aber zumindest bei der Ursache könnte ich mir vorstellen richtig zu liegen ;-)

Uwe Raabe 7. Nov 2017 22:00

AW: Record New/Dispose resultiert in MemoryLeaks
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1385542)
Dein Record scheint aber mindestens einen gemanagten Typen (String namens "aString") zu enthalten, der wird denke ich nicht vernünftig abgeräumt. Einfach nur der Platz für den Zeiger im Record wird freigegeben, der auf dem Heap angelegte Speicher für den String verbleibt auf ewig als Leiche dort.

Das ist kein String sondern ein PChar und da wird in der Schleife eine Stringkonstante übergeben.

Allerdings wäre die Offenlegung der Deklaration von PTestData sicher nicht schädlich.

Glados 7. Nov 2017 22:00

AW: Record New/Dispose resultiert in MemoryLeaks
 
Entfernen des PChars aus dem Record schafft keine Abhilfe. Finalize vor/statt Dispose aufzurufen leider auch nicht.

Vielleicht hilft es ja. Die Memory Leaks treten nur mit PostMessage auf. Mit SendMessage gibt es keine.

Zitat:

Allerdings wäre die Offenlegung der Deklaration von PTestData sicher nicht schädlich.
Steht doch oben im ersten Beitrag als Kommentar.

Uwe Raabe 7. Nov 2017 22:04

AW: Record New/Dispose resultiert in MemoryLeaks
 
Zitat:

Zitat von Glados (Beitrag 1385544)
Steht doch oben im ersten Beitrag als Kommentar.

Sorry, hatte ich überlesen.

Hast du denn mal gezählt, ob wirklich genauso viele Dispose wie New aufgerufen werden? Da PostMessage die Message ja nur in die Message-Queue stellt, könnte es ja sein, daß das Programm beendet wird, bevor die alle abgearbeitet wurden.

Der schöne Günther 7. Nov 2017 22:06

AW: Record New/Dispose resultiert in MemoryLeaks
 
Ich habe die Deklaration von TTestData auch erst überlesen ;-)

Ich habe meinen Beitrag in der Zwischenzeit noch einmal editiert. Die Windows Message Queue ist 10.000 Einträge groß, da passen natürlich die anderen 40.000 nicht mehr rein.

Glados 7. Nov 2017 22:07

AW: Record New/Dispose resultiert in MemoryLeaks
 
Habe das gerade schnell mit 2 böse, globalen Variablen und einem TTimer geteset.
Es gibt 50000 New() und 10000 Dispose().

Der schöne Günther 7. Nov 2017 22:15

AW: Record New/Dispose resultiert in MemoryLeaks
 
Das sollte mittlerweile niemanden mehr überraschen.

p80286 7. Nov 2017 22:45

AW: Record New/Dispose resultiert in MemoryLeaks
 
Mir kommt das ganze Konstrukt etwas seltsam vor. Da wird ein Pointer lokal definiert, mit seinen Daten wird ein postmessage losgeschickt und dann ist die procedure zu ende
und der Pointer und seine Daten hängen im undefinierten Raum???
Delphi-Quellcode:
begin
  new(p);
  sendmessage(p...);
  dispose(p);
Kann ich ja noch nachvollziehen, aber postmessage halte ich für Harakiri.

Oder bin ich da auf dem falschen Gleis?

Gruß
K-H

Glados 7. Nov 2017 22:47

AW: Record New/Dispose resultiert in MemoryLeaks
 
Ich verwende absichtlich PostMessage, da ich nicht auf die Antwort warten möchte wie es bei SendMessage wäre.

Einziger Zweck dieses Hin- und Hers mit dem Rercord ist es, dass ich mehr und verschiedene Daten per PostMessage senden können möchte.

p80286 7. Nov 2017 22:51

AW: Record New/Dispose resultiert in MemoryLeaks
 
Eben drum habe ich da Bauchschmerzen!

Gruß
K-H

Glados 7. Nov 2017 22:57

AW: Record New/Dispose resultiert in MemoryLeaks
 
Ich muss in manchen Fällen 3 verschiedene Dateninformationen schicken.

2 davon sind für die GUI, die dritte für ein case-of. Das funktioniert aber leider nur mit dem WndProc() MessageHandler.
Mit einem eigenen wäre mir das lieber, dadurch verliere ich aber von den 3 Plätzen schonmal einen, um den MessageHandler anzugeben.

Daher die Record-Geschichte.

Uwe Raabe 7. Nov 2017 23:14

AW: Record New/Dispose resultiert in MemoryLeaks
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1385546)
Die Windows Message Queue ist 10.000 Einträge groß, da passen natürlich die anderen 40.000 nicht mehr rein.

In dem Fall könnte es helfen, den Rückgabewert von PostMessage zu prüfen und im Fehlerfall den Record selbst wieder freizugeben.

Dalai 7. Nov 2017 23:17

AW: Record New/Dispose resultiert in MemoryLeaks
 
Zitat:

Zitat von p80286 (Beitrag 1385551)
Oder bin ich da auf dem falschen Gleis?

Ich hatte kürzlich mit dem Versenden und Empfangen von Nachrichten zwischen zwei Formularen zu tun, und habe dazu Folgendes gelesen: Der Sender der Nachricht muss sicherstellen, dass der Empfänger mit der Nachricht auch etwas anfangen kann. Das heißt, sofern da ein Zeiger auf irgendeine Datenstruktur versendet wird, muss dieser bis zum Ende der Verarbeitung der Nachricht gültig sein. Ich weiß nicht, wie das bei einem solchen Konstrukt funktionieren soll, ob das Ziel des Zeigers nach dem Verlassen der Methode noch sinnvolle Daten enthält. Insofern teile ich deine Bedenken.

@Glados:
Von wo nach wo werden denn die Nachrichten versendet? Von einem Formular zum anderen? IIRC ist es doch so, dass solche Nachrichten gar nicht in der Message Queue von Windows landen sondern direkt vom Formular verarbeitet werden. Andererseits passt die Anzahl der Lecks exakt zu dem Limit der Message Queue von 10k ...

Grüße
Dalai

Glados 7. Nov 2017 23:21

AW: Record New/Dispose resultiert in MemoryLeaks
 
Das mit dem 10.000-Limit wusste ich nicht.
Dann wäre es glaube ich das Beste, wenn ich das mit dem Record sein lasse und Strings stattdessen so mitschicke
Delphi-Quellcode:
PostMessage(receiver, command, wParam, LPARAM(PChar('string')));
Ich schicke aus einem Thread heraus.

Habe meinen Branch vorsichtshalber mal hard-resetet. Ich denke eine andere Lösung ohne records ist doch wesentlich besser.

Eine eigene MessageQueue ohne das 10.000-Limit ggf. Aber das ist sicher nicht möglich.

Zacherl 7. Nov 2017 23:44

AW: Record New/Dispose resultiert in MemoryLeaks
 
Zitat:

Zitat von Glados (Beitrag 1385558)
Das mit dem 10.000-Limit wusste ich nicht.
Dann wäre es glaube ich das Beste, wenn ich das mit dem Record sein lasse und Strings stattdessen so mitschicke
Delphi-Quellcode:
PostMessage(receiver, command, wParam, LPARAM(PChar('string')));

Das klappt auch ausschließlich mit String-Konstanten.

Im Falle von:
Delphi-Quellcode:
var
  S: String;
begin
  S := 'blabla';
  PostMessage(receiver, command, wParam, LPARAM(PChar(@S[1])));
  // ...
  S := '123';
kannst du nie sicher sein, ob
Delphi-Quellcode:
S
noch
Delphi-Quellcode:
'blabla'
oder schon
Delphi-Quellcode:
'123'
enthält, wenn deine Nachricht bearbeitet wird. Selbes gillt natürlich für den Falle, dass der Thread den String in jeder Iteration modifiziert.

Ich kann dir nur nochmal raten von dem ganzen Message Gefummel zurück zu
Delphi-Quellcode:
TThread.Queue
zu wechseln. Da musst du dir weder um manuelle Speicherverwaltung, noch um irgendwelche Message-Queue Limits oder andere Edgecases Gedanken machen.

Glados 7. Nov 2017 23:55

AW: Record New/Dispose resultiert in MemoryLeaks
 
Zitat:

Ich kann dir nur nochmal raten von dem ganzen Message Gefummel zurück zu TThread.Queue zu wechseln. Da musst du dir weder um manuelle Speicherverwaltung, noch um irgendwelche Message-Queue Limits oder andere Edgecases Gedanken machen.
Ich versuche meine vorhandenen Codefragmente mal in Prozeduren zu packen und das so zu machen.

Jedoch würde ich aus den Threads eine Prozedur in einer anderen Unit aufrufen (mit Übergabe der Thread-ID), um einige unnötige Zeilen (
Delphi-Quellcode:
Queue(nil{* <= kommt hier dann die Thread-ID hin? *}, procedure begin end);
) zu sparen.

Ich melde mich sobald ich alles umgebaut habe.

Zacherl 8. Nov 2017 00:04

AW: Record New/Dispose resultiert in MemoryLeaks
 
Statt der ThreadId, musst du die Instanz des aufrufenden Threads also
Delphi-Quellcode:
Self
als Parameter vom Typ
Delphi-Quellcode:
TThread
übergeben, aber dann sollte es ohne Probleme funktionieren die Funktion auszulagern :)

Glados 8. Nov 2017 00:10

AW: Record New/Dispose resultiert in MemoryLeaks
 
Ok danke ich melde mich :thumb:


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:31 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz