delete command in bm


Tweet blitzmax memory tutorials
(Posted 1 year ago) meemoeuk

is there a command that deletes objects in blitzmax or ng?

I know the idea & philosophy is to delete all references to the object then the garbage collector deletes it, but for objects with a lot of references that need cutting it can sometimes be much easier just to explictly delete it.

i often find myself writing delete methods whereby all references are found and cut, like where the object is stored in an array, either the array is searched thru, or the index of arrays that object is stored is maintained as a field in the object.

(Posted 1 year ago) markcwm commented:

You only use Null, you don't call Delete, that is handled by the GC, but you can switch the GC into manual mode with GCSetMode 2, this requires you to call GCCollect but Delete is still managed by the GC.

You can implement a Delete method in your type and free things there, but since you don't know when it will be called you should avoid putting anything critical there.

Release is for objects stored as integer handles and has to be called manually to free the object.

You still have to manage objects manually in Blitzmax with lists, etc but it's much less troublesome than it was in Blitz3d.

(Posted 1 year ago) markcwm commented:

This is an old tutorial by Mark Sibly, note that it's out of date now so FlushMem doesn't exist and MemAlloced is GCMemAlloced.

...

Here's a brief tutorial directed mainly at BlitzPlus/Blitz3D users who are a bit confused by BlitzMax's memory management...

BlitzMax Memory Management - or, 'where the @*#! did Delete go?!?'

As many users from a Blitz3D or BlitzPlus background have noticed, BlitzMax does not include a 'Delete' command.

This appears to have caused a great deal of bemusement and head scratching in the Blitz community, which this tutorial will attempt to clear up.

Note that any references to Blitz3D in the rest of this document also apply to BlitzPlus.

The BASICS

First up, let's have a look at what Blitz3D's Delete command actually does. It really serves 2 purposes:

  1. The object is removed from its 'type list'.

  2. The memory used by the object is returned to the system - this is called 'deallocating' the object.

Step 1 is necessary due to Blitz3D's type list system, which automatically adds all New-ed objects to their global linked list.

Step 2 is necessary to prevent memory leaks. A memory leak is when your program has allocated a chunk of memory, used it for something, and then forgotten to deallocate it when it is no longer useful. Since computers have a finite amount of memory, this will eventually cause problems if it happens often enough!

The first thing about these 2 steps to note is that the global type list system is no longer present in BlitzMax, so step 1 is now completely up to the user - if you have added an object to a linked list (or lists), it is up to you to remove it.

If all you are wanting to achieve is a system similar to Blitz3D's type list system, then this is as simple as using ListAddLast immediately after you have created an object using New, and using ListRemove immediately before deleting it using Delete - oops, there is no Delete! Ok, using ListRemove instead of Delete!

This still leaves the question of deallocating the object's memory, and this is done in BlitzMax using the FlushMem command. The basic idea behind FlushMem is that it automatically detects 'dead' objects - objects that are no longer in use by your program - and deallocates them for you.

FlushMem should be placed in your program's 'main loop', and perhaps at other 'hotspots' in your program where you have created a lot of objects - for example, after loading a game level.

Note that the issue of deallocating objects is completely separate from the issue of type lists. Yes, if you forget to remove an object from a list it will remain 'live', but this is the same as forgetting to delete an object in Blitz3D!

One final complicating issue: BlitzMax includes a feature that allows you to assign objects to integers. This feature exists mainly to simplify things for beginners so, instead of...

Local image:TImage=LoadImage( "somepic.png" ) 'normal object to object assignment...

...you can just go...

Local image=LoadImage( "somepic.png" ) 'assigning an object to an int! What gives?

However, if you are using this feature in your programs, you must later 'free' the object yourself using Release...

Release image

You can therefore think of Release as being similar to Blitz3D's FreeImage (or FreeSound, FreeThis, FreeThat etc).

All-in-all, it's probably best to avoid object-to-int assignments altogther if you are concerned about memory leaks.

And that's it! Well, not quite, but the above should be enough information for you to be able to write leak-free programs with. I'll go into some of the nitty gritty details below...

The nitty gritty part 1 : dead objects

Ok, lets have a look at the question of 'what exactly is a dead object'? Well, a dead object is an object that is no longer referred to by any live objects. In other words, dead objects are objects your program can no longer 'see' - they are therefore completely useless to your program and can be safely deallocated.

Here are a few examples of when objects become 'dead':

'example 1
Local p:MyType=New MyType 'allocate object 1
p=Null 'object 1 is now dead

'example 2
Local p:MyType=New MyType 'allocate object 1
Local q:MyType=New MyType 'allocate object 2
p=q 'object 1 is now dead, object 2 is still alive

'example 3
Local p:MyType[1] 'allocate object 1 (an array object)
p[0]=New MyType 'allocate object 2
p=Null 'object 1 and object 2 both now dead

'example 4
Local p:MyType=New MyType 'allocate object 1
p.my_type=New MyType 'allocate object 2
p.my_type=New MyType 'allocate object 3 - object 2 now dead
Local q:MyType=p.my_type
p=Null 'object 1 and object 2 dead, object 3 still alive thanks to the q assignment above

After seeing this sort of thing, there is a tendancy for people to stick '=Null' code all over the place in an attempt to 'force' objects to become dead - but this is generally not necessary.

Sooner or later, all variables will either be overwritten by new values, or (in the case of Locals and Fields) simply go out of scope. Both of these actions are enough to ensure that dead objects will be correctly detected and deallocated.

Probably the only good reason to use '=Null' is in the case of global variables, when you know that a global variable will not be modified for a long time, but it also happens to be keeping a large chunk of memory alive that is no longer useful to your program. In this case, an '=Null' is probably the right choice.

