Skip to content

Conversation

@mjkkirschner
Copy link
Member

@mjkkirschner mjkkirschner commented May 18, 2020

Purpose

Before these changes running just DynamoCoreWPFTests.dll consumed 6GB of ram on my machine.
After these changes memory use is approximately 1GB and forcing a garbage collection brings it down to 300~ megs.

There are likely a bunch of extraneous changes here and also - these changes are just WIP - they need to be cleaned up.

But here are some notes on what I found:

  • NodeViews leak, completely, every single customized node view was not being GC'd - these hold all sorts of things and some of them even have references to DynamoView/DynamoModel so then EVERYTHING leaks as a consequence.
  • DynamoView.Dispose was never called - as a consequence:
  • ViewExtensions never had their Dispose method called, only their shutdown method ... I removed the dispose code because some extensions already called this from shutdown and theres some caught exceptions being thrown - this is a TODO
  • many retention paths were holding onto the underlying canvases in Helix.

  • WorkspaceView and DynamoView left a bunch of handlers dangling.

  • Static references under some circumstances do not get GC'd as they live in a global object the CLR builds, these might not get cleaned up during tests so I've set them null during cleanup. Need to check if these are required.

  • I noticed some very strange behavior with readonly fields not being cleaned up - research seems split on if these can be a problem, need to check if this can be reverted.

  • Because of the design of ViewLoadedParams passed to ViewExtensions it's difficult to cleanup the handlers that are created there, I ended up storing a reference to a single params and using it for all view extensions that are loaded at Dynamo startup - I think this is okay. - though likely we could solve this more elegantly - it also means that view extensions loaded after startup, still leak, but thats an edge case.

  • InforBubble (error view) leaks - made some improvments.

  • removed Suppress GC on Defaultwatch3dViewModel - need to check if this was required - I don't see why we would want this though - we don't implement a finalizer.

  • removed a bunch of anonymous event handlers which caused leaks, while these are great and make code more succinct and readable, I think we should disallow their use unless the author memory profiles them or has a convincing argument why they will not introduce a leak... usually they are fine if they do not reference any external objects, IE an event on the same object on which the handler is defined.

  • CustomNodeWorkspaceModels leak - these were never cleaned up because we only clean the workspaces in the DynamoModel.Workspaces collection - but custom node workspaces are not in that collection unless they are actually open in a tab. When the customNodeManager is cleaned up now, these are uninitialized by the CNM which cleans them up.

  • Added additional dispose steps to HelixWatch3dViewModel as it did not call the base class dispose, and it did not clear all collections - not sure if this is required, but calling the base class is.

Declarations

Check these if you believe they are true

  • The codebase is in a better state after this PR
  • Is documented according to the standards
  • The level of testing this PR includes is appropriate
  • User facing strings, if any, are extracted into *.resx files
  • All tests pass using the self-service CI.
  • Snapshot of UI changes, if any.
  • Changes to the API follow Semantic Versioning and are documented in the API Changes document.

private bool isPSSCalledOnViewModelNoCancel = false;
private readonly DispatcherTimer _workspaceResizeTimer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 0, 0, 500), IsEnabled = false };

private ReadyParams sharedExtensionParams;
Copy link
Member Author

Choose a reason for hiding this comment

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

adding a shared viewLoadedParams object for all viewExtensions so we can call dispose on it later.

guard notification extension from double dispose issue
add watchview cleanup to watch3dview unload instead of calling it from DynamoWindow close
…r test suite.

Still not sure it's actually appropriate...
change name of shared params
@mjkkirschner mjkkirschner changed the title WIP Reduce memory leak in tests Reduce memory leak in tests May 21, 2020
@mjkkirschner mjkkirschner added PTAL Please Take A Look 👀 and removed WIP labels May 21, 2020
Copy link
Collaborator

@mmisol mmisol left a comment

Choose a reason for hiding this comment

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

Left some comments. Looks good for the most part but the TODOs worry me a little

