User Tools

Site Tools


xff_-_memory_management

xff - memory management

Motivation

Matlab's only way of passing arguments into a called function is “by-value”, which means that if the called function alters a variable (which can be highly complex, such as a nested struct!), the calling function needs to accept any such altered variables as returned values and re-assign them to their original variables. Here is an example:

matlab_pass_by_value.m
% the function called is fictitious, and would replace
% bad coordinate values in variables X, Y, and Z with
% NaNs in all three of them
[X, Y, Z] = nanbadcoords(X, Y, Z);

While this is relatively convenient (and Matlab's internal calling syntax detects that variables are to be overwritten, so unless an error occurs, there is little memory overhead), this is highly unusual if objects are used. Most object-oriented languages support a “call-by-reference” syntax, where the calling function only receives a reference or pointer to the actual object and any changes are thus automatically propagated to the calling function (and any function up the stack!).

Implementation

In xff (and also the other NeuroElf classes, for that matter), storage is not allocated in the struct variable that constitutes the class object (which is true for most other non-NeuroElf class objects!). The only field in the struct variable of any xff object is the .L field which contains a unique lookup identifier that allows xff and its internal methods to locate the actual storage of the object:

% creating an object
vmr = xff('new:vmr');
 
% displaying the struct contents:
struct(vmr)

This would produce something like the following sample output:

ans =

    L: 0.6692

The actual storage is kept in a global variable, xffcont, which I recommend against altering manually:

% intialize xff
xff;
 
% display list of global variables
whos global

would produce (if no other global variables are present):

  Name          Size              Bytes  Class     Attributes

  xffclup       1x1                   8  double    global    
  xffconf       1x1               29219  struct    global    
  xffcont       1x1             2700694  struct    global    
  xfflast       1x2                  16  double    global    
  xinimeth      1x1                5342  struct    global

These variables have the following content/meaning:

  • xffclup - content lookup list (contains one double number per loaded object, one of which is the ROOT object)
  • xffconf - xff configuration (global instead of persistent as it is used by some methods also)
  • xffcont - the actual storage of objects as a vector of structs, each having the fields
    • C - actual content (what is displayed when an object variable is used without a semicolon)
    • F - filename (empty if not loaded from disk and not saved yet, “<ROOT>” for the ROOT object)
    • H - sub-struct of handles to other objects (e.g. transimg objects for data slicing, etc.)
    • L - lookup value (for convenience stored also in the struct)
    • S - file format specification (extension(s), fields, etc.)
    • U - unwind stack information (used for the internal automatic garbage collection of xff)
  • xfflast - 1×2 double value, for which object was the fieldnames/methods function called last and second-to-last
  • xinimeth - method name storage of xini class; as xff uses xini for some settings, this class is also initialized

Consequences

As the storage is NOT directly associated with any given object variable, using Matlab's clear function on any such variable will not lead to the allocated memory being freed by Matlab! Instead the .ClearObject call must be issued to free up memory:

xff_clearobject_sample.m
% create a new VMR
vmr = xff('new:vmr');
 
% set random content
vmr.VMRData = uint8(100 + round(10 * randn(size(vmr.VMRData))));
 
% save as (without argument requests for filename)
vmr.SaveAs;
 
% clear object!!
vmr.ClearObject;

Without this last line, the associated memory would remain allocated even if this is used in a sub-function (which removes the vmr object from memory, but that only contains a struct with the .L field!).

Garbage collection

To ensure that user-written functions do not clutter up the global xffcont variable with objects that are no longer in use, a garbage collection has been implemented into xff. This works as follows:

  • for any object, the stack is recorded during object creation (which function created the object)
  • whenever any xff method is called, the stack of all remaining objects is checked, and if the current stack doesn't match, those objects are removed
  • to protect objects from being affected, the bless function must be called

bless

Here is an example of a user-written function that creates an object which is suitable for passing outside of the function and not being affected by this garbage collection (it also creates another object which would be removed by garbage collection if the .ClearObject method would not be called):

spheresvmr_sample.m
function vmr = spheresvmr_sample
% spheresvmr_sample  - create a VMR with 10 small spheres
%
% FORMAT:       vmr = spheresvmr_sample;
%
% No input fields.
%
% Output fields:
%
%       vmr         VMR object with 10 spheres
 
% create VMR object (output)
vmr = xff('new:vmr');
 
% create VOI object as a helper
voi = xff('new:voi');
 
% create 10 spheres in VOI
for c = 1:10
    voi.AddSphericalVOI(min(120, max(-120, round(40 * randn(1, 3)))), 7);
end
 
% set those coordinates to 200 in VMR
for c = 1:10
    vmr.VMRData(bvcoordconv(voi.VOI(c).Voxels, 'tal2bvc', vmr.BoundingBox)) = 200;
end
 
% make sure VMR is not affected by garbage collection
bless(vmr, 1);
 
% destroy voi (would be removed by garbage collection some time later otherwise)
voi.ClearObject;
 
% end of function
xff_-_memory_management.txt · Last modified: 2010/06/10 13:34 by jochen