Removing Class Object

Monkey Forums/Monkey Beginners/Removing Class Object

darky000(Posted 2014) [#1]
Hello,

I am wondering how to remove an object using a method of THAT Class. I tried Self = Null and it's obviously not working. I'm doing this because I want to remove the links, nodes, arrays attached to it.

Also, how can I be certain that it is removed? Is there a code to know if that particular memory is free?


ImmutableOctet(SKNG)(Posted 2014) [#2]

TL;DR: Don't worry about objects, just make sure not to keep references to ones you don't want (This includes containers like 'Lists', 'Maps', and 'Stacks', as well as the content within them). And don't worry about arrays, they're handled well. Your memory is already managed by Monkey.


Monkey is completely garbage collected, it'll check if there are references to the object before trying to delete it. Basically, garbage collectors do the work of memory management for you. The only way (Without external options) to delete an object is to set it to 'Null' / letting it be unreferenced. In other words, if you have no records of an object, it'll no longer exist.

Arrays are not objects, however. Arrays effectively/conceptually act as stack/scope local data structures. These array types are allocated on the current scope, and should not / can not be set to 'Null'. They'll get removed automatically. Of course, there's still a way of getting rid of an array manually, but that just generates an empty array. This can be done like so:

InsertArrayNameHere = []


If you had a C++ background and looked at how things were being done, you'd see why the existence of this problem makes so much sense.

That being said, any instances of classes within Monkey are objects (Arrays aren't); this effectively means that the standard libraries Monkey comes with ('list', 'map', etc; see the 'monkey' module for more details) are worked with as objects (Including 'Nodes' and such).

So basically, don't worry about your arrays, they'll be dealt with very well by Monkey. Objects on the other hand have to be "Nulled out" on some level; this means you can be really lazy about setting references/pointers to 'Null'. Take a 'List' for example; if you were to set your pointer to the list-object to 'Null', any objects it allocated would be collected along with the original list-object. That being said, if you were to hold (Point to) one of the objects the list had, that object would not be destroyed by the garbage collector.

There are a few notes about garbage collection to keep in mind, though:

1) Not all targets have the same garbage collector. This means you need to keep in mind how many objects you generate on some targets ('Pools' are an answer to this).
2) Some targets don't naturally have garbage collectors. On these targets, Monkey's own garbage collector is used (Written by Mark in C++).
3) When the standard/Monkey-specific garbage collector is used (Mainly C++ Tool, GLFW, and one or two other targets) it can be configured. This means you can change when collection is done, how it is done, or just plain disable it per-program. (This is done with the preprocessor; see V71a's version notes in the "VERSIONS.TXT" file)

So with this in mind, the reason why "Self = Null" doesn't work becomes apparent. Your local/global variable with a reference to the object is not the object, just a reference. If you were to set 'Self' to 'Null', all you'd be doing is setting a local reference (Pointer) to 'Null', not deleting your object.

Here's what the standard C++ target (Used for command-line tools such as Monkey's compiler) outputs for arrays (Cleaned up slightly for clarity):


Here's the equivalent Monkey code:


If you're curious about my use of ":=", click here.

As you can see, setting an array to "[]" isn't really the best idea. I (As well as many others) have made that mistake before, there's no real point with arrays. And of course, arrays are different per-target, but this should give you a rough idea, as Monkey is very consistent between targets.

So, unfortunately, you can't release/delete/remove the object from the object itself (Which is a bit of an odd choice on your end, honestly). You can, however, make your own destructor methods (Though, they need to be called manually). I tend to go with 'Free' for my destructor names. But you do have to realize that once you reference the objects within that given object, other places holding/pointing to that object will also see the effects. Basically for everything that isn't a primitive type (Or array), you'll be doing things 'by reference', instead of 'by data'. I personally have an elaborate system for this. There's also the integration of the 'brl.pool' module, but that requires a bit of work and redesign to use such a system. Most notably, you'd need several constructor and/or destructor methods (For proper destruction), but there's also more complicated systems you could make. I for one have made an object pooling system for 'GameObjects', which works rather well, but it's based on suspending objects, rather than completely rebuilding them.

And as a side note, setting your objects to 'Null' with a destructor method may be a good way to go, but that can get into different design practices as well.

Now, you might be thinking: "What about strings?" - Well, I'd get into that, but that's a little long, not to mention REALLY platform specific. Basically, they're like objects, only non-Monkey objects, so you can just act as if they're immutable primitive types (Int, Float, and Bool), really.

And by the way on most of the standard C++ targets, you can use 64-bit 'Floats'. (This is done by setting the preprocessor variable 'CPP_DOUBLE_PRECISION_FLOATS' to 'True')

