草庐IT

Python:垃圾收集器的行为

coder 2023-06-04 原文

我有一个 Django 应用程序,它表现出一些奇怪的垃圾收集行为。特别是有一种观点,每次调用时都会显着增加 VM 的大小 - 达到一定的限制,此时使用量再次下降。问题是到达那个点需要相当长的时间,实际上运行我的应用程序的虚拟机没有足够的内存让所有 FCGI 进程占用它们有时会占用的内存。

过去两天我一直在研究这个问题并了解 Python 垃圾收集,我想我确实了解现在正在发生的事情 - 大部分情况下。使用时

gc.set_debug(gc.DEBUG_STATS)

然后对于单个请求,我看到以下输出:

>>> c = django.test.Client()
>>> c.get('/the/view/')
gc: collecting generation 0...
gc: objects in each generation: 724 5748 147341
gc: done.
gc: collecting generation 0...
gc: objects in each generation: 731 6460 147341
gc: done.
[...more of the same...]    
gc: collecting generation 1...
gc: objects in each generation: 718 8577 147341
gc: done.
gc: collecting generation 0...
gc: objects in each generation: 714 0 156614
gc: done.
[...more of the same...]
gc: collecting generation 0...
gc: objects in each generation: 715 5578 156612
gc: done.

所以本质上,大量的对象被分配,但最初被移动到第 1 代,当第 1 代在同一个请求期间被扫描时,它们被移动到第 2 代。如果我手动执行 gc.collect(2 ) 之后,它们被删除。而且,正如我所提到的,当下一次自动第 2 代扫描发生时,它也会被删除,如果我理解正确的话,在这种情况下会像每 10 个请求一样(此时应用程序需要大约 150MB)。

好的,所以最初我认为在处理一个请求时可能会发生一些循环引用,从而阻止在处理该请求时收集这些对象中的任何一个。然而,我花了几个小时试图找到一个使用 pympler.muppy 和 objgraph,在请求处理之后和通过调试,但似乎没有。相反,在请求期间创建的大约 14.000 个对象似乎都在某个请求全局对象的引用链中,即一旦请求消失,它们就可以被释放。

无论如何,这一直是我试图解释的。但是,如果这是真的并且确实不存在循环依赖关系,那么一旦导致它们被持有的任何请求对象消失,是否不应该释放整个对象树,而不涉及垃圾收集器,纯粹是凭借引用计数降为零?

有了这个设置,我的问题如下:

  • 上面说的有道理吗,还是我必须在别处寻找问题?在这个特定的用例中,重要数据保留了这么长时间只是一个不幸的意外吗?

  • 有什么办法可以避免这个问题。我已经看到了一些优化 View 的潜力,但这似乎是一个范围有限的解决方案——尽管我也不确定我的通用解决方案是什么;例如,手动调用 gc.collect() 或 gc.set_threshold() 是否明智?

就垃圾收集器本身的工作方式而言:

  • 我是否正确理解一个对象总是被移动到下一代,如果扫描查看它并确定它具有的引用是不是循环,但实际上可以被追踪到根对象。

  • 如果 gc 进行了一次扫描,例如第 1 代扫描,并找到一个被第 2 代中的对象引用的对象,会发生什么情况;它是在第 2 代内部遵循这种关系,还是在分析情况之前等待第 2 代扫描发生?

  • 在使用 gc.DEBUG_STATS 时,我主要关心“每一代中的对象”信息;但是,我不断收到数百个“gc: 0.0740s elapsed.”、“gc: 1258233035.9370s elapsed”。消息;它们完全不方便——打印出来需要相当长的时间,而且它们使有趣的东西更难找到。有没有办法摆脱它们?

  • 我认为没有办法按代执行 gc.get_objects(),例如,仅从第 2 代检索对象?

最佳答案

Does the above even make sense, or do I have to look for the problem elsewhere? Is it just an unfortunate accident that significant data is kept around for so long in this particular use case?

是的,确实有道理。是的,还有其他值得考虑的问题。 Django 使用 threading.local 作为 DatabaseWrapper 的基础(并且一些贡献者使用它来使请求对象可以从没有显式传递的地方访问)。这些全局对象在请求中仍然存在,并且可以保留对对象的引用,直到线程中处理了一些其他 View 。

Is there anything I can do to avoid the issue. I already see some potential to optimize the view, but that appears to be a solution with limited scope - although I am not sure what I generic one would be, either; how advisable is it for example to call gc.collect() or gc.set_threshold() manually?

一般建议(可能你知道,但无论如何):避免循环引用和全局变量(包括 threading.local)。当 django 设计难以避免时,尝试打破循环并清除全局变量。 gc.get_referrers(obj) 可能会帮助您找到需要注意的地方。另一种禁用垃圾收集器并在每次请求后手动调用它的方法,这是最好的处理方式(这将阻止对象移动到下一代)。

I don't suppose there is a way to do a gc.get_objects() by generation, i.e. only retrieve the objects from generation 2, for example?

不幸的是,这对于 gc 接口(interface)是不可能的。但是有几种方法可以走。您可以只考虑 gc.get_objects() 返回的列表末尾,因为此列表中的对象按代排序。您可以通过在调用之间存储对它们的弱引用(例如,在 WeakKeyDictionary 中)将列表与从先前调用返回的列表进行比较。您可以在自己的 C 模块中重写 gc.get_objects() (这很容易,主要是复制粘贴编程!)因为它们是通过内部生成存储的,甚至可以使用 ctypes<>(需要非常深入的ctypes理解)。

关于Python:垃圾收集器的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1740394/

有关Python:垃圾收集器的行为的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  4. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  5. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  6. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  7. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

  8. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  9. Python 刷Leetcode题库,顺带学英语单词(31) - 2

    ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem

  10. python - 是否可以使用 Ruby 或 Python 禁用 anchor /引用来发出有效的 YAML? - 2

    是否可以在PyYAML或Ruby的Psych引擎中禁用创建anchor和引用(并有效地显式列出冗余数据)?也许我在网上搜索时遗漏了一些东西,但在Psych中似乎没有太多可用的选项,而且我也无法确定PyYAML是否允许这样做.基本原理是我必须序列化一些数据并将其以可读的形式传递给一个不是真正的技术同事进行手动验证。有些数据是多余的,但我需要以最明确的方式列出它们以提高可读性(anchor和引用是提高效率的好概念,但不是人类可读性)。Ruby和Python是我选择的工具,但如果有其他一些相当简单的方法来“展开”YAML文档,它可能就可以了。 最佳答案

随机推荐