1 python垃圾回收机制
1.1python垃圾回收机制使用引用计数
引用计数是因为变量是对内存数据区域引用。
1.2 python给对象(内存中区域)维护引用计数
创建,复制,计数+1
引用销毁,计数-1
减为0,就释放该内存空间。
作为参数传递给函数时,先复制引用,所以计数+1
del语句消除引用关系,没有_self自我引用,则内存释放
1.3 标记-清除回收机制
解决的问题:循环引用
解决思路: 内存分为两部分,一部分保存真实的引用计数,
另一部分作为引用计数的副本,在副本上进行引用计数的试验
1.4 分代回收
提升垃圾回收效率
垃圾检测->垃圾回收
同一代对象:垃圾回收频率相近的对象
判断对象的代:根据生存时间。
代码持续访问的活跃对象,会从零代连表转移到一代,再转移
到二代,python处理零代最为频繁,其次一代
1.5 python垃圾回收机制总结
采取是:引用计数为主,标记-清除+分代回收为辅的回收策略
局限:
1) 对于循环引用无效,需要垃圾回收模块gc
2) 重载类的__del__方法,指出了删除对象释放内存空间以外的操作
del语句先查看待删除对象引用计数如果为0,就释放内存并执行__del__方法。
collect方法默认不对重载了__del__方法的循环引用对象回收
2 python内存分析工具及用法
2.1 查看对象的引用
sys.getrefcount(xxx):
作用:查看对象xxx有几个引用
gc.collect([generation])
作用:如果没有参数,进行一次全面的回收
gc.garbage
是一个列表,每个元素是才能在的垃圾对象
2.2 pyrasite
作用: 连接运行的python程序,类似交互终端
1) 安装:
gdb安装检测:
直接输入gdb
如果没有按转gdb,执行如下命令安装
yum install gdb -y
pip install pyrasite
pip install Cython
yum install wget -y
wget https://files.pythonhosted.org/packages/98/c6/7fa12062ddfe1732d43b34b64a3fe99da958a88fa1d8b7550fe386a9ca01/meliae-0.4.0.tar.gz
tar -xvzf meliae-0.4.0.tar.gz
cd meliae-0.4.0
python setup.py install
2) 检查步骤
pyrasite-memory-viewer pid
作用:查看该进程中各种类型对象数量和内存占用情况
pyrasite-shell pid
作用: 进入到进程的shell
2.3 meliae
作用:把某个时可的内存dump到文件中,然后再对该文件分析,
1)安装
yum install -y python-meliae
2)使用
from meliae import loader
import meliae.scanner
def dumpMemoryByMeliae():
jsonPath = "./dumpMemory.json"
meliae.scanner.dump_all_objects(jsonPath)
def analyseDumpMemory():
memoryJson = loader.load('./dumpMemory.json')
# 计算对象引用关系
memoryJson.compute_parents()
# 去除对象的__dict__属性
memoryJson.collapse_instance_dicts()
# 分析内存占用情况
print memoryJson.summarize()
3)分析占据大内存的对象名称和值
Total 20523 objects, 116 types, Total size = 3.2MiB (3314994 bytes)
Index Count % Size % Cum Max Kind
0 617 3 775640 23 23 49432 dict
1 8315 40 684262 20 44 12479 str
2 99 0 394224 11 55 12624 module
其中看Count指标,如果很大,有可能内存泄漏
json文件:
{"address": 140637996210048, "type": "str", "size": 44, "len": 7, "value": "_remove", "refs": []}
其中address字段来自于, id(obj)的结果
id(obj)可以获取对象obj的内存地址
3.1)根据id即python中对象地址来获取该对象的值
import ctypes
def getVariableValue(objId):
result = ctypes.cast(objId, ctypes.py_object).value
return result
3.2)根据id即python中对象地址来获取该对象的变量名称
import ctypes
import gc
def getVariableName(objId):
result = gc.get_referrers(ctypes.cast(objId, ctypes.py_object).value)
return result
2.4 objgraph
objgraph:绘制python中对象图的包
ref:
https://www.baidu.com/link?url=lewTusnGDWqMj-89M8bYv2tY4mYG8yG4GCLxEkTZ7IpJWA95jL2Jq-QmDNM_ZTQo&wd=&eqid=cb75f2200006d61e000000055b7d12d5
1)安装
pip install objgraph
2)使用
import objgraph
objgraph.show_growth()
得到基准对象个数,输出增长的对象
objgraph.show_backrefs()
2.5 guppy
查看python对象占用堆内存大小
1)安装
pip install guppy
2)使用
from guppy import hpy
def getHeapInfo():
result = hpy().heap()
return result
3)结果示例
Partition of a set of 26171 objects. Total size = 3346832 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 11916 46 943536 28 943536 28 str
1 5878 22 474480 14 1418016 42 tuple
2 324 1 280032 8 1698048 51 dict (no owner)
2.6 pympler
作用:统计内存里面类型使用,获取对象大小
ref:
https://baijiahao.baidu.com/s?id=1576773427048442678&wfr=spider&for=pc
1)安装
pip install pympler
2)使用
from pympler import asizeof
def getObjMemory(obj):
result = asizeof.asizeof(obj)
return result
总结:
1 建议使用Pyrasite和meliae,Pyrasite可以连接到运行的进程,
meliae可以dump内存,并分析内存中对象,根据对象id反过来
2 找到该对象名称
建议先用Pyrasite-shell连接到进程,然后使用meliae
来dump出这个进程内存信息
3 python内存泄漏原因分析
ref:
https://www.douban.com/note/645539928/
原因1: 对象被另一个声明周期特别长的对象引用
例如: 网络中的长连接
原因2: 循环引用中的对象定义了__del__函数
<<程序员必知的Python陷阱与缺陷列表>>中介绍
如果定义了__del__函数,循环引用中python解释器
无法判断析构对象的顺序,就不处理
4 python内存分析相关代码
import ctypes
import gc
import os
import platform
import sys
from meliae import loader
import meliae.scanner
class People():
def __init__(self):
pass
def computeReferenceNumber():
t = People()
k = People()
t._self = t
print "t reference number: %d" % (sys.getrefcount(t))
print "k reference number: %d" % (sys.getrefcount(k))
def circularReference():
a = [1]
b = [2]
a.append(b)
b.append(a)
print "a reference number: %d" % (sys.getrefcount(a))
print "b reference number: %d" % (sys.getrefcount(b))
del a
del b
# print "a after del reference number: %d" % (sys.getrefcount(a))
# print "b after del reference number: %d" % (sys.getrefcount(b))
try:
sys.getrefcount(a)
except UnboundLocalError:
print "a is invalid"
# 强制进行垃圾回收
unreachableCount = gc.collect()
print "unreachable count: %d" % (unreachableCount)
class A():
def __init__(self):
pass
def __del__(self):
pass
class B():
def __init__(self):
pass
def __del__(self):
pass
def memoryLeak():
a = A()
b = B()
a._b = b
b._a = a
# del会检查该对象引用计数,如果为0,就执行待删除对象的__del__方法
del a
del b
print gc.collect()
result = gc.garbage
print result
print type(result)
def dumpMemoryByMeliae():
path = "./dumpMemory.json"
meliae.scanner.dump_all_objects(path)
def analyseDumpMemory():
memoryJson = loader.load('./dumpMemory.json')
# 计算对象引用关系
memoryJson.compute_parents()
# 去除对象的__dict__属性
memoryJson.collapse_instance_dicts()
# 分析内存占用情况
print memoryJson.summarize()
def process():
# computeReferenceNumber()
# circularReference()
# memoryLeak()
dumpMemoryByMeliae()
analyseDumpMemory()
if __name__ == "__main__":
process()
参考:
[1] https://www.cnblogs.com/franknihao/p/7326849.html
[2] https://www.cnblogs.com/pinganzi/p/6646742.html
[3] https://www.douban.com/note/645539928/
[4] https://baijiahao.baidu.com/s?id=1576773427048442678&wfr=spider&for=pc
[5] https://www.baidu.com/link?url=lewTusnGDWqMj-89M8bYv2tY4mYG8yG4GCLxEkTZ7IpJWA95jL2Jq-QmDNM_ZTQo&wd=&eqid=cb75f2200006d61e000000055b7d12d5