Finally, there is one subtly with FlushMem you should be aware of: FlushMem only deallocates objects which have become dead since the function the FlushMem appears in was entered.

For this reason, it pays to put FlushMem as 'low' in your code as possible. For most applications, inside the 'main loop' is fine.

The nitty gritty part 2 : Tracking memory allocation

BlitzMax provides the MemAlloced() function to determine the amount of memory currently used by your program. However, this should be used with care.

The best place to use MemAlloced() is inside the 'meatiest' loop in your program. The important thing is that your program does not lose memory over time, and simply using MemAlloced() after executing a few instructions is not generally a good indicator of this. This is because some commands may perform 'first time initialization' allocations - indeed, the BlitzMax runtime occasionally has to reallocate memory itself, to adapt to the needs of your program.

Here is an example of the 'cleanest' way to use MemAlloced()

'MemAlloced() example:
Strict

Function Test()
'code you want to test for mem stability here...
End Function

For Local k=1 To 10000
Test
FlushMem
Print MemAlloced()
Next
(Posted 1 year ago) meemoeuk commented:

thanks mark, that's what I suspected. I just didn't want to proceed without being sure first, it would have saved me considerable time if i could simply use a delete command instead of writing delete methods.
What is the consensus on why MSibly removed delete? Style? Or is it faster somehow?

My programs tend to be highly intra dependent, i.e. objects have a lot of references to other objects, so when i want to delete an object it is not trivial to cut all the references.

I see 3 ways to proceed

  1. write a delete method for each class\type that searches for all references and cuts them
  2. have a 'delete_flag' which is checked at every logic condition where an object is checked for existence. If the delete_flag is set then any condition that finds it will act as if the object is null, and references to the object at hand in that routine will be cut. Eventually, if all routines are run then all references to object will be cut.
  3. wrap every object in a container class, so all references to an object actually point to an object's container object. To delete an object, just cut the one reference in the container class to its wrapped object, which will delete the object. The problem is simplified to a problem of managing loads of container objects, which is still a considerable problem.

which is best do you think? Is there a better way?

(Posted 1 year ago) markcwm commented:

The reason why there is no Delete is because the garbage collector does that, and garbage collection is considered a better way to manage memory than manually, all modern languages have memory management systems of some kind, it's to make coding less work and less time spent checking for memory leaks.

Well personally I've never implemented Delete methods, I prefer deleting in custom methods so you're a bit more in charge of things. I have used the delete_flag method to know when an object was released for GC, this avoids a crash if the object is double freed.

3 is a bit too complicated. I recommend using a global list for each type to store references and then just remove it from the list in a custom method, you shouldn't even have to Null the object after, if nothing else is referencing it the GC will know and then release it, mostly things go out of scope and the GC picks them up for us.

(Posted 1 year ago) meemoeuk commented:

> it's to make coding less work

the misguided idea was to make coding easier, but writing an extra condition at every existence check and having reference cutting in every method \ function and having a delete flag instead of a delete isn't making things easier. Its like being fitted with a snorkel and scuba gear when u are capable of breathing in air already. Why memory managment was suddenly considered too hard for programmers in the '90s the world may never know. In speed tests languages without GC run faster every time. So much for programmers not being able to manage memory.

bah

(Posted 1 year ago) meemoeuk commented:

could Brucey write a delete command for ng?

(Posted 1 year ago) TomToad commented:

My programs tend to be highly intra dependent, i.e. objects have a lot of references to other objects, so when i want to delete an object it is not trivial to cut all the references.

You do not need to remove all the references. When the base object is removed by the GC, then any references it points to are set to be removed (assuming there are no more references to it). For example, object A has a reference to object B, which has a reference to object C. So A = New Obj, A.B = New Obj, A.B.C = New Obj. When there is no more reference to object A by either by it falling out of scope, A is replaced by another object, or set to Null, then A will be removed. Once A is removed, there is no longer any reference to B, so it also gets removed. When B is removed, there is no longer a reference to C, so it gets removed. No need to write any special delete method to do it.

If, for some reason, you need to explicitly remove the sub-objects, then you can create a custom Remove() method. In there, you would remove the objects it points to. I would usually do something like this

Method Remove()
    B.Remove()
    B = Null
End Method

Then, in the main code, when I no longer need A, I can do A.Remove() before changing the reference. That might seem like extra work to you, but it is no more work than what you do in a non managed language such as C++. In C++, what I put in the Remove() method will go in the object's destructor, and instead of calling A.Remove(), you would call delete A; instead.

The reason for the GC isn't so much about speed or ease of coding. It is to make a more stable, robust, bug free program. In non managed languages, as projects get larger, it is too easy to forget to delete an object, causing a memory leak. Or you call delete on an object that is still live (i.e. being used in another part of the code) so when the code tries to access the object, it is no longer valid.

To use your scuba analogy, imagine if breathing wasn't automatic. You had to consciously breath in and breath out. Now imaging you forget to breath and pass out, or you're swimming under water and forget not to breath. By having our breathing automatic, we are kept alive.

(Posted 1 year ago) meemoeuk commented:

>assuming there are no more references to it

if i don't cut all the references to the object i want to delete then the GC won't delete it. I have the 3 methods to delete object above, or get brucey to give bm what its needed for 15 years, a delete command.
Is Blitzmax written in C++ ? then it should be easy to give it a delete command. Sibly just refused to do it on lofty principle rather than practicality.

(Posted 1 year ago) meemoeuk commented:

>The reason for the GC isn't so much about speed or ease of coding. It is to make a more stable, robust, bug free program.

ok, that's within reason. I should be able to write my double condition clauses now without cursing Sibly every time.

Reply To Topic (minimum 10 characters)

Please log in to reply