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 &lt;= 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进行内存调试与问题排查

4 回复

这个问题我只能参观了!


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等工具。")

关键点说明:

  1. heapq模块:这是Python的堆队列算法实现,用于实现优先队列,不是内存调试工具。名称中的"heap"指的是数据结构中的堆,不是内存堆。

  2. 真正的内存调试工具

    • tracemalloc:Python标准库,追踪内存分配
    • memory-profiler:第三方库,逐行分析内存使用
    • objgraph:第三方库,可视化对象引用关系
    • sys.getsizeof():查看对象内存占用
    • gc模块:垃圾回收控制
  3. 常见内存问题排查步骤

    • 使用tracemalloc定位内存分配位置
    • memory-profiler分析函数内存使用
    • objgraph检查循环引用
    • 使用gc.collect()手动触发垃圾回收

总结建议: 用tracemalloc和memory-profiler替代heapq进行内存调试。

以前从来没有遇到过这种问题。提供一个思路,你需要 Pympler,每个变量创建前后调用 tracker.SummaryTracker,然后根据变化量就能知道内存占用了。

当然,更直观的方法就是 asizeof

<br>&gt;&gt;&gt; from pympler import asizeof<br>&gt;&gt;&gt; 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>

回到顶部