Skip to content

Conversation

@guitargeek
Copy link
Contributor

@guitargeek guitargeek commented Sep 4, 2023

The ROOT Python interfaces have many memory leaks, which is a major pain
point for people using it for long-running scripts in batch jobs.

One source of memory leaks was indentified to be the "heuristic memory
policy" of cppyy. This means that cppyy assumes that every non-const
pointer member function argument was interpreted as the object taking
ownership if the argument.

For examle, take the non-owning RooLinkedList container. It has a
RooLinkedList::Add(RooAbsArg *arg) method. ROOT wrongly assumes that
this means the RooLinkedList takes ownership of arg, and it drops the
ROOT overship. Nobody feels responsible for deleting the object
anymore, and there is a memory leak or arg.

That particular leak was reported in this forum post:
https://root-forum.cern.ch/t/memory-leak-in-fits/56249

Function parameters of type T * are very common in ROOT, and only
rarely do they imply ownership transfer. So changing the memory policy
to "strict" would surely fix also many other memory leaks that are not
reported so far. In fact, upstream cppyy doesn't even have this
heuristic memory policy anymore! So moving ROOT also to the strict
memory policy closes the gap between ROOT and cppyy.

The potential drawback of this change are crashes in usercode if memory
is not properly managed. But these problems should either be fixed by:

  • the user

  • dedicated pythonizations for these methods to manage shared
    ownership via Python reference counters (i.e., setting the parameter
    as an attribute of the object that the member function was called
    on)

This follows up on PR #4294, in particular it reverts guitargeek@3a12063.

@guitargeek guitargeek self-assigned this Sep 4, 2023
@guitargeek guitargeek changed the title [PyROOT] Set memory policy to strict [PyROOT] Set memory policy to "strict" Sep 4, 2023
@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch from 7b2132c to a2fe3d5 Compare September 4, 2023 13:06
guitargeek added a commit to guitargeek/roottest that referenced this pull request Sep 4, 2023
@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch from a2fe3d5 to f3aa24f Compare September 4, 2023 16:26
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
@root-project root-project deleted a comment from phsft-bot Sep 4, 2023
guitargeek added a commit to guitargeek/roottest that referenced this pull request Sep 4, 2023
@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch from f3aa24f to b147011 Compare September 4, 2023 22:48
@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch 2 times, most recently from b7f0c8f to 4eadd14 Compare September 5, 2023 08:40
guitargeek added a commit to guitargeek/roottest that referenced this pull request Jan 2, 2025
@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch from 819ccd4 to bb5d158 Compare January 2, 2025 08:36
@dpiparo dpiparo closed this Jan 2, 2025
@dpiparo dpiparo reopened this Jan 2, 2025
@dpiparo
Copy link
Member

dpiparo commented Jan 2, 2025

restarted all builds due to the issues with the server serving files for the tests.

guitargeek added a commit to guitargeek/roottest that referenced this pull request Jan 2, 2025
@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch from bb5d158 to 2141d65 Compare January 2, 2025 10:49
guitargeek added a commit to guitargeek/roottest that referenced this pull request Jan 2, 2025
@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch 2 times, most recently from d509a93 to 1833321 Compare January 3, 2025 09:33
@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch from 1833321 to 05f4dd0 Compare April 1, 2025 08:35
@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch from 05f4dd0 to 262a8b4 Compare April 12, 2025 07:56
@guitargeek guitargeek changed the title [PyROOT] Set memory policy to "strict" [Python] Set memory policy to "strict" Apr 12, 2025
Copy link
Member

@vepadulano vepadulano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm coming back to this old PR, I still agree with my previous statement that this is the right direction, but I see more potential misunderstandings and issues creeping up. I'm thinking about approaches we could employ to mitigate them, e.g. by running also these changes in CMSSW CI, or by thinking about more Pythonizations to include (e.g. in TList), or by thinking about if we can add more warnings for users in situations that lead to dangling pointers.

Also, a question that becomes more important now, are all of these changes still required in light of the recent and future improvements to the cppyy we use?

