Roku Developer Program

Developers and content creators—a complete solution for growing an audience directly.
cancel
Showing results for 
Search instead for 
Did you mean: 
ratish
Level 7

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
0 Kudos
3 Replies
RokuMarkn
Level 7

Re: help to understand memory management

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
0 Kudos
ratish
Level 7

Re: help to understand memory management

"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?
0 Kudos
Rek
Level 7

Re: help to understand memory management

"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
0 Kudos