Anyway, there's your long post about memory management techniques. I hope this helps, and as always, feel free to post any other questions. I'm willing to help with alternate ways of managing memory, but that'll require a bit more work on my part.

For a not-so-technical response, read the 'TL;DR' section. I went all-out on this post, so I hope it helps.

EDIT: And by the way, you might not want to get me started on manually calling Monkey's C++ garbage collector. That has been a bit of a problem in the past. (Fixed now, from the look of it)


darky000(Posted 2014) [#3]
Thanks for that detailed reply! I receive more information than I could have asked for. :)




The only way (Without external options) to delete an object is to set it to 'Null' / letting it be unreferenced. In other words, if you have no records of an object, it'll no longer exist.


This is what I want to do in my own class and I want it placed inside it's own method so I can reinitialize certain values in different parts of my code, setting other objects to null as well as setting this instance to null. Is there a way around this or do I have to settle with this?

This is what I want to achieve if it's possible

Class myClass

Field objNode:list.Node<myClass> = Null
Field fooObject:GameObject

    Method Remove%()
       objNode.Remove()
       fooObject.Remove()

      Self = Null <--- I know this isn't working but trying to make a work around it.

       Return 0
    End Method



Or am I stuck with this code:

.....
Field Bar:myClass

Method OnCreate%()
        Bar = New myClass

       .......

        Bar.Remove()
        Bar = Null
  
      #rem
          Bar.Remove()                    'instead of just this
       #end

       Return 0
End Method







So with this in mind, the reason why "Self = Null" doesn't work becomes apparent. Your local/global variable with a reference to the object is not the object, just a reference. If you were to set 'Self' to 'Null', all you'd be doing is setting a local reference (Pointer) to 'Null', not deleting your object.


It is actually an error on monkey and yes, you are correct.


ImmutableOctet(SKNG)(Posted 2014) [#4]
If I'm understanding you correctly, you want to actually delete the object in one go. If that's the case, then you really can't, you'll have to set the object to 'Null' after calling the method. You see, this comes back to what exactly your fields are; they aren't actually the objects you set them to, they're pointers to those objects. Monkey simply checks if there are any existing references before deleting the object. Even if you could do the whole "Self = Null" thing for deletion, you'd have fields pointing to an object that doesn't exist.

Though, now that you mention it, if this idea actually could work on all targets (Which it doesn't really, sadly), you might be on to something I thought about a long while ago. Basically, the idea is to have the GC set each reference pointing to that particular object to whatever you set 'Self' to. Of course, this is problematic in many ways, but not 100% impossible. The only especially easy (And platform-independent) way of doing this is boxing. The issue with boxing is that you're adding another layer to it, and Monkey only supports automatic conversion for primitives and strings ('ToInt', 'ToFloat', 'ToString', etc; see the 'monkey.boxes' module). As a fun fact, Monkey supports doing this the other way via constructors, but that tends to be inefficient, and is generally something left to occasional shorthand.

Back on topic, you technically don't need a 'Remove' command, unless you're dealing with more advanced setups. That works better for things such as game-objects, and more complicated classes like the standard containers (Which technically isn't needed, but it's a really nice thing to have). Just to make sure you're on the same page, I'm talking about destructors geared toward object re-use, such as the 'Clear' method in the 'List' class, or more elaborate setups for object pooling.

And for those who want to do this, the commands for manual garbage collection with Monkey's custom collector are mainly 'gc_collect', and 'gc_collect_all' (Via 'Extern'):


Those two commands work differently, and will likely change with different GC modes. The 'gc_collect_all' command is basically a full check, and the 'gc_collect' command is for a normal call as it normally would do automatically. I don't recommend using those without full understanding of Monkey's GC. But for testing purposes, knock yourself out.

EDIT: I forgot to mention this, but Monkey's documentation has a section about this.


Gerry Quinn(Posted 2014) [#5]
I think I'm repeating ImmutableOctet's point to some extent. But yes, there are cases where you want to encourage the freeing of resources, and List.Clear() is a good example. The list object remains afterwards, but it doesn't point to anything. Later on, the list may get forgotten, but even if it isn't, there are only a few bytes defining the list structure itself.

So if your class creates a giant array or stuff like that, you could put in a Clear() method that forgets all the resources. But the owner of the class has to forget the class instance itself.


darky000(Posted 2014) [#6]
@ImmutableOctet - I'm used to the old C in my days where I have this constant nagging in my head to manually free memory (reinitialize pointers and values). I hate moving on without knowing what is happening inside. If I am optimizing my code more efficiently, etc... Thanks for this hefty amount of information. I'll try them all out!

@Gerry - Thanks Gerry as always. I'm using that when restarting the game... :-)