-
-
Notifications
You must be signed in to change notification settings - Fork 953
Have Styles and RenderStyles keep a weak reference to node #6017
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A think it would be a better solution to break those reference cycles. Widgets have a well-defined lifetime. Unneeded references can be removed when the widget has finished processing events.
Thanks for the feedback Will. By "break those reference cycles" do you mean to set for example Styles.node to None at suitable places? |
That would be one way, yes. |
Is my understanding correct that one lifecycle of a widget starts when it is mounted into the DOM tree and ends when it is removed from the DOM tree? There seems nothing that prevents a widget from being mounted again after it is removed from the DOM tree. It seems to me a By treating a On the other hand, I also agree that introducing weak references complicates the code and increases maintenance burden in the future. A key advantage of Python (and Java, C#, etc) is to free the developer from having to reason about the lifetime of objects, and let the runtime do it instead. Code constructs (such as weak reference) whose main purpose is to precisely define the lifetime adds complexity that is unrelated to the "feature" of the product. In the current case, the additional memory footprint caused by the reference cycle seems insignificant, and the user may call Thanks for checking! |
Background
j0shm4n reported in discord that adding and removing
TabPane
s repeatedly had memory usage grow until indeterministic time points. After some investigation, this appears to be due to the (added and then removed)TabPane
objects being kept alive by reference cycles until they are collected by CPython's generational garbage collector.To illustrate this, run the following script and press the key sequence
a a a a a a a a d d d d d d d d g
, which adds eight tabs, removes them all, and then generates a GraphViz file showing (part of) the reference graph of the livingTabPane
objects. The script makes use of theobjgraph
package, which may be installed bypip install objgraph
.Demo Script
The generated GraphViz file may be viewed by e.g. GraphViz Online. It looks like below:
The graph shows that one instance of
TabPane
is kept alive byFIFOCache
, and seven other instances are kept alive by reference cycles to (and from)Styles
andRenderStyles
members. The reference-cycled objects are collected automatically at certain times or whengc.collect(2)
is called to perform a full garbage collection. This may be verified by pressing2
beforeg
to see that the resulting object graph doesn't contain any "dangling" reference cycles.Change
While reference cycles are ultimately garbage collected and hence not really memory leaks, they do make the memory footprint larger than necessary if the program adds and removes widgets repeatedly. In addition, the cyclic reference between
DOMNode
andStyles
/RenderStyles
makes less obvious the ownership relation between the objects.This PR makes
Styles
andRenderStyles
object keep a weak (rather than strong) reference to the associatedDOMNode
. With this change, performing the same steps in the Demo Script generates the following object graph:Note that there is no more reference cycles involving
DOMNode
.Backward compatibility
A side effect of the change is that code that relies on the
Styles
orRenderStyles
object keeping alive the associated node would break. Such code must be updated to keep a strong reference to the node explicitly. An example isDOMNode._component_styles
, which held a sole reference to a "virtual node" for each component class. This is fixed by storing the virtual nodes (inDOMNode._component_styles_nodes
) instead. No other usage is identified in the test cases.Checklist
Updated documentationUpdated CHANGELOG.md (where appropriate)