Difference between revisions of "Documentation/Nightly/Developers/Tutorials/MemoryManagement"

From Slicer Wiki
Jump to: navigation, search
(Prepend documentation/versioncheck template. See http://na-mic.org/Mantis/view.php?id=2887)
(Made the memory management differences more clear between C++ and Python)
Line 1: Line 1:
 
<noinclude>{{documentation/versioncheck}}</noinclude>
 
<noinclude>{{documentation/versioncheck}}</noinclude>
From http://massmail.spl.harvard.edu/public-archives/slicer-devel/2011/007513.html
 
  
Like VTK, Slicer contains some "factory" methods:
+
Similarly to VTK, Slicer contains some "factory" methods:
 
  - vtkMRMLScene::CreateNodeByClass()
 
  - vtkMRMLScene::CreateNodeByClass()
 
  - vtkMRMLScene::GetNodesByClass()
 
  - vtkMRMLScene::GetNodesByClass()
 
  - ...
 
  - ...
Like in C++, it means that the functions return an object with a reference
 
count of 1 that nobody "owns" and the caller must take care of releasing
 
the object to avoid memory leak.
 
  
While there is workaround for some methods (
+
Factory methods return a pointer to a VTK object (with a reference count of 1) that the caller "owns", so the caller must take care of releasing the object to avoid memory leak.
slicer.mrmlScene.CreateNodeByClass('vtkMRMLModelNode') should be replaced
 
by slicer.vtkMRMLModelNode() ) there is currently no automatic/clean
 
mechanism to release the object created by such methods.
 
  
The only "hack" that exists for now is to decrease the reference count
+
= Loadable modules (C++) =
manually in your code:
 
  
  nodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLLinearTransformNode')
+
If storing in a new variable:
nodes.UnRegister(slicer.mrmlScene)      # ----> (reference count was 2, keep 1 for the python reference)
+
 
...
+
  vtkSmartPointer<vtkCollection> nodes = vtkSmartPointer<vtkCollection>::Take(mrmlScene->GetNodesByClass("vtkMRMLLinearTransformNode"));
  
In C++, the following would apply:
+
If the variable is created already:
vtkCollection* nodes =
 
mrmlScene->GetNodesByClass("vtkMRMLLinearTransformNode");
 
...
 
nodes->Delete();
 
  
or using vtkSmartPointer:
 
 
  vtkSmartPointer<vtkCollection> nodes;
 
  vtkSmartPointer<vtkCollection> nodes;
 
  nodes.TakeReference(mrmlScene->GetNodesByClass("vtkMRMLLinearTransformNode"));
 
  nodes.TakeReference(mrmlScene->GetNodesByClass("vtkMRMLLinearTransformNode"));
 +
 +
Unsafe, legacy method without using smart pointers (not recommended, because the Delete() method may be forgotten or skipped due to an early return from the function):
 +
vtkCollection* nodes = mrmlScene->GetNodesByClass("vtkMRMLLinearTransformNode");
 
  ...
 
  ...
 +
nodes->Delete();
 +
 +
= Python scripts and scripted modules =
  
Amendment from JC http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=19772
+
Factory methods return an object that the caller owns (and thus the caller has to delete, with reference count >0) and Python adds an additional reference to this object when stored in a Python variable, resulting in a reference count of >1. To make sure that the object is deleted when the Python variable is deleted, we have to remove the additional reference that the factory method added. There is currently no automatic mechanism to remove that additional reference, so it has to be done manually by calling UnRegister (the reference count shouldn't be explicitly set to any specific value, it is only allowed to increment/decrement it using Register/UnRegister):
  
The reference count shouldn't be decreased using the naive approach where
+
nodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLLinearTransformNode')
it is set to one. Indeed, internally, the reference count could be any
+
nodes.UnRegister(slicer.mrmlScene)  # reference count is increased by both the factory method and the python reference; unregister to keep only the python reference
number greater than one. It doesn't have to be 2.
+
...
  
Considering this last remark, using the following approach is *REQUIRED*,
+
'''To avoid forgetting the UnRegister call, it is better to avoid factory methods whenever it is possible.'''
otherwise it will lead to a CRASH of the application.
 
  
 +
For example, instead of using the CreateNodeByClass factory method and call UnRegister manually:
 
  n = slicer.mrmlScene.CreateNodeByClass('vtkMRMLViewNode')
 
  n = slicer.mrmlScene.CreateNodeByClass('vtkMRMLViewNode')
 
  slicer.mrmlScene.addNode(n)
 
  slicer.mrmlScene.addNode(n)
 
  n.UnRegister(slicer.mrmlScene)
 
  n.UnRegister(slicer.mrmlScene)
 
+
this should be used (if the name of the node is known when the script is written):
Even better, this specific example can be simplified using the following
 
syntax, this is the *RECOMMENDED* approach, it will prevent bug, memory
 
leaks and crashes:
 
 
 
 
   n = slicer.mrmlScene.addNode(slicer.vtkMRMLViewNode())
 
   n = slicer.mrmlScene.addNode(slicer.vtkMRMLViewNode())
 
+
or this should be used (if the name of the node is generated at runtime):
And if the name of the node is generated at runtime, this could be done:
 
 
 
 
   n = eval('slicer.mrmlScene.AddNode(slicer.%s())' % 'vtkMRMLViewNode')
 
   n = eval('slicer.mrmlScene.AddNode(slicer.%s())' % 'vtkMRMLViewNode')

Revision as of 16:01, 31 October 2013

Home < Documentation < Nightly < Developers < Tutorials < MemoryManagement


For the latest Slicer documentation, visit the read-the-docs.


Similarly to VTK, Slicer contains some "factory" methods:

- vtkMRMLScene::CreateNodeByClass()
- vtkMRMLScene::GetNodesByClass()
- ...

Factory methods return a pointer to a VTK object (with a reference count of 1) that the caller "owns", so the caller must take care of releasing the object to avoid memory leak.

Loadable modules (C++)

If storing in a new variable:

vtkSmartPointer<vtkCollection> nodes = vtkSmartPointer<vtkCollection>::Take(mrmlScene->GetNodesByClass("vtkMRMLLinearTransformNode"));

If the variable is created already:

vtkSmartPointer<vtkCollection> nodes;
nodes.TakeReference(mrmlScene->GetNodesByClass("vtkMRMLLinearTransformNode"));

Unsafe, legacy method without using smart pointers (not recommended, because the Delete() method may be forgotten or skipped due to an early return from the function):

vtkCollection* nodes = mrmlScene->GetNodesByClass("vtkMRMLLinearTransformNode");
...
nodes->Delete();

Python scripts and scripted modules

Factory methods return an object that the caller owns (and thus the caller has to delete, with reference count >0) and Python adds an additional reference to this object when stored in a Python variable, resulting in a reference count of >1. To make sure that the object is deleted when the Python variable is deleted, we have to remove the additional reference that the factory method added. There is currently no automatic mechanism to remove that additional reference, so it has to be done manually by calling UnRegister (the reference count shouldn't be explicitly set to any specific value, it is only allowed to increment/decrement it using Register/UnRegister):

nodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLLinearTransformNode')
nodes.UnRegister(slicer.mrmlScene)  # reference count is increased by both the factory method and the python reference; unregister to keep only the python reference
...

To avoid forgetting the UnRegister call, it is better to avoid factory methods whenever it is possible.

For example, instead of using the CreateNodeByClass factory method and call UnRegister manually:

n = slicer.mrmlScene.CreateNodeByClass('vtkMRMLViewNode')
slicer.mrmlScene.addNode(n)
n.UnRegister(slicer.mrmlScene)

this should be used (if the name of the node is known when the script is written):

 n = slicer.mrmlScene.addNode(slicer.vtkMRMLViewNode())

or this should be used (if the name of the node is generated at runtime):

 n = eval('slicer.mrmlScene.AddNode(slicer.%s())' % 'vtkMRMLViewNode')