I'm trying to find the source of a memory leak coming from a thread. The thread triggers synchronized events repetitively, returning a thread-protected object.
I trigger this event within the thread by calling a procedure...
procedure TDeskMonThread.DoOnImage(const ID: Integer; const R: TRect;
ABmp: TLockBmp);
begin
FSyncOnImageID:= ID;
FSyncOnImageRect:= R;
FSyncOnImageBmp:= ABmp;
Synchronize(SYNC_OnImage);
end;
The 3 private fields are only used for this purpose - temporary storage to be used in the event trigger. TLockBmp
is just a wrapper around a TBitmap
with a critical section, requiring Lock and Unlock.
Then, the Synchronize calls this procedure:
procedure TDeskMonThread.SYNC_OnImage;
begin
if Assigned(FOnImage) then //trigger event
FOnImage(FSyncOnImageID, FSyncOnImageRect, FSyncOnImageBmp);
end;
and this event is handled by this procedure:
procedure TfrmMain.ThreadOnImage(const ID: Integer; const R: TRect;
ABmp: TLockBmp);
var
B: TBitmap;
begin
if ID = FCurMon then begin //Only draw if it's the current monitor
B:= ABmp.Lock;
try
FBmp.Assign(B); //Copy bitmap over
finally
ABmp.Unlock; //Hurry and unlock so thread can continue its work
end;
ResizeBitmap(FBmp, pbView.ClientWidth, pbView.ClientHeight, clBlack);
pbView.Canvas.Draw(0, 0, FBmp); //Draw to canvas
end;
end;
Now I've narrowed it down to ResizeBitmap
because when I comment out that line of code, I do not get the memory leak. Here's that procedure:
procedure ResizeBitmap(Bitmap: TBitmap; Width, Height: Integer; Background: TColor);
var
R: TRect;
B: TBitmap;
X, Y: Integer;
begin
if assigned(Bitmap) then begin
B:= TBitmap.Create;
try
if Bitmap.Width > Bitmap.Height then begin
R.Right:= Width;
R.Bottom:= ((Width * Bitmap.Height) div Bitmap.Width);
X:= 0;
Y:= (Height div 2) - (R.Bottom div 2);
end else begin
R.Right:= ((Height * Bitmap.Width) div Bitmap.Height);
R.Bottom:= Height;
X:= (Width div 2) - (R.Right div 2);
Y:= 0;
end;
R.Left:= 0;
R.Top:= 0;
B.PixelFormat:= Bitmap.PixelFormat;
B.Width:= Width;
B.Height:= Height;
B.Canvas.Brush.Color:= Background;
B.Canvas.FillRect(B.Canvas.ClipRect);
B.Canvas.StretchDraw(R, Bitmap);
Bitmap.Width:= Width;
Bitmap.Height:= Height;
Bitmap.Canvas.Brush.Color:= Background;
Bitmap.Canvas.FillRect(Bitmap.Canvas.ClipRect);
Bitmap.Canvas.Draw(X, Y, B);
finally
B.Free;
end;
end;
end;
The memory leak message is what has me confused:
The x 3
varies depending on how long it's been running, but isn't the number of iterations. For example, the thread may repeat 20 iterations and show x 3
or may repeat 10 iterations and show x 7
but I can't even find a pattern of how many leaks compared to how many iterations. It seems this happens at random moments, not on every iteration.
So I went into debugging the ResizeBitmap
procedure, but when I run it by its self, even repeatedly and rapidly, I never get any memory leaks. It seems to be something to do with calling it repeatedly from the thread. I know it's creating/destroying an instance of TBitmap
which may not be the best practice, but still, I only get this memory leak when it's being called repeatedly from the thread. I'm assuming there's a hidden exception (out of resources) which never actually raises an exception - and thus gets trapped as a memory leak.
Where could this memory leak be coming from? How can I prevent it?
Synchronize
to return, which won't happen until the event handler finishes running. – Rob Kennedy