Memory leak using objects in a TFile

Hello, I have a trouble with TFile, which probably is my fault. I have a piece of code which needs to open a file, read a TTree contained in the file, do some operations and then close the file. I need to do this several times. I noticed that every time I read from the file a memory leak is generated if I previously closed the file or the tree. Here’s a simplified version of my code to illustrate the problem:

int main(int argc, char **argv) {

  TTree *tr;

  while (true) {
    TFile *f0 = (TFile*) gROOT->GetListOfFiles()->FindObject(argv[1]);
    bool alreadyopened = false;
    if (f0)
      alreadyopened = true;
    else
      //f0 = new TFile(argv[1]);
      f0 = TFile::Open(argv[1]);
    if (!f0)
      return false;

    TrkCalib *calib = new TrkCalib();

    tr = (TTree*) f0->Get("CalibTrk1");
    tr->SetBranchAddress("CalibTrk1", calib->GetPointerTo());
    tr->GetEntry(0);

    // Enable any of these to create a memory leak
    //f0->Delete("CalibTrk1");
    //tr->Delete();
    //f0->Close();
    //f0->Delete();

    calib->Delete();
  }

  return 0;

}

The file passed from the command line contains a tree named CalibTrk1, which contains a branch also named CalibTrk1. I read the first entry of this branch using the buffer object calib, and then if I close the file (so that at the successive iteration it is opened again) I have a memory leak at the successive GetEntry. It seems that closing the file doesn’t clear the memory used by it, since I see no decrease in memory consumption after closing (nor after deleting the file). Another face of the problem is that if I delete the TTree tr a memory leak shows up as well, regardless of deleting or closing the file.
I think that I’m doing something wrong, or maybe it is a problem related to the fact that the tree and the branch have the same name? My ROOT version is 5.20.
Thanks

replace your code by

[code]int main(int argc, char **argv) {

TTree *tr;

while (true) {
TFile f0 = (TFile) gROOT->GetListOfFiles()->FindObject(argv[1]);
bool alreadyopened = false;
if (f0)
alreadyopened = true;
else
f0 = TFile::Open(argv[1]);
if (!f0)
return false;

TrkCalib *calib = new TrkCalib();

tr = (TTree*) f0->Get("CalibTrk1");
tr->SetBranchAddress("CalibTrk1", calib->GetPointerTo());
tr->GetEntry(0);

// Enable any of these to create a memory leak
delete calib;
delete f0;

}

return 0;

}[/code]

Thanks for the prompt answer, Rene. I modified the code as you suggested (I added the two delete statements) but the memory leak is still there…

What happens if you comment the line
tr->GetEntry(0);
Do you still get the leak?
Did you check your destructors?

Rene

[quote=“brun”]What happens if you comment the line
tr->GetEntry(0);
Do you still get the leak?
Did you check your destructors?

Rene[/quote]
I removed the GetEntry, keeping the delete statements as you suggested before: the leak vanishes, or at least it is greatly reduced in size. To identify this residual leak I eliminated all the data read section, including the allocation and deletion of my TrkCalib object (so that my destructors, which by the way I have checked and seems fine, does not interfere at all), ending up with:

int main(int argc, char **argv) {

  TTree *tr;

  while (true) {
    TFile *f0 = (TFile*) gROOT->GetListOfFiles()->FindObject(argv[1]);
    bool alreadyopened = false;
    if (f0)
      alreadyopened = true;
    else
      //f0 = new TFile(argv[1]);
      f0 = TFile::Open(argv[1]);
    if (!f0)
      return false;

    //TrkCalib *calib = new TrkCalib();

    //tr = (TTree*) f0->Get("CalibTrk1");
    //tr->SetBranchAddress("CalibTrk1", calib->GetPointerTo());
    //tr->GetEntry(0);

    // Enable any of these to create a memory leak


    //f0->Delete("CalibTrk1");
    //tr->Delete();
    //f0->Close();
    //f0->Delete();

    //calib->Delete();
    //delete calib;
    delete f0;
  }

  return 0;

}

the residual leak is still there, meaning that simply opening and deleting a file still leaves some uncollected garbage around. But the major leak is due to the GetEntry statement.

Could you quantify the leak size?
Opening a ROOT file reads the medata data of the classes in the file. This metada is not deleted when deleting a file because in general the following files contain similar classes.

Rene

The leak size of the full code (with GetEntry) is about 25KB per iteration. The total number of bytes read (as returned by cout << tr->GetEntry(0)) is 221868.
Removing GetEntry and keeping only opening and deletion of the file is more difficult to evaluate the leak. On a single iteration I see no problem, but if I let the code run freely I see a slight increase in the memory consumption, something like a few KB every minute (sorry but I can’t be more precise right now).

OK this shows that you have a problem with the destructor(s) of the class TRkcalib or classes pointed by
this top level class.

Rene

[quote=“brun”]OK this shows that you have a problem with the destructor(s) of the class TRkcalib or classes pointed by
this top level class.

Rene[/quote]
I modified my test program to create only once the read buffer (eg., I moved the instantiation of TrkCalib outside the loop, so that the memory for it is instantiated only once). Now the TrkCalib destructor should play no role:

int main(int argc, char **argv) {

  TTree *tr;

  TrkCalib *calib = new TrkCalib();
  while (true) {
    TFile *f0 = (TFile*) gROOT->GetListOfFiles()->FindObject(argv[1]);
    bool alreadyopened = false;
    if (f0)
      alreadyopened = true;
    else
      //f0 = new TFile(argv[1]);
      f0 = TFile::Open(argv[1]);
    if (!f0)
      return false;


    tr = (TTree*) f0->Get("CalibTrk1");
    tr->SetBranchAddress("CalibTrk1", calib->GetPointerTo());
    tr->GetEntry(0);

    delete f0;
  }

  delete calib;
  return 0;

}

The leak is still there. Also, to check if my destructor works, I made a loop of allocation and deallocations:

int main(int argc, char **argv) {

  while (true) {
     TrkCalib *calib = new TrkCalib();
     delete calib;
  }
   return 0;
}

In this case no leak shows up. So I think it’s not a problem with my class.

When you call “new TrkCalib()”, you call only the default constructor. What about possible structures built later on, eg when calling GetEntry ?
I am afraid that I cannot give more details without looking at your class, but it looks pretty obvious that the leak is coming from problems with your class destructors.

Rene

I did some checks and I found that actually it is a problem with the root file I’m trying to open. If I try to open another file (containing the same data structure) the leak vanishes.
Thank you very much for your patience, Rene.