from memory_profiler import profile
@profiledefallocate_memory():# Allocate a list with a range of numbers
a =[i for i inrange(10000)]# Allocate another list with squares of numbers
b =[i **2for i inrange(10000)]return a, b
if __name__ =="__main__":
allocate_memory()技术支持
import tracemalloc
# Start tracing memory allocations
tracemalloc.start()# Code block for which memory usage is to be trackeddefallocate_memory():
a =[i for i inrange(10000)]# List of numbers from 0 to 9999
b =[i**2for i inrange(10000)]# List of squares of numbers from 0 to 9999return a, b
# Keep the allocated lists in scope
allocated_lists = allocate_memory()# Take a snapshot
snapshot = tracemalloc.take_snapshot()# Stop tracing memory allocations
tracemalloc.stop()# Analyze the snapshot to see memory usage
top_stats = snapshot.statistics('lineno')# Write the memory usage data to a text filewithopen('memory_usage.txt','w')as f:
f.write("[ Top 10 memory consumers ]\n")for stat in top_stats[:10]:
f.write(f"{stat}\n")# Detailed traceback for the top memory consumer
f.write("\n[ Detailed traceback for the top memory consumer ]\n")for stat in top_stats[:1]:
f.write('\n'.join(stat.traceback.format())+'\n')print("Memory usage details saved to 'memory_usage.txt'")技术支持
import tracemalloc
# Start tracing memory allocations
tracemalloc.start()# Function to allocate memorydefallocate_memory():
a =[i for i inrange(10000)]# List of numbers from 0 to 9999
b =[i**2for i inrange(10000)]# List of squares of numbers from 0 to 9999return a, b
# Initial memory usage snapshot
snapshot1 = tracemalloc.take_snapshot()# Perform memory allocations
allocated_lists = allocate_memory()# Memory usage snapshot after allocations
snapshot2 = tracemalloc.take_snapshot()# Further memory allocations (simulate more work to find potential leaks)
allocated_lists += allocate_memory()# Memory usage snapshot after additional allocations
snapshot3 = tracemalloc.take_snapshot()# Stop tracing memory allocations
tracemalloc.stop()# Compare the snapshots
stats1_vs_2 = snapshot2.compare_to(snapshot1,'lineno')
stats2_vs_3 = snapshot3.compare_to(snapshot2,'lineno')# Save the comparison results to a text filewithopen('memory_leak_analysis.txt','w')as f:
f.write("[ Memory usage increase from snapshot 1 to snapshot 2 ]\n")for stat in stats1_vs_2[:10]:
f.write(f"{stat}\n")
f.write("\n[ Memory usage increase from snapshot 2 to snapshot 3 ]\n")for stat in stats2_vs_3[:10]:
f.write(f"{stat}\n")# Detailed traceback for the top memory consumers
f.write("\n[ Detailed traceback for the top memory consumers ]\n")for stat in stats2_vs_3[:1]:
f.write('\n'.join(stat.traceback.format())+'\n')print("Memory leak analysis saved to 'memory_leak_analysis.txt'")技术支持
从快照 1 到快照 2,我们看到创建列表 a 和 b 是内存使用激增的主要原因。从napshot 2 到快照 3,我们也看到更多对列表 a 和 b 的分配提升了内存利用率。
import tracemalloc
# Start tracing memory allocations
tracemalloc.start()# Code block for which memory usage is to be trackeddefallocate_memory():
a =[i for i inrange(10000)]
b =[i**2for i inrange(10000)]
allocate_memory()# Take a snapshot
snapshot = tracemalloc.take_snapshot()# Analyze top memory consumers
top_stats = snapshot.statistics('lineno')print("[ Top 10 memory consumers ]")for stat in top_stats[:10]:print(stat)# Stop tracing memory allocations
tracemalloc.stop()技术支持
# Show the most common object types
objgraph.show_most_common_types()# Get the details of a specific object type
objs = objgraph.by_type('Node')
objgraph.show_refs(objs[:3], refcounts=True, filename='node_details.png')技术支持
from memory_profiler import profile
@profiledefallocate_memory():
a =[i for i inrange(10000)]
b =[i **2for i inrange(10000)]return a, b
if __name__ =="__main__":
allocate_memory()技术支持
tracemalloc 示例
import tracemalloc
tracemalloc.start()defallocate_memory():
a =[i for i inrange(10000)]
b =[i **2for i inrange(10000)]return a, b
allocate_memory()
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')for stat in top_stats[:10]:print(stat)技术支持
stats = snapshot2.compare_to(snapshot1, 'lineno')
for stat in stats[:10]:
print(stat)技术支持
示例
import tracemalloc
# Start tracing memory allocations
tracemalloc.start()# Function to run heavy computationdefrun_heavy_computation():
a =[i for i inrange(10000)]
b =[i**2for i inrange(10000)]return a, b
# Initial memory usage snapshot
snapshot1 = tracemalloc.take_snapshot()# Perform memory allocations
run_heavy_computation()# Memory usage snapshot after allocations
snapshot2 = tracemalloc.take_snapshot()# Compare the snapshots
stats = snapshot2.compare_to(snapshot1,'lineno')# Print the top memory-consuming lines of codeprint("[ Top 10 memory consumers ]")for stat in stats[:10]:print(stat)# Stop tracing memory allocations
tracemalloc.stop()技术支持
# Show the most common object types
objgraph.show_most_common_types()# Get details of a specific object type
nodes = objgraph.by_type('Node')
objgraph.show_refs(nodes[:3], refcounts=True, filename='node_details.png')技术支持
示例
import objgraph
classNode:def__init__(self, value):
self.value = value
self.children =[]# Create some nodes and establish relationships
node1 = Node(1)
node2 = Node(2)
node3 = Node(3)
node1.children.append(node2)
node2.children.append(node3)# Generate a graph of object references
objgraph.show_refs([node1], filename='object_refs.png')# Create a circular reference
node3.children.append(node1)# Detect circular references
chain = objgraph.find_backref_chain(node1, objgraph.is_proper_module)
objgraph.show_chain(chain, filename='circular_refs.png')# Show the most common object types
objgraph.show_most_common_types()# Get details of a specific object type
nodes = objgraph.by_type('Node')
objgraph.show_refs(nodes[:3], refcounts=True, filename='node_details.png')技术支持
Python 应用,尤其是处理大型数据集或复杂计算的程序,可能会占用过多内存,从而导致变慢、崩溃或内存不足错误。内存分析是一种技术,它可以让您分析 Python 代码的内存使用情况,识别内存泄漏和瓶颈,并优化内存消耗,以提升性能与稳定性。
在本教程中,我们将探讨内存分析、可用的工具,以及如何使用这些工具来分析您的 Python 代码的实用示例。
什么是内存分析?
内存分析是一种在程序运行时监控和评估其内存使用情况的技术。该方法可帮助开发者发现内存泄漏、优化内存利用,并理解程序的内存消耗模式。内存分析对于防止应用程序使用超出必要的内存、从而引发性能下降或崩溃至关重要。
内存分析的优势
内存分析带来多项关键好处,对于维护高效、健壮的应用不可或缺:
常用的内存分析工具
我们来看看几种常用的内存分析工具,介绍其特性、安装方法与用法。具体将涵盖用于逐行内存分析的
memory_profiler,用于拍摄并比较内存快照的tracemalloc,以及用于可视化对象关系的objgraph。memory_profiler
memory_profiler是一款功能强大的 Python 工具,可详细洞察代码的内存使用情况。它通过提供细粒度的逐行内存使用明细,方便定位导致高内存消耗的确切代码行。通过这种层级的了解,开发者可以有针对性地进行优化。安装
要安装
memory_profiler及所需的其他包,请按以下步骤操作:以下命令安装
memory_profiler包:如果您希望用图表可视化内存使用情况,可以运行以下命令安装
matplotlib:用法
要使用
memory_profiler,请按照以下步骤:为需要分析的函数添加装饰器。
在文本编辑器或 IDE 中打开您的 Python 脚本。
从
memory_profiler导入 profile 装饰器,并将其应用于要分析的函数。示例脚本如下:
代码说明
这里我从
memory_profiler包中导入了 profile 装饰器。该装饰器将帮助我们监控被装饰函数的内存使用。我将
@profile装饰器应用到allocate_memory()函数上。这意味着当allocate_memory()执行时,memory_profiler会跟踪其内存使用并提供详细报告。在
allocate_memory()函数内,我创建了两个列表。第一个列表包含 0 到 9999 的数字;第二个列表包含 0 到 9999 的平方。以下代码块确保仅在脚本被直接运行时才调用
allocate_memory()函数,而不是在作为模块导入时调用。运行该脚本后,memory_profiler会输出allocate_memory()的内存使用情况,便于您了解在创建列表a和b时消耗了多少内存。在终端或命令提示符中,导航到脚本所在目录。使用
-m memory_profiler选项运行脚本。每行代码的内存使用情况。图片来源:作者。
在上图中,我们可以看到每行代码的内存使用以及每行的增量。下方的图表更详细地展示了各行代码如何使用内存,以及随着每次迭代内存如何增加。
内存使用随时间变化。图片来源:作者。
tracemalloc
tracemalloc是 Python 内置的强大模块,用于跟踪内存的分配与释放。理解应用如何使用内存对于优化性能与识别内存泄漏至关重要。Tracemalloc 提供了易用的接口来捕获与分析内存使用快照,是每位 Python 开发者都非常有价值的工具。我们将看看
tracemalloc如何拍摄内存使用快照、比较这些快照来识别内存泄漏,并分析内存占用最高的部分拍摄内存使用快照
tracemalloc允许您在程序的特定时间点拍摄内存使用快照。快照会捕获内存分配的状态,提供内存使用位置的详细视图。以下是使用
tracemalloc拍摄快照的方法:我使用
tracemalloc模块在 Python 应用中跟踪并检查内存使用。通过拍摄内存分配的快照并查看内存占用最高的部分,我确定了代码中使用最多内存的区域。随后,我将这些分析写入文本文件以便进一步阅读与参考。比较快照以识别内存泄漏
tracemalloc的强大功能之一是可以比较在程序不同时间点拍摄的快照,从而识别内存泄漏。通过分析快照之间的差异,您可以定位哪些地方分配了额外内存且未被释放。以下是比较快照的方法:
从快照 1 到快照 2,我们看到创建列表
a和b是内存使用激增的主要原因。从napshot 2 到快照 3,我们也看到更多对列表a和b的分配提升了内存利用率。通过比较这些快照,我们发现内存使用增加的主要原因是反复分配大型列表。在真实应用中,如果这些分配并非必要,可能就意味着内存泄漏。详细的回溯可以定位导致这些分配的精确代码行。
分析内存占用最高的部分
tracemalloc还提供工具来分析应用中内存占用最高的部分。这有助于您将优化重点放在最消耗内存的代码片段上。以下是分析高内存占用位置的方法:
在此示例中,拍摄快照后,我使用 snapshot.statistics('lineno') 获取按行号分组的内存使用统计。该输出可帮助您快速识别代码中哪些部分消耗内存最多,从而有针对性地进行优化。
objgraph
Objgraph 是一个 Python 库,旨在帮助开发者可视化和分析代码中的对象引用。它提供工具来创建显示内存中对象彼此引用关系的可视化图表。这种可视化有助于理解程序内部的结构与关系、识别内存泄漏并优化内存使用。
安装
首先,我们安装
objgraph库。您可以通过 pip 安装:objgraph的关键特性objgraph为内存分析与可视化提供了若干关键功能:objgraph,您可以创建对象及其引用关系的可视化表示。这有助于理解对象之间的互联结构,使调试与优化更为容易。使用 objgraph 可视化对象关系
通过一些示例了解
objgraph的实际用法。示例 1:生成对象引用图
假设我们有一个简单的类,并创建一些实例:
在该示例中,我们定义一个
Node类,并创建具有关联关系的节点实例。objgraph.show_refs()函数会从node1开始生成对象引用的可视化图,并保存为object_refs.png。该图有助于我们理解节点之间的互联方式。示例 2:检测循环引用
要检测循环引用,我们可以使用
objgraph.find_backref_chain()函数:此处我们通过将 node1 添加为 node3 的子节点来创建循环引用。
objgraph.find_backref_chain()函数会检测到该循环引用,而 objgraph.show_chain 会对其进行可视化,并将结果保存为 circular_refs.png。示例 3:分析对象类型与大小
要分析对象的类型与大小,我们可以使用 objgraph.show_most_common_types 和 objgraph.by_type:
在该示例中,objgraph.show_most_common_types 会显示当前内存中最常见的对象类型。
objgraph.by_type()函数会检索所有 Node 类的实例,我们再可视化其引用(包含引用计数),并将图表保存为 node_details.png。如何使用内存分析工具
现在我们来看看高效使用内存分析工具的多种技巧。我们将重点介绍如何分析特定函数、跟踪整体内存使用,以及可视化对象图。
分析特定函数
使用
memory_profiler或tracemalloc针对特定函数进行分析。memory_profiler示例memory_profiler提供了一种直接的方法,可在函数内部逐行监控内存消耗,帮助您定位可降低内存使用的区域。tracemalloc示例跟踪整体内存使用
要随时间监控程序的总内存使用情况,我们可以使用内置的 tracemalloc 模块。以下展示如何跟踪总体内存使用并解读结果以识别趋势。
使用
tracemalloc的步骤我们开始于脚本开头初始化内存跟踪。
随后我们运行希望监控的代码部分。
我们拍摄内存使用的快照,以便在不同时间点捕获内存分配状态。
最后,我们比较快照以理解内存使用模式并检测泄漏。
示例
结果解读提示
以下提示有助于您理解分析数据:
可视化对象图
使用
objgraph库,我们可以创建对象关系的可视化图。这有助于分析内存使用、检测泄漏并理解对象生命周期。以下是可视化对象图的分步流程。首先生成对象引用图;随后检测循环引用;最后对图表进行分析。
生成对象引用图
检测循环引用
分析对象类型与大小
示例
分析图表
最后一步,我们来分析图表。可按以下顺序考虑:
实用技巧与最佳实践
以下关键技巧与最佳实践可帮助您最大化分析成效:
结论
在本教程中,我们探讨了理解并优化 Python 应用内存使用的关键工具与技术。其中包括
memory_profiler,它提供在函数内部逐行监控内存消耗的简便方式,以及objgraph,它具备强大的可视化能力,帮助您分析对象关系、检测循环引用,并理解内存使用结构。将内存分析工具融入您的开发流程,可帮助您深入洞察代码如何管理内存,做出明智的优化决策,并提升应用的整体性能与可靠性。无论您是在编写小型脚本还是构建大规模系统,内存分析都是每位致力于编写高效稳健代码的 Python 开发者必不可少的实践。
若要加深您对内存分析与高效编码的理解和实践,建议学习 Writing Efficient Code in Python Course。此外,我们的 Python Programming 课程提供关于最佳实践与高级技术的全面概览,助您成长为一名熟练的开发者。