Question on memory leak

Hi Rooters,
I was trying to fix some memory leaks in my analysis and ran into some trouble.

For the analysis I get several waveforms (TH1F) which are plotted in a TCanvas and stored in a TFolder.
The problem is that if I delete the TCanvas the TH1F stays in the memory as it is not owned by the TCanvas.
Is there a way to delete a TH1F which is drawn in a TCanvas by deleting the TCanvas?

In the attached code I add all the TCanvas to the TFolder and push them into a vector. After writing the TFolder to a TFile I use the vector to loop and delete all the TCanvas canvas. The same is done for the vector of TH1F which is fine in the example but get out of hand if i start ploting severals TObjects in one TCanvas.
Is there a way to delete a TCanvas which is added to the TFolder?

Thanks for your help in advance.

Cheers,
Christian


//C++
#include <iostream>
#include <sstream>

//ROOT
#include "TROOT.h"
#include "TH1F.h"
#include "TCanvas.h"
#include "TFile.h"
#include "TFolder.h"

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

  TH1F* th1;
  TFolder* tf_th1 = new TFolder("tfolder","tfolder");

  std::vector<TCanvas*> v_c1;
  std::vector<TH1F*> v_th1;

  for(int i=0;i<4000;i++){
    std::cout << i << "\r";
    std::cout.flush();


    std::stringstream ss_th1;
    ss_th1 << "Name_" << i ;

    th1 = new TH1F(ss_th1.str().c_str(),ss_th1.str().c_str(),50000,0,50000);
    
    for(int j=0;j<50000;j++){
      th1->Fill(j);
    }


    std::stringstream ss_c;
    ss_c << "c_" << i ;
    TCanvas* c1 = new TCanvas(ss_c.str().c_str(),ss_c.str().c_str(),800,1000);
    th1->Draw();

    tf_th1->Add(c1);    
    v_c1.push_back(c1);
    v_th1.push_back(th1);
  }

  std::cout << "here the memory is at a high level" << std::endl;
  std::cout << "Press ENTER to continue....." << std::endl;
  std::cin.ignore(1);



  // create a root file to store the TFolder containing all TCanvas
  TFile* tFile = new TFile("memoryTest.root","RECREATE");
  tFile->cd();
  tf_th1->Write();
  tFile->Close();
  delete tf_th1;    // delete the TFolder 

  std::cout << "data is stored, memory is at a high level" << std::endl;
  std::cout << "Press ENTER to continue....." << std::endl;
  std::cin.ignore(1);

  for(unsigned int i=v_c1.size()-1;i>0;i--){
    delete v_c1.at(i);
  }
  std::cout << "size of v_c1 " << v_c1.size();
  v_c1.clear();
  std::cout << " " << v_c1.size() << std::endl;

  std::cout << "removed vector of TCanvas, memory is at a high level" << std::endl;
  std::cout << "TCanvas does not own the TH1 in it thus they are not deleted" << std::endl;
  std::cout << "Press ENTER to continue....." << std::endl;
  std::cin.ignore(1);

  for(unsigned int i=v_th1.size()-1;i>0;i--){
    delete v_th1.at(i);
  }
  std::cout << "size of v_th1 " << v_th1.size();
  v_th1.clear();
  std::cout << " " << v_th1.size() << std::endl;

  std::cout << "removed vector of TH1, memory is back to low level" << std::endl;
  std::cout << "Press ENTER to continue....." << std::endl;
  std::cin.ignore(1);

  delete tFile;

  std::cout << "TFile pointer finally also removed" << std::endl;
  std::cout << "memory is at back to low level" << std::endl;
  std::cout << "Press ENTER to continue....." << std::endl;
  std::cin.ignore(1);


}

Hi Rooters,
just to break down the problem:
Is there a way to delete all pointers to TH1F, TF1, TLine,… drawn in a TCanvas when the TCanvas is deleted?
Is there a way to delete all TCanvas added to a TFolder when the TFolder is deleted?

I couldn’t figure out a way to get access to the Objects added to the TFolder or drawn in the TCanvas.

Thanks for the feedback

Christian

Quote from documentation of kCanDelete-bit:

Hi tc3t,

thanks for your answer. It helped a lot.
Just for completeness I changed the previously attached code using the kCanDelete bit.

Cheers,
Christian

//C++
#include <iostream>
#include <sstream>

//ROOT
#include "TROOT.h"
#include "TH1F.h"
#include "TCanvas.h"
#include "TFile.h"
#include "TFolder.h"
#include "TList.h"

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

  TFolder* tf_th1 = new TFolder("tfolder","tfolder");
  tf_th1->SetOwner(true);      // make the TFolder of all TObjects added to it

  for(int i=0;i<4000;i++){
    std::cout << i << "\r";
    std::cout.flush();


    std::stringstream ss_th1;
    ss_th1 << "Name_" << i ;

    TH1F* th1 = new TH1F(ss_th1.str().c_str(),ss_th1.str().c_str(),50000,0,50000);
    
    for(int j=0;j<50000;j++){
      th1->Fill(j);
    }

    std::stringstream ss_c;
    ss_c << "c_" << i ;
    TCanvas* c1 = new TCanvas(ss_c.str().c_str(),ss_c.str().c_str(),800,1000);

    th1->Draw();    // Draw the th1
    th1->SetBit(kCanDelete);  // Set the kCanDelete bit to true
    // std::cout << i << " " << th1->TestBit(kCanDelete) << std::endl;

    tf_th1->Add(c1);

  }

  std::cout << "here the memory is at a high level" << std::endl;
  std::cout << "Press ENTER to continue....." << std::endl;
  std::cin.ignore(1);

  // create a root file to store the TFolder containing all TCanvas
  TFile* tFile = new TFile("memoryTest.root","RECREATE");
  tFile->cd();
  tf_th1->Write();   // write the TFolder to TFile
  //tf_th1->ls();
  tf_th1->Clear();   // clear the TFolder, removes all TCanvas as the TFolder owns them (see line 16)
  tFile->Close();    // close the TFile
  delete tf_th1;    // delete the TFolder 
  delete tFile;     // delete the TFile

  std::cout << "here the memory is back to the original level" << std::endl;
  std::cout << "Press ENTER to continue....." << std::endl;
  std::cin.ignore(1);
}