<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><br><div><div>On Sep 10, 2014, at 4:11 AM, Li Tianqing <<a href="mailto:jazeltq@163.com">jazeltq@163.com</a>> wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite"><div style="line-height: 1.7; font-size: 14px; font-family: Arial;"><div>After some research, i find the reason for the cycle reference. In closure, the _fix_paswords.func_closre reference the _fix_passwords. So the</div><div>cycle reference happened. </div><div>And  in <a href="https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf" style="line-height: 1.7;">https://thp.io/2012/python-gc/python_gc_final_2012-01-22.pdf</a> page 5, it says that </div><div><div>We observe that Python implementations with distinct GCs behave differently: CPython does not even try to get the order of finalizers right, and</div><div>simply puts uncollectable objects into the global list of garbage for the developer to deal with manually.</div></div><div>So the gc can not handle cycle reference, then the memory leak happened. </div></div></blockquote><div><br></div><div>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.   </div><div><br></div><div>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__.</div><div><br></div><div>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:</div><div><br></div><div>import weakref</div><div><br></div><div><br></div><div>class MyCleanupThing(object):</div><div>    def __init__(self):</div><div>        self.connection = connection = "Some Connection"</div><div>        self._ref = weakref.ref(</div><div>            # key: the weakref callback *cannot* refer to self</div><div>            self, lambda ref: MyCleanupThing._cleanup(connection))</div><div><br></div><div>    @staticmethod</div><div>    def _cleanup(connection):</div><div>        print("Cleaning up %s!" % connection)</div><div><br></div><div><br></div><div>mc = MyCleanupThing()</div><div><br></div><div>print("about to clean up...")</div><div>del mc</div><div>print("should be clean!")</div><div><br></div><div>output:</div><div><br></div><div><div style="margin: 0px; font-family: Consolas; color: rgb(200, 198, 202); background-color: rgb(6, 7, 53);">about to clean up...</div><div style="margin: 0px; font-family: Consolas; color: rgb(200, 198, 202); background-color: rgb(6, 7, 53);">Cleaning up Some Connection!</div><div style="margin: 0px; font-family: Consolas; color: rgb(200, 198, 202); background-color: rgb(6, 7, 53);">should be clean!</div><div><br></div></div><div><br></div><div> </div><div><br></div><div><br></div><blockquote type="cite"><div style="line-height: 1.7; font-size: 14px; font-family: Arial;"><div><br></div><div>If there is something wrong, please fix it. Thanks</div><div><br></div><span style="line-height: 1.7;">--</span><br><div><div>Best</div><div>    Li Tianqing</div></div><div id="divNeteaseMailCard"></div><br>在 2014-09-10 11:52:28,"Li Tianqing" <<a href="mailto:jazeltq@163.com">jazeltq@163.com</a>> 写道:<br> <blockquote id="isReplyContent" style="PADDING-LEFT: 1ex; MARGIN: 0px 0px 0px 0.8ex; BORDER-LEFT: #ccc 1px solid"><div style="line-height: 1.7; font-size: 14px; font-family: Arial;"><div>Hello,</div><div>    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. </div><div>Those looks like this (catched in ceilometer-collector)</div><div><br></div><div><div>['_context_auth_token', 'auth_token', 'new_pass'],</div><div> (<cell at 0x363bc58: list object at 0x361c170>,</div><div>  <cell at 0x363bda8: function object at 0x361a5f0>),</div><div> <function _fix_passwords at 0x361a5f0>,</div><div> <cell at 0x363bde0: list object at 0x363c680>,</div><div> <cell at 0x363bd70: function object at 0x361a668>,</div><div> ['_context_auth_token', 'auth_token', 'new_pass'],</div><div> (<cell at 0x363bde0: list object at 0x363c680>,</div><div>  <cell at 0x363bd70: function object at 0x361a668>),</div><div> <function _fix_passwords at 0x361a668>,</div><div> <cell at 0x363bf30: list object at 0x361b680>,</div><div> <cell at 0x363e168: function object at 0x361a758>,</div><div> ['_context_auth_token', 'auth_token', 'new_pass'],</div><div> (<cell at 0x363bf30: list object at 0x361b680>,</div><div>  <cell at 0x363e168: function object at 0x361a758>),</div><div> <function _fix_passwords at 0x361a758>,</div><div> <cell at 0x363e1a0: list object at 0x363cdd0>,</div><div> <cell at 0x363e130: function object at 0x361a7d0>,</div></div><br><div><span style="line-height: 1.7;">and i suspect those code in oslo.messaging</span></div><div><span style="line-height: 1.7;"><br></span></div><div><div>def _safe_log(log_func, msg, msg_data):</div><div>    """Sanitizes the msg_data field before logging."""</div><div>    SANITIZE = ['_context_auth_token', 'auth_token', 'new_pass']</div><div><br></div><div>    def _fix_passwords(d):</div><div>        """Sanitizes the password fields in the dictionary."""</div><div>        for k in d.iterkeys():</div><div>            if k.lower().find('password') != -1:</div><div>                d[k] = '<SANITIZED>'</div><div>            elif k.lower() in SANITIZE:</div><div>                d[k] = '<SANITIZED>'</div><div>            elif isinstance(d[k], dict):</div><div>                _fix_passwords(d[k])</div><div>        return d</div><div><br></div><div>    return log_func(msg, _fix_passwords(copy.deepcopy(msg_data)))</div></div><div><br></div><div>i can resolve this problem by add _fix_passwords = None before _safe_log returns.</div><div><br></div><div>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.</div><div>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.</div><div><br></div><div>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.</div><div>Thanks a lot lot ......</div><div><br></div><div>--<br><div>Best</div><div>    Li Tianqing</div></div></div><br><br><span title="neteasefooter"><span id="netease_mail_footer"></span></span></blockquote></div><br><br><span title="neteasefooter"><span id="netease_mail_footer"></span></span>_______________________________________________<br>OpenStack-dev mailing list<br><a href="mailto:OpenStack-dev@lists.openstack.org">OpenStack-dev@lists.openstack.org</a><br>http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev<br></blockquote></div><br></body></html>