{
//TODO we should be careful with this, viewExtensions also have their shutdown method called
//when the dynamoWindow closes so it's possible there are some double dispose issues for ours or
//external extensions. Looking for feedback here!
Copy link
Collaborator

Choose a reason for hiding this comment

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

For the extensions I have seen we usually call Dispose from Shutdown. I'm not sure there is any documentation established around that pattern though.

You might want to check in which cases, DynamoView.Dispose was being called directly, and maybe use another DisposeAllButExtensions in the context of WindowClosed. In any case this is not so bad as any error will be catched.

Copy link
Member Author

Choose a reason for hiding this comment

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

DynamoView.Dispose was never called!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could integrators be using it? If not, maybe we can remove the part where we Dispose extensions, just to keep things the way they were before.

Copy link
Member Author

@mjkkirschner mjkkirschner May 21, 2020

Choose a reason for hiding this comment

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

that is a possible solution, the safest thing to do for now is to leave out the ViewExtension dispose calls and properly document the relationship between shutdown and dispose. - BUT that may leave user viewExtensions hanging around with being disposed - unlikely to cause issues except in tests I suppose.


Open(@"UI\CoreUINodes.dyn");

//TODO why did this pass before?
Copy link
Collaborator

Choose a reason for hiding this comment

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

Anything pending to investigate here?

Copy link
Member Author

Choose a reason for hiding this comment

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

nope, this seems fine, must have temporarily broken something as I was in progress.

@mjkkirschner
Copy link
Member Author

https://master-15.jenkins.autodesk.com/view/DYN/job/DYN-DevCI_Self_Service_net47/1/consoleText - only failing tests are the analytics ones that Tibi has fixed in his latest prs.

@aparajit-pratap @mmisol what do you think we should do for the ViewExtension disposal issue - the previous state was that Dispose was never called on ViewExtensions - now Dispose is called when the DynamoView window is closed - the calls are in addition to the Shutdown calls already made right beforehand.

otherwise this should be complete - believe I addressed all comments.

Copy link
Contributor

@aparajit-pratap aparajit-pratap left a comment

Choose a reason for hiding this comment

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

@mjkkirschner still not quite sure why you're explicitly calling DynamoView's Dispose method. Also, I think we can Dispose view extensions directly in the WindowClosed method for DynamoView itself, no?

Lastly, I had a question about FilteredResult in SearchViewModel and making that an ObservableCollection in the future.

{

Dispose();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

If Dispose is called after Shutdown in DynamoView.WindowClosed, what's the need call it here?

Copy link
Member Author

@mjkkirschner mjkkirschner May 21, 2020

Choose a reason for hiding this comment

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

@aparajit-pratap - this depends on if we leave the ViewExtension disposal calls in or not - @mmisol was suggesting maybe leaving them out to keep the behavior as it was before this PR - if we do that, I can leave this.

If we keep the disposal calls after shutdown, I can remove this.

Copy link
Collaborator

@mmisol mmisol left a comment

Choose a reason for hiding this comment

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

@mjkkirschner I would say to leave this as it was with regards to disposing extensions is the ideal but it's not something absolutely needed as we are still catching any errors that may occur from double disposals. Also, like we mentioned, it's more like a documentation issue that we could take separately.

Copy link
Contributor

@aparajit-pratap aparajit-pratap left a comment

Choose a reason for hiding this comment

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

Taking a look further I see that the ViewExtensionManager holds a reference to a collection of all ViewExtension objects and its Dispose method merely clears that collection rather than disposing each view extension. See


I think it should own the disposal of each view extension and then DynamoView can simply call Dispose on its viewExtensionManager property/field.

@mjkkirschner
Copy link
Member Author

mjkkirschner commented May 21, 2020

@aparajit-pratap I'll take a look at moving the disposal into the ViewExtensionsManager if you prefer that approach?

@mjkkirschner
Copy link
Member Author

@aparajit-pratap fixed, and also, this was a good catch it freed up even more memory in my test suite!

@mjkkirschner mjkkirschner merged commit 65b703d into DynamoDS:master May 21, 2020
@QilongTang QilongTang removed the PTAL Please Take A Look 👀 label Aug 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants