python老司机必备-内存泄露分析优化

简介: python老司机必备-内存泄露分析优化

13.png发现应用程序正在耗尽内存是开发人员需要面对的棘手问题之一。内存问题通常很难诊断和修复,并且在Python中更难。Python的自动垃圾收集使它很容易上手并易学,但是它很善于避开障碍,以至于当它不能如预期的那样工作时,开发人员可能会对如何识别和修复问题感到困惑。


在文中,我将展示如何在EvalML中诊断和修复内存问题,EvalML是Alteryx创新实验室开发的开源AutoML库。没有解决内存问题的魔法配方,但我希望开发人员,特别是Python开发人员,能够了解在将来遇到这类问题时可以利用的工具。


读完这篇博文后,你应该遵循以下几点:


  • 为什么查找和修复程序中的内存问题很重要,
  • 什么是循环引用,为什么它们会导致Python中的内存泄漏,以及
  • 了解Python的内存分析工具,以及可以用来识别内存问题原因的一些步骤。

设置舞台

EvalML团队在发布我们的包的新版本之前运行一套性能测试,以捕获任何性能回归。这些性能测试包括在各种数据集上运行我们的AutoML算法,测量我们的算法达到的分数以及运行时间,并将这些指标与我们之前发布的版本进行比较。


有一天我正在运行测试,突然应用程序崩溃了。发生了什么事?


Step 0 - 什么是内存,什么是泄漏?

任何编程语言最重要的功能之一就是它在计算机内存中存储信息的能力。每当程序创建一个新变量时,它就会分配一些内存来存储该变量的内容。


内核为程序定义了一个接口来访问计算机的cpu、内存、磁盘存储等等。每一种编程语言都提供了要求内核分配和释放内存块以供运行程序使用的方法。


当程序要求内核留出一块内存来使用时,就会发生内存泄漏,但是由于错误或崩溃,程序永远不会告诉内核它何时结束使用该内存。在这种情况下,内核将继续认为被遗忘的内存块仍在被正在运行的程序使用,而其他程序将无法访问这些内存块。


如果在运行一个程序时重复发生相同的泄漏,那么被遗忘的内存的总大小可能会变得非常大,从而消耗计算机内存的很大一部分!在这种情况下,如果程序试图请求更多内存,内核将引发一个“内存不足”错误,程序将停止运行,或者换句话说,“崩溃”。


因此,在您编写的程序中找到并修复内存泄漏非常重要,因为如果不这样做,您的程序可能最终耗尽内存并崩溃,或者可能导致其他程序崩溃。


Step 1: 确定这是一个内存问题

应用程序崩溃的原因有很多——可能是运行代码的服务器崩溃了,也可能是代码本身存在逻辑错误——所以确定当前的问题是内存问题是很重要的。


EvalML性能测试以一种诡异的安静方式崩溃。突然,服务器停止了记录进度,工作也悄然完成了。服务器日志将显示由编码错误引起的任何堆栈跟踪,因此我预感这个无声的崩溃是由使用了所有可用内存的作业引起的。


我再次运行了性能测试,但这一次启用了Python的内存分析器,以获得一段时间内内存使用情况的图表。测试再次崩溃,当我查看内存图时,我看到了这个:


14.png


我们的内存使用在一段时间内保持稳定,但随后它达到了8g !我知道我们的应用服务器有8g的RAM,所以这个配置文件证实了我们的内存即将耗尽。此外,当内存稳定时,我们将使用大约4 GB的内存,但是我们之前的

EvalML版本使用了大约2 GB的内存。因此,由于某些原因,当前版本使用的内存是正常版本的两倍。


现在我需要找出原因。


Step 2: 用一个最小的例子在本地产生内存问题

找出内存问题的原因需要大量的实验和迭代,因为答案通常并不明显。如果是的话,您可能就不会把它写进代码中了!出于这个原因,我认为用尽可能少的代码行重现这个问题是很重要的。这个最小的示例使您可以在修改代码的同时在分析器下快速运行它,以查看是否有进展。


在我的例子中,根据经验,我知道我们的应用程序运行一个150万行的出租车数据集时,我看到了一个大的峰值。我将我们的应用程序精简到只有运行这个数据集的部分。我看到了一个类似于我上面描述的峰值,但是这次,内存使用达到了10g !


在看到这个之后,我知道有一个足够好的最小的例子来深入研究。


15.png


出租车数据集上本地复制器的内存占用


