在这篇博客中,我将会分享关于减少 Python 内存消耗的方法和分析导致内存消耗/膨胀根源的问题。这些都是从实际操作中总结的经验,我们正在构建Datos IO 的 RecoverX 分布式备份和恢复平台,这里主要要介绍的是在 Python(在 C++ ,Java 和 bash 中也有一些类似的组件) 中的开发。
Python 垃圾收集Python解释器对正在使用的对象保持计数。当对象不再被引用指向的时候,垃圾收集器可以释放该对象,获取分配的内存。例如,如果你使用常规的Python(CPython, 不是JPython)时,Python的垃圾收集器将调用free()/delete() 。
实用工具 资源(resource)‘resource’ 模块用来查看项目当前得的固有的)内存消耗
[固有内存是项目实际使用的RAM]?
>>> import resource
>>>resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 4332 对象(objgraph)‘objgraph’ 是一个实用模块,可以展示当前内存中存在的对象
[objgraph 文档和实例地址: https://mg.pov.lt/objgraph/]来看看objgraph的简单用法:
?
import objgraph
import random
import inspect
class Foo( object ):
def __init__( self ):
self .val = None
def __str__( self ):
return “foo val:{ 0 }”. format ( self .val)
def f():
l = []for i in range ( 3 ):
foo = Foo()
#print“idoffoo:{0}”.format(id(foo))
#print“foois:{0}”.format(foo)
l.append(foo)
return l
def main():
d = {}
l = f()
d[‘k’] = lprint “ list lhas{ 0 }objectsof type Foo()”. format ( len (l))
objgraph.show_most_common_types()
objgraph.show_backrefs(random.choice(objgraph.by_type(‘Foo’)),
filename = “foo_refs.png”)
objgraph.show_refs(d,filename = ‘sample - graph.png’)
if __name__ = = “__main__”:
main()
pythontest1.pylist lhas 10000 objectsof type Foo()
dict 10423
Foo 10000 ――――>Guiltyascharged!
tuple 3349
wrapper_descriptor 945
function 860
builtin_function_or_method 616
method_descriptor 338
weakref 199
member_descriptor 161
getset_descriptor 107
注意,我们在内存中还持有10,423个‘dict’的实例对象。
可视化objgraph依赖项Objgraph有个不错的功能,可以显示Foo()对象在内存中存在的因素,即,显示谁持有对它的引用 (在这个例子中是list ‘l’)。
在RedHat/Centos上, 你可以使用sudo yum install yum install graphviz*安装graphviz
如需查看对象字典,d,请参考:
objgraph.show_refs(d, filename=’sample-graph.png’)

从内存使用角度来看,我们惊奇地发现――为什么对象没有释放?这是因为有人在持有对它的引用。
这个小片段展示了objgraph怎样提供相关信息:
?
objgraph.show_backrefs(random.choice(objgraph.by_type(‘Foo’)),filename = “foo_refs.png”)

在这一案例中, 我们查看了Foo类型的随机对象。我们知道该特定对象被保存在内存中,因其引用链接在指定范围内。
有时,以上技巧能帮助我们理解,为什么当我们不再使用某对象时,Python垃圾回收器没有将垃圾回收。
难处理的是,有时候我们会发现Foo()占用了很多内存的类。这时我们可以用heapy()来回答以上问题。
heapy 是一个实用的,用于调试内存消耗/泄漏的工具。查看 http://guppy-pe.sourceforge.net/ 。通常,我将objgraph和heapy搭配使用:用 heapy 查看分配对象随时间增长的差异,heapy能够显示对象持有的最大内存等;用Objgraph找backref链(例如:前4节),尝试获取它们不能被释放的原因。
Heapy的典型用法是在不同地方的代码中调用一个函数,试图为内存使用量提供大量收集线索,找到可能会引发的问题:
?
from guppy import hpy
def dump_heap(h,i):
“””
@paramh:Theheap( from hp = hpy(),h = hp.heap())
@parami:Identifier str
“””
print “Dumpingstatsat:{ 0 }”. format (i)
print ‘Memoryusage:{ 0