On Sep 10, 2014, at 4:11 AM, Li Tianqing <jaze...@163.com> wrote:

> After some research, i find the reason for the cycle reference. In closure, 
> the _fix_paswords.func_closre reference the _fix_passwords. So the
> cycle reference happened. 
> And  in https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf page 5, 
> it says that 
> We observe that Python implementations with distinct GCs behave differently: 
> CPython does not even try to get the order of finalizers right, and
> simply puts uncollectable objects into the global list of garbage for the 
> developer to deal with manually.
> So the gc can not handle cycle reference, then the memory leak happened. 

An object is only uncollectable in Python if it has a __del__ method and is 
part of an unreachable cycle.  Based on a grep, there is only one class in 
oslo.messaging that has a __del__ and that is ConnectionContext in 
_drivers/amqp.py.   

Removing the __del__ method from this object would be the best approach.    
There’s nothing wrong with reference cycles in Python as long as we don’t use 
__del__, which IMHO has no legitimate use case.   Additionally, it is very 
difficult to be 100% vigilant against reference cycles reappearing in many 
cases, but it is extremely simple to be 100% vigilant about disallowing __del__.

In this case it appears to be a “safety” in case someone uses the 
ConnectionContext object outside of being a context manager.  I’d fix that and 
require that it be used as a context manager only.   Guessing that the user is 
going to mis-use an object and provide __del__ to protect the user is a bad 
idea - and if you genuinely need this pattern, you use a weakref callback 
instead:

import weakref


class MyCleanupThing(object):
    def __init__(self):
        self.connection = connection = "Some Connection"
        self._ref = weakref.ref(
            # key: the weakref callback *cannot* refer to self
            self, lambda ref: MyCleanupThing._cleanup(connection))

    @staticmethod
    def _cleanup(connection):
        print("Cleaning up %s!" % connection)


mc = MyCleanupThing()

print("about to clean up...")
del mc
print("should be clean!")

output:

about to clean up...
Cleaning up Some Connection!
should be clean!


 


> 
> If there is something wrong, please fix it. Thanks
> 
> --
> Best
>     Li Tianqing
> 
> 在 2014-09-10 11:52:28,"Li Tianqing" <jaze...@163.com> 写道:
> Hello,
>     I use backdoor of eventlet to enable gc.DEBUG_LEAK, and after wait a few 
> minutes, i can sure that there will some objects that can not be collected by 
> gc.collect in gc.garbage. 
> Those looks like this (catched in ceilometer-collector)
> 
> ['_context_auth_token', 'auth_token', 'new_pass'],
>  (<cell at 0x363bc58: list object at 0x361c170>,
>   <cell at 0x363bda8: function object at 0x361a5f0>),
>  <function _fix_passwords at 0x361a5f0>,
>  <cell at 0x363bde0: list object at 0x363c680>,
>  <cell at 0x363bd70: function object at 0x361a668>,
>  ['_context_auth_token', 'auth_token', 'new_pass'],
>  (<cell at 0x363bde0: list object at 0x363c680>,
>   <cell at 0x363bd70: function object at 0x361a668>),
>  <function _fix_passwords at 0x361a668>,
>  <cell at 0x363bf30: list object at 0x361b680>,
>  <cell at 0x363e168: function object at 0x361a758>,
>  ['_context_auth_token', 'auth_token', 'new_pass'],
>  (<cell at 0x363bf30: list object at 0x361b680>,
>   <cell at 0x363e168: function object at 0x361a758>),
>  <function _fix_passwords at 0x361a758>,
>  <cell at 0x363e1a0: list object at 0x363cdd0>,
>  <cell at 0x363e130: function object at 0x361a7d0>,
> 
> and i suspect those code in oslo.messaging
> 
> def _safe_log(log_func, msg, msg_data):
>     """Sanitizes the msg_data field before logging."""
>     SANITIZE = ['_context_auth_token', 'auth_token', 'new_pass']
> 
>     def _fix_passwords(d):
>         """Sanitizes the password fields in the dictionary."""
>         for k in d.iterkeys():
>             if k.lower().find('password') != -1:
>                 d[k] = '<SANITIZED>'
>             elif k.lower() in SANITIZE:
>                 d[k] = '<SANITIZED>'
>             elif isinstance(d[k], dict):
>                 _fix_passwords(d[k])
>         return d
> 
>     return log_func(msg, _fix_passwords(copy.deepcopy(msg_data)))
> 
> i can resolve this problem by add _fix_passwords = None before _safe_log 
> returns.
> 
> But i do not really understand why this problem happened, and in depth why 
> the gc can not collect those object. Although i can make those uncollectable 
> objects disappeared.
> But this is not good enough, because if you do not understand it you will 
> write out some code like this in future, and then also has memory leak too.
> 
> So can some one helps me give some detailed on recursive closure used like 
> the code above, and on why gc can not collect them.
> Thanks a lot lot ......
> 
> --
> Best
>     Li Tianqing
> 
> 
> 
> 
> _______________________________________________
> OpenStack-dev mailing list
> OpenStack-dev@lists.openstack.org
> http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev

_______________________________________________
OpenStack-dev mailing list
OpenStack-dev@lists.openstack.org
http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev

Reply via email to