Step 3:查找分配最多内存的代码行

一旦我们将问题隔离到尽可能小的代码块中,我们就可以看到程序在哪里分配了最多的内存。这可能是您重构代码和修复问题所需的确凿证据。


我认为filprofiler是一个很好的Python工具。它会在内存使用高峰时显示应用程序中每一行代码的内存分配。这是我本地示例的输出:


16.png


文件分析器根据内存分配对应用程序中的代码行(以及依赖项代码)进行排序。线路越长越红,分配的内存就越多。


分配最多内存的行是创建pandas数据帧(pandas/core/algorithms.py和pandas/core/internal/managers.py),总计4 gb的数据!我在这里截断了filprofiler的输出,但是它能够跟踪pandas代码到创建pandas数据帧的EvalML中的代码。


看到这一点有点令人困惑。是的,EvalML创建pandas数据帧,但是这些数据帧在AutoML算法中是短命的,当它们不再使用时就应该被释放。由于情况并非如此,而且这些数据帧在内存中仍然存在足够长的时间,我认为最新版本引入了内存泄漏。


Step 4:识别泄漏对象

在Python上下文中,泄漏对象是指Python的垃圾回收器在执行完回收后没有释放的对象。由于Python使用引用计数作为其主要的垃圾收集算法之一,这些泄漏对象通常是由于对象持有对它们的引用时间过长而导致的。


这些类型的对象很难找到,但是可以利用一些Python工具使方便搜索。第一个工具是gc。垃圾收集器的DEBUG_SAVEALL标志。通过设置这个标志,垃圾收集器将在gc中存储不可到达的对象:垃圾的列表。这将让您进一步研究这些对象。


第二个工具是objgraph库。一旦对象在gc垃圾列表,我们可以过滤这个列表到pandas数据帧,并使用objgraph来查看其他对象引用这些数据帧,并将它们保存在内存中。通过阅读O 'Reilly的这篇博客文章,我得到了这个方法的想法。


这是我在可视化其中一个数据帧时看到的对象图的子集:

17.png

pandas数据帧使用的内存图,显示导致内存泄漏的循环引用。


这就是我要找的确凿证据!数据帧通过一个叫做PandasTableAccessor的东西对自身进行引用,它创建了一个循环引用,因此这会将对象保存在内存中,直到Python的垃圾收集器运行并能够释放它。(你可以通过dict, PandasTableAccessor, dict, _dataframe跟踪循环。)这对于EvalML来说是有问题的,因为垃圾收集器将这些数据帧保存在内存中太长时间,导致了内存耗尽!


我能够跟踪PandasTableAccessor到Woodwork库,并将这个问题提交给维护者。他们在一个新版本中修复了这个问题,并将相关问题提交给了pandas库——这是开源生态系统中协作的一个很好的例子。


在Woodwork更新发布后,我可视化了相同数据帧的对象图,循环引用消失了!


18.png

Step 5: 验证修复效果

升级了EvalML中的Woodwork版本后,我测量了应用程序的内存占用。我很高兴地报告,内存使用现在不到以前的一半!


19.png


修复后性能测试的内存


结束语

正如我在这篇文章的开头说的,没有解决内存问题的魔法配方,但是这个案例研究提供了一个通用的框架和一组工具,如果你在将来遇到这种情况,你可以利用。我发现memory-profiler和filprofiler是调试Python内存泄漏利器。


我还想强调的是,Python中的循环引用会增加应用程序的内存占用。垃圾收集器最终将释放内存,但是,正如我们在本例中看到的,循环引用无法被回收随着时间推移,内存就会耗尽!


Python中很容易无意地引入循环引用,我能够在EvalML、scikit- optimization和scipy中找到这种情况。我鼓励你睁大研究仔细分析,看这些循环引用是否必须!


