Skip to content
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

Fixed several reference cycles to prevent memory leaks. #2967

Merged
merged 5 commits into from
Mar 4, 2017

Conversation

Cykooz
Copy link
Contributor

@Cykooz Cykooz commented Mar 1, 2017

I have corrected three places in the code where appear reference cycles due to closure.
Also I have added a simple test for detect memory leaks after application closing.


def get_gc_count(self):
last_collected = 0
while True:
Copy link
Member

Choose a reason for hiding this comment

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

Is this actually guaranteed to converge? The possibility here for an infinite while loop concerns me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know :) May be it possible in some cases with threads that works simultaneously with this test. Then GC will collects different count of objects in each step of cycle.

@@ -933,7 +933,7 @@ def register(permission=permission, renderer=renderer):
if not exception_only and isexc:
derived_view = runtime_exc_view(derived_view, derived_exc_view)

derived_view.__discriminator__ = lambda *arg: discriminator
derived_view.__discriminator__ = lambda *arg: view_intr.discriminator
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I see the cycle here, can you explain?

Copy link
Contributor Author

@Cykooz Cykooz Mar 2, 2017

Choose a reason for hiding this comment

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

This reference cycle prevents the removal of instance of Сonfigurator after a WSGI application has created.
If skip some steps then cycle looks like that:
discriminator (always instance of Deferred in this place) => discrim_func => Configurator (as self in closure) => Registry => Introspector => default_exceptionrespone_view => __discrimantor__ => lambda => discriminator.
My fix removes the instance of Deferred from closure. Instead it, closure contains now instance of Introspectable with undeferred value of discriminator.

For details, see the attached image with graph of backrefs for instance of Configurator after running this code:

app = Configurator().make_wsgi_app()
gc.collect()

cycle1

Copy link
Member

Choose a reason for hiding this comment

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

Would this be solved if we fixed the Deferred to release the closure when it is resolved? Imagine self.func = None in

def value(self):

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it was a first solution that I have tried when investigate this reference cycle. It works too.

Copy link
Member

Choose a reason for hiding this comment

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

I think I'd prefer that solution because it feels less error prone. The current fix is quite subtle.

@@ -42,7 +42,7 @@ def excview_tween(request):
provides = providedBy(exc)
try:
response = _call_view(
registry,
request.registry,
Copy link
Member

Choose a reason for hiding this comment

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

Again, I guess the issue here is that the registry eventually retains the excview_tween and the excview_tween retains the registry via the closure, but this is all cleaned up via GC in general and likely occurs in many other parts of code.

What practical problem is this solving that I should accept this change? Is the submitted integration test going to prevent me from writing this code again later?

Copy link
Member

Choose a reason for hiding this comment

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

Sorry but just to clarify I think the integration test and the weakref fixes on the WeakOrderedSet are good. I'm just nitpicking these other two changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm... Yes, I agree with you - this closure has not matter and do not prevents GC to resolve a cycle "registry-tween-registry" .
I could remove this change, if you think that it is absolutely useless.

@mmerickel
Copy link
Member

Thank you @Cykooz. I'm leaving this open for now until we backport these fixes to 1.7 and 1.8 as well.

@mmerickel mmerickel merged commit 78ac16c into Pylons:master Mar 4, 2017
mmerickel added a commit that referenced this pull request Mar 4, 2017
mmerickel pushed a commit to mmerickel/pyramid that referenced this pull request Mar 4, 2017
…test for detect memory leaks after application closing.

Backport Pylons#2967 to the 1.7-branch
mmerickel pushed a commit to mmerickel/pyramid that referenced this pull request Mar 4, 2017
…test for detect memory leaks after application closing.

Backport Pylons#2967 to 1.8-branch.
mmerickel pushed a commit to mmerickel/pyramid that referenced this pull request Mar 4, 2017
…test for detect memory leaks after application closing.

Backport Pylons#2967 to the 1.7-branch
mmerickel pushed a commit to mmerickel/pyramid that referenced this pull request Mar 4, 2017
…test for detect memory leaks after application closing.

Backport Pylons#2967 to 1.8-branch.
mmerickel pushed a commit to mmerickel/pyramid that referenced this pull request Mar 4, 2017
…test for detect memory leaks after application closing.

Backport Pylons#2967 to the 1.7-branch
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.

3 participants