Change in object deletion when reading TTrees between Root5 and Root6?

We have an program that was written a while ago, which reads TTrees containing custom objects. In ROOT 5, this works as expected. In ROOT 6, it leaks memory like it’s going out of style.

The objects we are storing in the tree contain a pointer to another object, which allocates memory. Let’s call the objects in the TTree MyTreeElem, each of which contains a pointer to a MyObj on the heap, which also contains pointers to memory allocated for that class.

eg:

class MyObj : public TObject {
public:
  void * someblock;
  MyObj() : someblock(new void[100]);
  ~MyObj() { delete someblock; }
}
class TreeElem : public TObject {
public:
  MyObj * content;
  TreeElem() { content(new MyObj()); }
  ~TreeElem() { delete content; }
}

We set up to read our tree, and read a couple of elements.

  TTree * mytree = ... // get from some file
  MyTreeElem * elem = 0;
  mytree->SetBranchAddress("myelem",&elem);
  mytree->GetEntry(0);
  mytree->GetEntry(1);

When built against ROOT5, when we get the second entry in the tree, it appears that ~MyTreeElem() is not called but ~MyObj() is called, cleaning up the memory assigned by the heap MyObj owned by the MyTreeElem.

When built against ROOT6, the~MyObj() method is not called when getting the next entry, meaning that without a manual cleanup call the program leaks memory extremely rapidly.

I can get around this by setting TBranch::SetAutoDelete(kTrue);, which leads to a call to ~MyTreeElem(), which cleans up the other objects too. The documentation, however, suggests that using SetAutoDelete() is to be avoided. On top of this, we would require the SetAutoDelete() in every script that reads these files. Other forms of loop that do not include this call also leak dangerously, like a python loop

for event in mytree:
  print event.myelem;

Can someone explain what exactly has changed here, why it was changed and how I can continue to have files that behave as people expect, without requiring extra steps to prevent leakage?

Thanks in advance.

Missing your description is the explanation on how the ‘void *’ is handled for I/O purposes. This is likely the crucial point in understand why the memory leaks.

setting TBranch::SetAutoDelete(kTrue);, which leads to a call to ~MyTreeElem(),

This was always the intended behavior and likely we ‘fix’ cases where the destructor was being called inadvertently. Instead the object is intended to be re-used.

suggests that using SetAutoDelete() is to be avoided.

Yes, as re-using the object rather than going through the cycle destruction-deallocation-construction-reallocation for each entry is usually a waste of CPU cycles (i.e. reuse of allocate memory is faster).

Cheers,
Philippe.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.