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
8242621: TabPane: Memory leak when switching skin #318
Conversation
👋 Welcome back arapte! A progress list of the required criteria for merging this PR into |
Webrevs
|
no review yet, just a couple of quick comments from my experience on recent cleanup of skins:
yeah, you don't get away with not writing tests *good-humored-grinning |
/reviewers 2 |
@kevinrushforth |
|
||
getSkinnable().getTabs().removeListener(weakTabsListener); | ||
|
||
getChildren().remove(tabHeaderArea); |
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.
As mentioned offline, can you check whether removing tabHeaderArea
is necessary?
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.
good question :) Just have run into a similar case while working on cleaning up TextFieldSkin.
My current understanding is ... depends ;)
As tests show, some children keep the skin alive, others don't. Which must imply that the first somehow keep a strong reference to the skin, the latter (nor any of its children) don't. An example for the former is tabHeaderArea, an example for the latter is tabContentRegion. Was puzzled about how to distinguish the one from the other (which boils down to finding that strong reference) - until I (faintly ;) remembered that inner classes have an implicit reference to the enclosing instance: TabHeaderArea is an inner class with an implicit reference to the enclosing skin, while TabContentRegion is static nested. As long as the former reside in the scenegraph, it makes the skin leaky.
So we have to watch out for
- explicit strong references from any node (down the hierarchy) to the skin
- implicit strong reference to the enclosing skin class.
Both groups have to be removed in dispose to prevent memory leaks.
Even if not obviously leaking, children pile up when switching skin: most skins add to children, rarely set. We started a discussion of how to handle those that add Spinner misbehavior - not yet decided (personally, I didn't do anything about them in the cleanups, deferred to later ;) From today's knowledge, I would suggest to explicitly remove all direct children that the skin added to the control.
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.
As mentioned offline, can you check whether removing
tabHeaderArea
is necessary?
Both groups have to be removed in dispose to prevent memory leaks.
The list returned by SkinBase.getChildren()
is actually the same list as Parent.getChildren()
.
SkinBase()
constructor gets the reference of Parent.getChildren()
and store it's reference in the class member named children
. [see here]
So, Skin and Parent share the same list of children. So when a skin is being dispose()
ed, it should remove any children that it added to the list. Otherwise, those children continue to be part of scenegraph.
Please check the newly added test SkinCleanupTest.testChildrenCountAfterSkinIsReplaced().
This does not stop a skin object from getting GCed, but the Children that skin adds don't get removed from Scenegraph. Looking at this behavior, it seems like this should be done for cleanup of all Skins.
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.
quick nit-pick:
This does not stop a skin object from getting GCed,
actually, it does ... if it keeps a strong reference to the skin :)
modules/javafx.controls/src/main/java/javafx/scene/control/skin/TabPaneSkin.java
Outdated
Show resolved
Hide resolved
modules/javafx.controls/src/main/java/javafx/scene/control/skin/TabPaneSkin.java
Outdated
Show resolved
Hide resolved
quick question: is this still in the lane for fx16? |
Yes, I will update PR later this week. |
cool, looking forward to it :) |
I'm still in holiday mood - and hope you are all well :) - will review later today, have to update my notes to the latest changes. |
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.
Fix looks good, verified tests failing (except one, commented) before and passing after the fix.
Left a couple of comments inline. Plus there's an @ignore related to the issue in TabPaneTest (testNPEOnSwitchSkinAndChangeSelection
) that should be removed.
modules/javafx.controls/src/main/java/javafx/scene/control/skin/TabPaneSkin.java
Outdated
Show resolved
Hide resolved
modules/javafx.controls/src/main/java/javafx/scene/control/skin/TabPaneSkin.java
Outdated
Show resolved
Hide resolved
modules/javafx.controls/src/main/java/javafx/scene/control/skin/TabPaneSkin.java
Outdated
Show resolved
Hide resolved
modules/javafx.controls/src/main/java/javafx/scene/control/skin/TabPaneSkin.java
Outdated
Show resolved
Hide resolved
modules/javafx.controls/src/test/java/test/javafx/scene/control/skin/SkinCleanupTest.java
Outdated
Show resolved
Hide resolved
modules/javafx.controls/src/test/java/test/javafx/scene/control/skin/SkinCleanupTest.java
Outdated
Show resolved
Hide resolved
modules/javafx.controls/src/test/java/test/javafx/scene/control/skin/SkinCleanupTest.java
Outdated
Show resolved
Hide resolved
modules/javafx.controls/src/test/java/test/javafx/scene/control/skin/SkinCleanupTest.java
Outdated
Show resolved
Hide resolved
Thanks for the review. I have updated the PR according to all comments. Please take a look. :) |
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.
looks fine now :)
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.
Looks good.
@arapte This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 1 new commit pushed to the
Please see this link for an up-to-date comparison between the source branch of this pull request and the ➡️ To integrate this PR with the above commit message to the |
/integrate |
@arapte Since your change was applied there has been 1 commit pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit c197b62. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
TabPaneSkin
installs some listeners that are not removed whenTabPaneSkin
is changed.The fix converts listeners to WeakListeners and also removes them on dispose.
There is a NPE check change needed in
isHosrizontal()
. Without this check it causes NPE if pulse is in progress while TabPaneSkin is getting disposed.SkinMemoryLeakTest
already had a test which only needed to be enabled.Test fails before and passes after this change.
Progress
Issue
Reviewers
Download
$ git fetch https://git.openjdk.java.net/jfx pull/318/head:pull/318
$ git checkout pull/318