Forum Discussion

ratish's avatar
ratish
Visitor
11 years ago

help to understand memory management

Hi

Sharing you a sample design pattern i have implemented towards an object oriented framework design. Need some help to understand if the object allocation and deallocation done in code does help the GarbageCollector to free up memory accordingly.

Below pasted is illustration of How I defined a class and used it in main.brs. Controller.brs holds a class with an id, an array object and two methods addListener and OnClose.
Inside main.brs's Main() i am creating an instance of Controller by Calling InitializeController() which returns me the 'this' associative array holding id, Listeners and the functions.
I can use the returned 'this' as controller object and access its elements using dot operator.
controllerObj1.id, controllerObj1.addListener(), controllerObj1.OnClose() etc.

In main() once i am done with controllerObj1, i am calling controllerObj1.OnClose() which in turn will release all element in controllerObj1.
Would like to understand if the Garbage collector would get triggered to free up memory allocated by the elements id, Listeners when we do controllerObj1.OnClose().
Or just calling controllerObj1 = invalid serve the purpose of releasing the elements?

Please ignore any typo in syntax as this was typed down for illustration and its not a working code.

Controller.brs
--------------------
Function InitializeController()
this = {
id:"controller1"
Listeners:CreateObject("roAssociativeArray")
addListener:CTRL_AddListener
onClose:CTRL_OnClose
}

return this
End Function

Function CTRL_AddListener(keyName, value)
m.Listeners.AddReplace(keyName, value)
End Function

Function CTRL_OnClose()
for each obj in m
if (type(m[obj]) <> "roFunction") or (type(m[obj]) = "roFunction" and obj <> "onclose") then
m[obj] = invalid
m.Delete(obj)
end if
next
End Function

Main.brs
----------
sub Main(argArr = invalid as Dynamic )
controllerObj1 = InitializeController()
.
.
*****some operetions with the controllerObj1 *******
controllerObj1 .addListener("listener1", listenerObj1)
controllerObj1 .addListener("listener2", listenerObj2)


'release the controllerObj1
controllerObj1 .OnClose()
controllerObj1 = invalid
end Sub

3 Replies

  • Seems like you're doing some unnecessary work there. Memory management is based on reference counting the objects, so unless you have circular references, you can just drop the reference to the top level object. If it contains any references to other objects, those will also be deleted when the referring object is destroyed and thus no longer refers to them. There shouldn't be any need for your OnClose function.

    --Mark
  • "RokuMarkn" wrote:
    Seems like you're doing some unnecessary work there. Memory management is based on reference counting the objects, so unless you have circular references, you can just drop the reference to the top level object. If it contains any references to other objects, those will also be deleted when the referring object is destroyed and thus no longer refers to them. There shouldn't be any need for your OnClose function.

    --Mark


    If my understanding is correct, does it mean calling controllerObj1 = invalid in main() would be enough to release the memory allocated by this and its elemenst such as id, listeners etc?
  • "ratish" wrote:
    "RokuMarkn" wrote:
    Seems like you're doing some unnecessary work there. Memory management is based on reference counting the objects, so unless you have circular references, you can just drop the reference to the top level object. If it contains any references to other objects, those will also be deleted when the referring object is destroyed and thus no longer refers to them. There shouldn't be any need for your OnClose function.

    --Mark


    If my understanding is correct, does it mean calling controllerObj1 = invalid in main() would be enough to release the memory allocated by this and its elemenst such as id, listeners etc?


    In this case, that is correct. However, if there was a circular reference within controllerObj1, you would be leaking memory. Consider the following example:

    Controller.brs

    Function InitializeController()
    this = {
    id:"controller1"
    Listeners:CreateObject("roAssociativeArray")
    addListener:CTRL_AddListener
    onClose:CTRL_OnClose
    }

    return this
    End Function

    Function CTRL_AddListener(keyName, value)
    m.Listeners.AddReplace(keyName, value)
    End Function

    Function CTRL_OnClose()
    for each obj in m
    if (type(m[obj]) <> "roFunction") or (type(m[obj]) = "roFunction" and obj <> "onclose") then
    m[obj] = invalid
    m.Delete(obj)
    end if
    next
    End Function


    Main.brs

    sub Main(argArr = invalid as Dynamic)
    ' ----------------------------------------------------------------------
    ' Scenario #1

    controllerObj1 = initializeController()
    controllerObj1.addListener("listener1", function() as Void
    ?"Listener 1 executed"
    end function)

    ' ***** SNIP some operations *****

    ' Invalidate controllerObj1, this causes garbage collection.
    controllerObj1 = invalid

    ' ----------------------------------------------------------------------
    ' Scenario #2

    controllerObj2 = initializeController()
    controllerObj2.addListener("listener1", controllerObj2) ' Create circular reference. (controller references itself)

    ' ***** SNIP some operations *****

    ' Invalidate controllerObj2, this will leak since controllerObj2 has
    ' a circular reference.
    controllerObj2 = invalid


    In the second scenario the controller has a reference to the AA, which has a reference to the controller, which has a reference to the AA, which... etc. In order to allow garbage collection, the circular reference must be broken. In this case, you could simply clear the AA. You may find it valuable to add a common destructor interface which handles any necessary deletion. Something like this:

    Controller.brs

    Function InitializeController()
    this = {
    id:"controller1"
    destroy:CTRL_Destroy
    Listeners:CreateObject("roAssociativeArray")
    addListener:CTRL_AddListener
    onClose:CTRL_OnClose
    }

    return this
    End Function

    Function CTRL_AddListener(keyName, value)
    m.Listeners.AddReplace(keyName, value)
    End Function

    Function CTRL_OnClose()
    for each obj in m
    if (type(m[obj]) <> "roFunction") or (type(m[obj]) = "roFunction" and obj <> "onclose") then
    m[obj] = invalid
    m.Delete(obj)
    end if
    next
    End Function

    Function CTRL_Destroy()
    m.Listeners.clear()
    End Function


    Main.brs

    sub Main(argArr = invalid as Dynamic)
    ' ----------------------------------------------------------------------
    ' Scenario #2 with destruction

    controllerObj2 = initializeController()
    controllerObj2.addListener("listener1", controllerObj2) ' Create circular reference. (controller references itself)

    ' ***** SNIP some operations *****

    ' Allow the controller to perform cleanup and break circular references
    controllerObj2.destroy()

    ' Invalidate controllerObj2, this will cause garbage collection.
    controllerObj2 = invalid