Documentation/4.4/Developers/Tutorials/MemoryManagement

From Slicer Wiki
Revision as of 06:46, 25 December 2014 by UpdateBot (talk | contribs) (Nightly -> 4.4)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Home < Documentation < 4.4 < Developers < Tutorials < MemoryManagement


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


Pointers to VTK object

Calling the plain New() method of VTK objects and storing the returned pointer in a plain pointer should be avoided, as this very often causes memory leaks.

Bad, should be avoided:

 vtkMRMLScalarVolumeNode* vol = vtkMRMLScalarVolumeNode::New();
 ... do something, such as vol->GetImageData(), someObject->SetVolume(vol)...
 vol->Delete();
 vol=NULL;

Recommended:

 vtkNew<MRMLScalarVolumeNode> vol;
 ... do something, such as vol->GetImageData(), someObject->SetVolume(vol.GetPointer())...

Also good:

 vtkSmartPointer<MRMLScalarVolumeNode> vol=vtkSmartPointer<MRMLScalarVolumeNode>::New();
 ... do something, such as vol->GetImageData(), someObject->SetVolume(vol)...

vtkNew is preferred in general for new object creation, as it has a simpler syntax and used almost exclusively in the Slicer core source code. A slight inconvenience is that when we need use the GetPointer() method to get the actual pointer value.

A vtkSmartPointer pointer can be created without actually creating an object, so vtkSmartPointer should be used when:

  • we don't know the exact object type that we want to create at the time of the pointer creation (e.g., we create a vtkSmartPointer<vtkMRMLVolumeNode> and later set it to point to a vtkMRMLScalarVolumeNode or vtkMRMLVectorVolumeNode), or
  • we need to take the ownership of an already created object (using vtkSmartPointer::Take(...), see a examples below)

Factory methods

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')