Confusion of memory management with Locals
BlitzMax Forums/BlitzMax Programming/Confusion of memory management with Locals
| ||
I have been tracking down a bug in my code for a couple of days and finally realized that it seems the memory management doesn't pay any attention to the existence of Local references to information. For example, I'd created a Local TPixmap variable in a main piece of code. Then I called a Method which loaded an image into a TPixmap and stored the handle to the pixmap in a Field variable as part of the type. Since that Field variable is permanent, it kept the reference to the pixmap. If, within the method, I then make that Field=Null, and do a flushmem, then my Local reference to that pixmap outside of the method is now also Null, even if I was wanting to still use it. The whole problem was then fixed by changing the local into a Global, because then it seems like the memory manager `sees` that there is a second reference to the object and doesn't destroy it with flushmem. Things then behaved as I expected them to. So, not saying this is a bug, just something to bear in mind, a lesson learned. If you want to keep your object references, you probably have to put them in non-Local variables, or at least know that doing a flushmem will also eliminate any local references if those are the only ones left. Does this sound about accurate? Seems to be the case. |
| ||
This code seems to disprove your theory:Type blah Field img:Tpixmap Method killImage() img = Null End Method End Type Graphics 640,480 Local img:Tpixmap = LoadPixmap("fltkwindow.png") b:blah = New blah b.img:Tpixmap = LoadPixmap("fltkwindow.png") b.killImage FlushMem If img = Null Then Print "NO IMAGE in LOCAL" If b.img = Null Then Print "NO IMAGE in TYPE FIELD" End Give it a try. |
| ||
If you are experiencing that problem AD, then it is a bug. It is supposed to work as in Beaker's code above. Generally speaking, it should not be necessary to worry about memory management in your programs. Just create New objects when you need them and let BlitzMAX do the rest. |
| ||
Your code above looks sensible enough so I'm not sure why the difference. For me it would not work. If I defined `img` as a Local, in my version of code, it would become Null and lose the image. I agree I shouldn't need to be concerned about the memory management. |
| ||
Have you checked for strict - non-strict? Locals behave very different in non-strict and its usage isn't recommended for OO programming, just for procedural one! |
| ||
Here is some code extracted straight from my actual program that I'm working on, containing the elements needed to reproduce the situation I described. The basic idea is this: I have a custom `Bitmap` type which I am using to process my version of pixmap's in main memory, for use with my own blitting routines and such. The program should load a regular PNG image which must be 256x200 pixels in RGBA format, into the Bitmap object. To do this the PNG is loaded into a Pixmap and then the Bitmap object `cludges` onto the pixmap memory with a static bank, and stores the pixmap handle in one of the type's Fields for safekeeping. Then, more or less the same file, only in a RAW RGBA data version, also 256x200, is loaded into the same Bitmap object, the pixmap reference is made Null, and a Flushmem is performed. What SHOULD happen, you'd think, is that the other handle on that pixmap - the Local variable TestPX:TPixmap, should still point to the pixmap and it should still be drawable with DrawPixmap(). However, it isn't. When TestPX is a Local, the pixmap is not drawn at all. When TestPX is changed to a Global, the pixmap is drawn. So what is going on here, if it's not a memory management bug/issue/feature? I'd like to know if this is a bug, or if its working how it's meant to but I'm just not understanding it right. Strict Type Bitmap 'Object to define the dimensions and properties of a `bitmap` containing 4-byte-per-pixel (RGBA) data Field DataBankPtr:Byte Ptr 'Byte-Pointer to the actual memory reserved in the bank Field DataBankIntPtr:Int Ptr 'Int-Pointer to the memory in the bank Field DataBankLongPtr:Long Ptr 'Long-Pointer to the memory in the bank Field DataBank:TBank 'Pointer to a TBank object to store data Field Width:Int 'Total width of the bitmap in terms of pixels or units of representation Field Height:Int 'Total height of the bitmap in terms of pixels or units of representation Field RowBytes:Int 'How many total bytes of data are allocated in each row of pixels or units (Width*4) Field TotalBytes:Int 'How many bytes in total does the bitmap data take up Field FromPixmap:TPixmap 'Pointer to a Pixmap object which may have been used for loading data from an image, used as a static bank Field MaskValue:Int 'RGBA color or 4-byte value, where all pixels in the Bitmap of that color/value are transparent/non-copied when copying in Mode 2 (ValueMask mode) Method New() 'Executed when a new instance of `Bitmap` is created DataBankPtr=Null DataBankIntPtr=Null DataBankLongPtr=Null DataBank=Null Width=0 Height=0 RowBytes=0 TotalBytes=0 FromPixmap=Null MaskValue=0 End Method Method Initialize(MWidth:Int,MHeight:Int) 'To create and populate this `Bitmap` instance with memory space and valid variables Width=MWidth Height=MHeight RowBytes=MWidth Shl 2 '4 Bytes per pixel TotalBytes=MWidth*MHeight Shl 2 '4 Bytes per pixel DataBank=CreateBank(TotalBytes) DataBankPtr=BankBuf(DataBank) DataBankIntPtr=Int Ptr(DataBankPtr) DataBankLongPtr=Long Ptr(DataBankPtr) FromPixmap=Null MaskValue=0 'Default black or $0000 FlushMem End Method Method LoadRAWData(MFilePath:String) 'To load RAW data from a file into the memory bank 'You should call Initialize() first If DataBank=Null Print "Bitmap was not initilized prior to trying to load RAW file "+MFilePath+" into Bitmap!" End '!!!! EndIf Local MStream:TStream MStream=ReadStream(MFilePath) 'For reading only, pre-exists If MStream MStream=BigEndianStream(MStream) 'To read data in order, left to right MStream.Read(DataBankPtr,TotalBytes) CloseStream(MStream) Else Print "Couldn't load RAW file "+MFilePath+" into Bitmap!" End '!!!! EndIf FromPixmap=Null FlushMem End Method Method LoadImageData(MFilePath:String) 'To load an image-format file (png/jpg etc) and convert it into the `Bitmap`s memory bank to represent 4-byte-per-pixel values Local TempPixmap:TPixmap=New TPixmap TempPixmap=LoadPixmap(MFilePath) If TempPixmap=Null Print "There was a problem loading the file "+MFilePath+" as an image into a Bitmap object. File not found or error with file or wrong file type!" End '!!!! EndIf If PixmapFormat(TempPixmap)<>PF_RGBA8888 Then TempPixmap=ConvertPixmap(TempPixmap,PF_RGBA8888) DataBankPtr=PixmapPixelPtr(TempPixmap,0,0) DataBankIntPtr=Int Ptr(DataBankPtr) DataBankLongPtr=Long Ptr(DataBankPtr) Width=PixmapWidth(TempPixmap) Height=PixmapHeight(TempPixmap) RowBytes=PixmapPitch(TempPixmap) If RowBytes Mod 4>0 'Mod 4 due to PF_RGBA8888 which is 4 bytes per pixel Print "Image being loaded as Color "+MFilePath+" has width that is not multiple of 4 bytes (ie has skip pixels per row) even after ConvertPixmap!" End '!!!!! EndIf TotalBytes=(Width*Height) Shl 2 '4 Bytes per pixel DataBank=CreateStaticBank(DataBankPtr,TotalBytes) 'Bank's memory pointer = pixmap's memory pointer FromPixmap=TempPixmap 'Store FlushMem End Method End Type Graphics 640,480,0 Cls Flip Cls Flip Local Test:Bitmap=New Bitmap Cls Test.Initialize(256,200) Test.LoadImageData("TestImages/Green256x200.png") Local TestPX:TPixmap=Test.FromPixmap 'Must be Global otherwise when the only other Global FromPixmap is freed, the pixmap is lost DrawPixmap TestPX,320,16 Test.LoadRAWData("TestImages/SavedRAWData-Green256x200.raw") DrawPixmap TestPX,320,16 Flip;WaitKey End |
| ||
Hi, Not 100% sure what's its meant to do, but note that the Test.LoadRAWData call is overwriting the contents of the pixmap. This is because you create a static bank from the pixmap, which means that the bank shares exactly the same block of memory as the pixmap. So when Test.LoadRAWData loads stuff into the bank, it's also loading into the pixmap. I tend to think the TBank is unnecessary, as are all the ptrs. Why not just store a pixmap and work from that? I also get exactly the same result with or without any flushmems. |
| ||
The Bitmap object is meant to be retained the the image data is meant to be overwritten by the LoadRAWData() call. That's how it's designed to work. The only call I have that destroys the existing pixmap AND Fields is the loading of the PNG image. Everything else should retain the dimensions etc and just load/save raw data. Reason being that I am going to be loading part of a raw data file into a rectangular area within the existing bitmap image. Do you get the same result with a Local TestPX variable as with a Global one? With a Local, for me, the pixap does not draw (or draws black). Also the reason I'm not using pixmaps is because they are more generic than I need and, additionally, the Bitmap object isn't always going to contain an image that came from an image file, hence most of the time data is going to be loaded from raw and that FromPixmap field will be Null. So I would then have to have separate routines for when there is a pixmap to work from and when there isn't, if I were to make use of the pixmap routines. So I am globally doing all of the work myself. I will also be having fields that pixmap's don't have and it will relate to the rest of the system in a way that pixmap's don't, so it has to be custom. |
| ||
Sooooooooo............. does anyone know why the above code doesn't show the pixmap when the TestPX variable is a Local, but it does when it's a Global? |
| ||
I get the same error either way - Local or Global: "Couldn't load RAW file SavedRAWData-Green256x200.raw into Bitmap!" |
| ||
BUT, if I create my own SavedRAWData-Green256x200.raw file (copied the png and renamed it) then it works in both instances. |
| ||
Okay. Just to make sure I'm not nuts or making it up, I re-compiled it here again, with and without debug, and it continues to exhibit the same behavior. When I create Local TestPX:TPixmap the DrawPixmap command which should display it on the screen, actually display a black/empty pixmap. I drew a white rect behind it and it does blit the pixmap, it's just black rather than the image. When I change it to Global TestPX:TPixmap the image, as loaded, appears. In both instances, the same files are being loaded with the same routines. Now, when I remove both of the FlushMem's, the program works correctly. Question is, why does flushmem cause the image to not be loaded correctly? the pixmap is stored in the FromPixmap field before flushing, so it should still be retained. Ideas? |