Python中如何使用heapy进行内存调试与问题排查
本程序现状如下:
(Pdb) h0 = hp.heap()
(Pdb) h0
Partition of a set of 4254865 objects. Total size = 700955416 bytes.
Index Count % Size % Cumulative % Kind (class / dict of class)
0 646764 15 427504800 61 427504800 61 dict (no owner)
1 3073581 72 210210328 30 637715128 91 unicode
2 120775 3 17186360 2 654901488 93 list
3 71772 2 10795432 2 665696920 95 str
4 37269 1 10594328 2 676291248 96 _sre.SRE_Pattern
5 124398 3 10406784 1 686698032 98 tuple
6 721 0 2114200 0 688812232 98 dict of module
7 83485 2 2003640 0 690815872 99 int
8 11706 0 1498368 0 692314240 99 types.CodeType
9 11157 0 1338840 0 693653080 99 function
<653 more rows. Type e.g. '_.more' to view.>
可以看到,dict 占用了大部分内存。然而这道这点儿当然不够,我还要知道“哪些字典”大,比如,哪个模块里创建的,怎么引用到它,之类问题。
难处就在于,这个情景下,嵌套的字典非常多,给一段代码模拟这个场景:
def rand():
return random.random()
def rand_int(i):
return random.randint(0, i)
def rdt(max_depth, max_width):
r = {}
if max_depth <= 1:
for i in range(rand_int(max_width)):
r[rand()] = rand()
else:
for i in range(rand_int(max_width)):
r[rand()] = rdt(rand_int(max_depth) - 1, max_width)
return r
t0 = rdt(9, 9)
t1 = rdt(7, 14)
t2 = rdt(5, 19)
t3 = rdt(3, 24)
现在,请问我直接创建的四个 t,哪个大?
“大”当然不是指那一个 PyObject 占用的空间大就行了,而是所有直接间接 referents dicts 都加在一起,才有意义。
所以类似这样的解答不行:
>>> (h0[0] - h0[0].referents).byvia
Partition of a set of 5 objects. Total size = 2168 bytes.
Index Count % Size % Cumulative % Referred Via:
0 1 20 1048 48 1048 48 "['t4']"
1 1 20 280 13 1328 61 "['t0']"
2 1 20 280 13 1608 74 "['t1']"
3 1 20 280 13 1888 87 "['t2']"
4 1 20 280 13 2168 100 "['t3']"
各位牛人,请教了
Python中如何使用heapy进行内存调试与问题排查
这个问题我只能参观了!
import heapq
import sys
import tracemalloc
# 示例1:使用tracemalloc进行内存追踪
def memory_intensive_operation():
"""一个可能造成内存问题的函数"""
data = []
for i in range(10000):
# 故意创建大量对象
data.append([j for j in range(i % 100)])
return data
def track_memory():
"""使用tracemalloc追踪内存使用"""
tracemalloc.start()
# 执行可能的内存密集型操作
result = memory_intensive_operation()
# 获取当前内存快照
snapshot = tracemalloc.take_snapshot()
# 显示内存使用最多的10个地方
top_stats = snapshot.statistics('lineno')
print("内存使用最多的10个位置:")
for stat in top_stats[:10]:
print(stat)
tracemalloc.stop()
return result
# 示例2:使用heapq进行堆操作(注意:heapq是堆队列算法,不是内存调试工具)
def heap_operations():
"""展示heapq的正确用法"""
# 创建堆
heap = []
heapq.heappush(heap, 5)
heapq.heappush(heap, 2)
heapq.heappush(heap, 8)
heapq.heappush(heap, 1)
print("堆中的元素:", heap)
print("弹出最小元素:", heapq.heappop(heap))
print("弹出后堆中的元素:", heap)
# 堆排序
nums = [3, 1, 4, 1, 5, 9, 2, 6]
heapq.heapify(nums)
sorted_nums = [heapq.heappop(nums) for _ in range(len(nums))]
print("堆排序结果:", sorted_nums)
# 示例3:使用sys.getsizeof查看对象内存大小
def check_object_size():
"""检查不同对象的内存占用"""
objects = [
"Hello World",
[1, 2, 3, 4, 5],
{"a": 1, "b": 2, "c": 3},
(1, 2, 3, 4, 5),
set([1, 2, 3, 4, 5])
]
print("对象内存占用检查:")
for obj in objects:
size = sys.getsizeof(obj)
print(f"{type(obj).__name__}: {obj} -> {size} bytes")
# 示例4:使用memory_profiler(需要安装:pip install memory-profiler)
"""
# 保存为memory_profile_demo.py并运行:python -m memory_profiler memory_profile_demo.py
from memory_profiler import profile
@profile
def memory_profile_example():
data = []
for i in range(1000):
data.append([j for j in range(i % 50)])
return data
if __name__ == "__main__":
memory_profile_example()
"""
# 示例5:使用objgraph查找循环引用(需要安装:pip install objgraph)
"""
import objgraph
import gc
def create_circular_reference():
class Node:
def __init__(self, value):
self.value = value
self.next = None
a = Node(1)
b = Node(2)
a.next = b
b.next = a # 创建循环引用
return a, b
# 查找循环引用
def find_circular_references():
a, b = create_circular_reference()
# 显示循环引用图
objgraph.show_backrefs([a], filename="backrefs.png")
# 统计对象数量
print("Node实例数量:", len(objgraph.by_type('Node')))
# 运行示例
if __name__ == "__main__":
print("=== 示例1:内存追踪 ===")
track_memory()
print("\n=== 示例2:堆操作 ===")
heap_operations()
print("\n=== 示例3:对象大小检查 ===")
check_object_size()
print("\n注意:heapq是堆队列算法模块,用于优先队列操作,不是内存调试工具。")
print("内存调试推荐使用:tracemalloc、memory-profiler、objgraph等工具。")
关键点说明:
-
heapq模块:这是Python的堆队列算法实现,用于实现优先队列,不是内存调试工具。名称中的"heap"指的是数据结构中的堆,不是内存堆。
-
真正的内存调试工具:
tracemalloc:Python标准库,追踪内存分配memory-profiler:第三方库,逐行分析内存使用objgraph:第三方库,可视化对象引用关系sys.getsizeof():查看对象内存占用gc模块:垃圾回收控制
-
常见内存问题排查步骤:
- 使用
tracemalloc定位内存分配位置 - 用
memory-profiler分析函数内存使用 - 用
objgraph检查循环引用 - 使用
gc.collect()手动触发垃圾回收
- 使用
总结建议: 用tracemalloc和memory-profiler替代heapq进行内存调试。
以前从来没有遇到过这种问题。提供一个思路,你需要 Pympler,每个变量创建前后调用 tracker.SummaryTracker,然后根据变化量就能知道内存占用了。
当然,更直观的方法就是 asizeof<br>>>> from pympler import asizeof<br>>>> asizeof.asizeof(t0)<br>4880<br>
多谢 pympler 这个工具推荐
我搜 python memory profile and debug,为什么没搜出这个工具呢,只看到了 line profiler,heapy 啥的
这样搞是一个思路了,很笨的一个思路呵呵,很慢,跑十几分钟才跑出十分之一:python<br>from pympler.asizeof import asizeof<br><br>h0 = hp.heap()<br><br>ds = h0[0]<br><br># 获取"根"dict set<br>root_ds = (ds - ds.referrers).byid<br><br># 看一看每个“根 dict ”的 recursive size<br>sizes = []<br>for i in range(len(root_ds)):<br> info = i, asizeof(root_ds[i])<br> sizes.append(info)<br><br># check size<br>

