SRL Archive - Research & Development Lounge

Bitmap memory leak suggestion


mixster

2009-05-14 22:43:36

I've been working with bitmaps a lot recently and debugging memory leaks became quite a big thing to do, so I decided to make a self debugging method of catching these annoying misses.

function MBitmap(w, h: Integer; str, from: string): Integer; // Creation half of the memory leak debugging
begin
Result := BitmapFromString(w, h, str); // Start by actually making the bitmap since we need the reference number
while (Length(bitDeb) < Result + 1) do // While the string is too short
bitDeb := bitDeb + '2'; // Increase the length of the string by adding a 2 which signifies bitmap created by alternate means
bitDeb[Result + 1] := '1'; // Set the index to 1 to signify in use - string's start at 1 not 0, hence the + 1
SetLength(bitLoc, Length(bitDeb) + 1); // This is 1 longer than the string due to it starting on 0 and me wanting them in line
bitLoc[Result + 1] := from; // Set where the bitmap was created from
end;
^ is for handling the creation of new bitmaps - the first three variables related directly to BitmapFromString with the last just being the procedure where it is created, though it really can hold any identifier to show where it is created in order to find it.


procedure MFreeBitmap(b: Integer); // Destroying half of the memory leak debugging
begin
FreeBitmap(b); // Start by freeing it as the reference remains afterwards
b := b + 1 // Increment it to line it up with the arrays
if (b > Length(bitDeb)) then // If the debug string is too short
exit; // Then we don't need to change anything (created by LoadBitmap or something similar) so exit procedure
bitDeb[b] := '0'; // Set to 0 to signify not in use
bitLoc[b] := ''; // Set to blank
end;
^ This handles the freeing of the bitmap.
There are two global variables used to keep track of the information, this being


bitDeb: string; // Holds the string that has the info about all the bitmaps currently in use
bitLoc: TStringArray; // Holds the location from where the relating bitmap was created
It could be put all into one global variable, but I prefer the ease of use of using a string to hold the status of the bitmap as it's easier to access, though must be careful with the index in the string relating to index - 1 in the array.


while True do
begin
i := Pos('1', bitDeb);
if (i = 0) then
Break;
Writeln('Bitmap created from ''' + bitLoc[i] + ''' not freed!');
MFreeBitmap(i - 1);
end;
^ This is then used at the end of the script when everything should be freed already and the like to see which ones weren't.

It doesn't seem like much, but it can help a lot to find memory leaks and also shows ones that you may have missed. Also, it can be modified to support DTM creating and freeing as well as other forms of bitmap loading.

Lastly, I've used it a lot and it hasn't shown any problems. It handles it fine when bitmaps are created aside from using it and all that stuff, so I don't see any problem it can run into. The only possible one will be when you free the bitmap without using the MFreeBitmap procedure as it will assume it's still in use then try to free it at the end, though can be fixed by surrounding in a try..except..end (I prefer to leave it in so it notifies me that I freed it elsewhere first) or removing the freeing at the end.

~Enjoy

ShowerThoughts

2009-05-14 23:40:37

What about:

Var
DTM:integer;



procedure LDTM(WDTM:string);
begin
case lowercase(WDTM) of
'rune essence': DTM := DTMFromstring('78DA63DCC4C8C0B01E888100423230C447443' +
'08840F9FF8180710990B50E260B03A87A189702595B09A80199B1' +
'97801A90FC0E026A0E10760F00B9C20D99');
'air tiara': DTM := DTMFromstring('78DA63B461666098C0C80002109281E1EFDFB' +
'F0C2250FE7F20607447A84100543D8C4E40355308A8B107AA994A' +
'408D39116A0C816A26E257030093FB0C0D');

'air tally': DTM := DTMFromstring('78DA630C636460886400034608C5B06FEB560' +
'61128FF3F10307A0259290C688011450FA31F909541400DC8AE74' +
'026AE281AC18026A7C106EC6A50600987C0A80');
'air rune':DTM := DTMFromstring('78DA637463626078CA00068C108AE1C983070' +
'C2250FE7F20603401AA79CC80061851F4309A02D5BC22A0C602A8' +
'E63D0135AE40359F08A8F103AA794D849AFBF8D5000053A70EFD');

'air altar center':DTM := DTMFromString('78DA63AC636260F0604001AD798A0CFF81342' +
'310FF0702C610A01A6F881C23544D4BB6378324129F3102A8C680' +
'809A02A01A69026A2603D5C81050B308A8C696809AA54035C904D' +
'42402D528E057030009E6150B');

'walk portal': DTM := DTMFromstring('78DA632C606260486700034608C5702D889FE' +
'13F94FF1F0818BD806A8251D554A439334822F11983816AAC09A8' +
'2903AA5120A0660E508D3601354B816A3C08A8590B54934940CD7' +
'4A09A0A026A62816A3AF1AB0100536E1787');

//'':
else
Debug('DTM doesn'+#39+'t exist.');
end;
end;

function FDTM(WDTM:string; var AA, BB :integer; X1, Y1, X2, Y2:integer):boolean;
begin
LDTM(WDTM);
Result := FindDTM(DTM, AA, BB, X1, Y1, X2, Y2);
if Result then Debug(WDTM+' Found! at: '+inttostr(AA)+','+inttostr(BB)) else Debug(WDTM+' Not Found!');
FreeDTM(DTM);
end;
used as:

Result := FDTM(DTM, AA, BB, MIX1, MIY1, MIX2, MIY2);
if Result then

Not trying to haijck your thread they probably won't like it.

mixster

2009-05-15 00:32:48

That doesn't debug memory leaks at all though, thus not even slightly relating to the topic at hand. Adapting my system for DTM's would pretty much just be the case of adding an identical set of global variables and functions, only instead of using bitmap related creation and destruction, it would use DTM based stuff.

Smartzkid

2009-05-15 06:00:53

I like the idea of relating each bitmap to a 'place' so they can be found easier. Definitely stealing it for my system :)

Lets see… suggestions. Just to make sure everything gets freed, add the 'final cleanup' code inside a procedure named ScriptTerminate.

If createbitmap and freebitmap were added as 'plugin accessible functions' in SCAR, this could be made completely transparent to the scripter. Now wouldn't that be convenient..

bullzeye95

2009-05-15 07:17:34

SCAR automatically frees any loaded bitmaps when the script ends.

mrpickle

2009-05-15 07:20:12

Sometimes it doesn't. When I was running forefeathers, it kept running out of memory every 6ish hours…

bullzeye95

2009-05-15 07:25:50

I said it frees them when the script ends, in a response to smartz's post.

mrpickle

2009-05-15 07:30:26

Oh, sry , i'm getting sleepy >.<.

mixster

2009-05-15 19:04:39

bullzeye95: You're running a script and you forget to free a bitmap in a commonly used procedure. Over time, this means that you end up having hundreds of bitmaps in storage that are never used, which eventually causes Scar to crash with an out of memory or like error.
My method means that upon termination, you can see if you left any in storage, hence allowing you to fix it. If it's a small script or only involved global bitmaps, then it is unneeded, but for larger scripts, with bitmaps used a lot, I think it would help to find many leaks. Removing leeks is great, because I dislike leeks - they taste weird :(

Smartzkid

2009-05-16 05:21:14

Ahh, I see. Well then enclose the main loop in a try..except, and in the except, have it check for a memory out of range error, and if there is one, free everything, and re enter the loop.