相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
相关文章
|
19小时前
|
数据采集 数据可视化 数据处理
利用Python和Pandas库实现高效的数据处理与分析
在大数据和人工智能时代,数据处理与分析已成为不可或缺的一环。Python作为一门强大的编程语言,结合Pandas库,为数据科学家和开发者提供了高效、灵活的数据处理工具。本文将介绍Pandas库的基本功能、优势,并通过实际案例展示如何使用Pandas进行数据清洗、转换、聚合等操作,以及如何利用Pandas进行数据可视化,旨在帮助读者深入理解并掌握Pandas在数据处理与分析中的应用。
|
1天前
|
数据采集 数据可视化 Python
Python分析香港26281套在售二手房数据
Python分析香港26281套在售二手房数据
|
1天前
|
大数据 Python
【Python DataFrame专栏】DataFrame内存管理与优化:大型数据集处理技巧
【5月更文挑战第20天】本文介绍了使用Python的pandas库优化DataFrame内存管理的六个技巧:1) 查看DataFrame内存占用;2) 使用高效数据类型,如`category`和`int32`;3) 仅读取需要的列;4) 分块处理大数据集;5) 利用`inplace`参数节省内存;6) 使用`eval()`和`query()`进行快速筛选。这些方法有助于处理大型数据集时提高效率。
【Python DataFrame专栏】DataFrame内存管理与优化:大型数据集处理技巧
|
2天前
|
并行计算 索引 Python
讨论如何优化 DataFrame 操作,减少内存占用和提高执行速度
【5月更文挑战第19天】优化 DataFrame 操作涉及选择合适的数据类型、避免复制、使用向量化、高效迭代和设置索引。通过这些策略,如使用 `np.int8` 节省内存,直接修改列数据,利用 `itertuples`,设置分类数据类型,以及分块和并行计算,可以显著减少内存占用和提高执行速度,从而更好地处理大规模数据。实践中需结合具体情况综合运用,不断测试和优化。
14 2
|
2天前
|
存储 数据挖掘 数据处理
【Python DataFrame 专栏】优化 DataFrame 性能:提升数据处理效率的秘诀
【5月更文挑战第19天】优化 Python DataFrame 性能的关键点包括:选择合适的数据类型以节省内存,避免重复计算,利用向量化操作,考虑使用 `iterrows` 或 `itertuples` 迭代,优化索引以及借助 `Cython` 或 `Numba` 加速代码执行。通过这些策略,能提升数据处理效率,应对大规模数据挑战。
【Python DataFrame 专栏】优化 DataFrame 性能:提升数据处理效率的秘诀
|
3天前
|
安全 Java C语言
【Python 的内存管理机制专栏】Python 内存管理机制与底层实现:C 语言视角的剖析
【5月更文挑战第18天】Python的内存管理涉及对象分配、引用计数和垃圾回收。对象分配类似C的动态内存,但更自动化。引用计数跟踪对象引用,计数为0时回收。垃圾回收机制自动清理不再使用的对象,避免内存泄漏。这种高效自动化管理让开发者能专注于业务逻辑,而底层实现的理解有助于解决特殊问题和优化性能。
【Python 的内存管理机制专栏】Python 内存管理机制与底层实现:C 语言视角的剖析
|
3天前
|
算法 Java Python
【Python 的内存管理机制专栏】Python 内存管理实战:性能优化与内存泄漏检测
【5月更文挑战第18天】Python内存管理关乎程序性能与稳定性。优化包括避免过多临时对象,如优化列表推导式减少对象创建。警惕循环引用造成的内存泄漏,如示例中的Node类。使用`gc`模块检测泄漏,通过`gc.set_debug(gc.DEBUG_LEAK)`和`gc.collect()`获取信息。实践中需持续分析内存使用,优化算法、数据结构和资源释放,以提升程序质量与效率。
【Python 的内存管理机制专栏】Python 内存管理实战:性能优化与内存泄漏检测
|
3天前
|
存储 Java 程序员
【Python 的内存管理机制专栏】深入解析 Python 的内存管理机制:从变量到垃圾回收
【5月更文挑战第18天】Python内存管理关乎程序性能与稳定性,包括变量存储和垃圾回收。变量存储时,如`x = 10`,`x`指向内存中值的引用。垃圾回收通过引用计数自动回收无引用对象,防止内存泄漏。了解此机制可优化内存使用,避免循环引用等问题,提升程序效率和稳定性。深入学习内存管理对成为优秀Python程序员至关重要。
【Python 的内存管理机制专栏】深入解析 Python 的内存管理机制:从变量到垃圾回收
|
4天前
|
数据采集 人工智能 数据挖掘
「一行分析」利用12000条招聘数据分析Python学习方向和就业方向
「一行分析」利用12000条招聘数据分析Python学习方向和就业方向
|
5天前
|
JSON JavaScript 数据格式
利用 python 分析基金,合理分析数据让赚钱赢在起跑线!(1)
利用 python 分析基金,合理分析数据让赚钱赢在起跑线!(1)