Comment on lines 16 to 24
c = ROOT.TList()
pylist = []
for _ in range(self.num_elems):
o = ROOT.TObject()
# Prevent immediate deletion of C++ TObjects
ROOT.SetOwnership(o, False)
c.Add(o)
pylist.append(o)

# To prevent memory leaks
c._owned_objects = pylist
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These few lines seem overly complicated: do we need both the SetOwnership and the extra attribute lifeline? Will this surface in real user code or is it a contrived example just for the test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, we don't need to release the ownership here. The lifeline is enough.

Comment on lines 22 to 25
# A TList is by default non-owning. To make sure that the objects live
# long enough, we attach then as an attribute of the output list, such
# that the Python reference counter doesn't hit zero.
sc.owning_pylist = sc_pylist
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds counter-intuitive for a Python user, shall we perhaps discuss making it part of a TList pythonization?

Comment on lines 98 to 101
**Note:** You can change back to the old policy by calling
`ROOT.SetMemoryPolicy(ROOT.kMemoryHeuristics)` after importing ROOT, but this
should be only used for debugging purposes and this function might be removed
in the future!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make this perhaps explicit at usage by also raising a warning from the function when calling it.

@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch from 262a8b4 to d0e71c7 Compare January 7, 2026 15:29
@guitargeek
Copy link
Contributor Author

Hi @vepadulano, thanks for coming back to this! Let's try to make progress then and merge this soon 🙂

I'm coming back to this old PR, I still agree with my previous statement that this is the right direction, but I see more potential misunderstandings and issues creeping up. I'm thinking about approaches we could employ to mitigate them, e.g. by running also these changes in CMSSW CI, or by thinking about more Pythonizations to include (e.g. in TList), or by thinking about if we can add more warnings for users in situations that lead to dangling pointers.

Good points. I'll address them once we see where we stand with a fresh CI run.

Also, a question that becomes more important now, are all of these changes still required in light of the recent and future improvements to the cppyy we use?

The upcoming cppyy rebase on libinterop is more happening in the backend, and we have no frontend development lined up that change the behavior of these memory heuristics. But what we should consider is that it's better to not change both backend and frontend too much in one release, because it makes potential regressions harder to fix. If we assume that the backend upgrade will hit in 6.42, then this means the best time to update the memory policy is now for 6.40. Otherwise, it would be wiser to wait for 6.44, but this is quite far away.

@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch 11 times, most recently from 630a6ca to 8c11bac Compare January 8, 2026 10:23
The ROOT Python interfaces have many memory leaks, which is a major pain
point for people using it for long-running scripts in batch jobs.

One source of memory leaks was indentified to be the "heuristic memory
policy" of cppyy. This means that cppyy assumes that every non-const
pointer member function argument was interpreted as the object taking
ownership if the argument.

For examle, take the non-owning RooLinkedList container. It has a
`RooLinkedList::Add(RooAbsArg *arg)` method. ROOT wrongly assumes that
this means the RooLinkedList takes ownership of arg, and it drops the
ROOT overship. Nobody feels responsible for deleting the object
anymore, and there is a memory leak or `arg`.

That particular leak was reported in this forum post:
https://root-forum.cern.ch/t/memory-leak-in-fits/56249

Function parameters of type `T *` are very common in ROOT, and only
rarely do they imply ownership transfer. So changing the memory policy
to "strict" would surely fix also many other memory leaks that are not
reported so far. In fact, upstream cppyy doesn't even have this
heuristic memory policy anymore! So moving ROOT also to the strict
memory policy closes the gap between ROOT and cppyy.

The potential drawback of this change are crashes in usercode if memory
is not properly managed. But these problems should either be fixed by:

  * the user

  * dedicated pythonizations for these methods to manage shared
    ownership via Python reference counters (i.e., setting the parameter
    as an attribute of the object that the member function was called
    on)

This follows up on PR root-project#4294, in particular it reverts 3a12063.
@guitargeek guitargeek force-pushed the pyroot_memory_policy_1 branch from 8c11bac to 18dd852 Compare January 8, 2026 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants