Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix LayoutGrid leaks #25853

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 11, 2023
Merged

Fix LayoutGrid leaks #25853

merged 2 commits into from
May 11, 2023

Conversation

QuLogic
Copy link
Member

@QuLogic QuLogic commented May 10, 2023

PR summary

Internally, the LayoutGrid parent is only used in __init__, so doesn't need to be saved as an attribute. Externally, the constrained layout code needs the parent only for subfigure margins, but we have the parent gridspec available directly to use.

The LayoutGrid stores its children in a NumPy array, and saving the parent causes a reference cycle. Apparently having one reference in a C-level NumPy array causes Python to not realize it can garbage collect the cycle, causing all LayoutGrid and its children to leak.

Removing the parent attribute removes the cycle and fixes the leak.

Additionally, I've removed extraneous Variables that are not used for anything other than __repr__, but they are not actually filled with anything real there. In the example from #25819, this saves 16 Variables per iteration.

Fixes #25819

PR checklist

QuLogic added 2 commits May 10, 2023 05:21
While the above comment claims that these variables are useful, they are
not added to the solver nor added to any constraints, so they cannot
contain any useful information.

They are only really used for the `__repr__`, which can be changed to
the actually-used Variables instead.
Internally, the parent is only used in `__init__`, so doesn't need to be
saved as an attribute. Externally, the constrained layout code needs the
parent only for subfigure margins, but we have the parent gridspec
available directly to use.

The `LayoutGrid` stores its children in a NumPy array, and saving the
parent causes a reference cycle. Apparently having one reference in a
C-level NumPy array causes Python to not realize it can garbage collect
the cycle, causing all `LayoutGrid` and its children to leak.
@QuLogic QuLogic added topic: geometry manager LayoutEngine, Constrained layout, Tight layout Performance labels May 10, 2023
@QuLogic QuLogic added this to the v3.7.2 milestone May 10, 2023
Copy link
Contributor

@greglucas greglucas left a comment

Choose a reason for hiding this comment

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

Nice investigation! I verified that this works locally.

I'm wondering if leaks like this are worth adding to the test suite to make sure the reference cycles don't get reintroduced? We could depend on objgraph for this, or do some of our own verification asserting LayoutGrid isn't in gc.get_objects()

@jklymak
Copy link
Member

jklymak commented May 11, 2023

So was it the second paragraph that was the memory leak or the first paragraph also? Thanks!

@QuLogic
Copy link
Member Author

QuLogic commented May 11, 2023

The second paragraph is the leak; the first is just motivation for making the change this way instead of trying to break the cycle after layout manually.

@tacaswell tacaswell merged commit 369cdb5 into matplotlib:main May 11, 2023
meeseeksmachine pushed a commit to meeseeksmachine/matplotlib that referenced this pull request May 11, 2023
tacaswell added a commit that referenced this pull request May 11, 2023
…853-on-v3.7.x

Backport PR #25853 on branch v3.7.x (Fix LayoutGrid leaks)
@QuLogic QuLogic deleted the layoutgrid-leaks branch May 11, 2023 22:34
@jklymak
Copy link
Member

jklymak commented May 12, 2023

Thanks for fixing this - sorry I didn't have time to investigate, but suspect I'd never have figured this out. I'm happy with the fix, however, this does seem to me like an upstream bug? Is it really a thing that object a and b cannot reference each other and expect to be garbage collected if they are both removed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Performance topic: geometry manager LayoutEngine, Constrained layout, Tight layout
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Bug]: Memory leak in constrained_layout
5 participants