Memory leak in TSocket communciation with pyROOT

Hi,
I’m working on a DAQ system for my collaboration. In particular, what I’m trying to right now is to use a C++ program that spies on the data stream and fills the TH1F histograms, and opens a TServerSocket to communicate, so the histograms can be retrieved.

To retrieve the histogram, I use a simple pyROOT based class:

import time
import ROOT

import time

ROOT.gROOT.SetBatch(True)

class ROOTClient:
    def __init__(self, host='localhost', port=6060):
        self.host = host
        self.port = port
        self.socket = None
        self.histograms = []
        self.waves = []

    def connect(self):
        self.socket = ROOT.TSocket(self.host, self.port)
        if not self.socket.IsValid():
            raise ConnectionError(f"Error connecting to {self.host}:{self.port}")

    def disconnect(self):
        self.socket.Close()

    def send(self, msg):
        if self.socket.Send(msg) <= 0:
            return False
        return

    def receive(self):
        msg = ROOT.TMessage()

        if self.socket.Recv(msg) <= 0:
            return False
        
        if msg.GetClass().GetName() in ["TH1F"]:
            obj = msg.ReadObject(msg.GetClass())
            return obj
        
        return False
    
    def clear_arrays(self):
        if( len(self.histograms) == 0 ): return
        for obj in self.histograms:
            print(f"Deleting {obj.GetName()}")
            obj.Delete()
            del obj
        self.histograms = []
        if( len(self.waves) == 0 ): return
        for obj in self.waves:
            print(f"Deleting {obj.GetName()}")
            obj.Delete()
            del obj
        self.waves = []
    
    def collect(self):
        self.connect()
        self.send("get")
        self.clear_arrays()
        while True:
            obj = self.receive()
            if( obj == False ): break
            print( "Received object", obj.GetName() )
            if "Wave" in obj.GetName():
                self.waves.append(obj)
            else:
                self.histograms.append(obj)
        self.disconnect()

    def stop(self):
        self.connect()
        self.send("stop")

The histograms are retrieved from the TSocket each second by calling the collect(self) function. Everything works fine, but it seems there is a memory leak present somewhere. After about 2 hours of acquisition, the program crashes due to insufficient memory (more than 16GB is used at this point).

I tried to debug the code with the memory_profiler library, and it seems that the memory from the objects I get from the TSocket is never freed, but it increases at each collect(self) call. One of the attempts was to implement the clear_arrays(self) function, but the memory seems not to be freed anyway.

Is there something I’m overlooking, or some mistake I’m doing? I really tried everything, but it seems that there is nothing that works and each time the object is received, it is never removed.

Thank you for your help,
Jakub

_ROOT Version: 6.32/02
_Platform: Linux

Hello @skowroik,

thanks for bringing this issue to our attention!
I am not sure whether there actually is a memory leak or not in your case. I add in the loop @pcanal as he might have a better understanding of your situation.

Cheers,
Monica

There is no obvious leak in the code.

the objects I get from the TSocket is never freed,

From my reading of your code, they should be freed from:

        for obj in self.histograms:
            print(f"Deleting {obj.GetName()}")
            obj.Delete()
            del obj

are those lines executed? (also only of the 2 delete should be needed). @vpadulan any other ideas?

Yes, these lines are executed at each loop. In the meantime, I’ve managed to overcome the problem by modifying

if msg.GetClass().GetName() in ["TH1F"]:
            obj = msg.ReadObject(msg.GetClass())
            return obj

in

if msg.GetClass().GetName() in ["TH1F"]:
            obj = msg.ReadObject(msg.GetClass())
            msg.Delete()
            return obj

For some reason, TMessage is never freed? Might there be an another workaround for this? It does not seem right that at each loop I have to explicitly delete all the objects to free the memory. Could it be something with the ownership of the objects when they are send through the TSocket?