使用malloc_trim管理Python程序GC问题

使用malloc_trim管理Python程序GC问题Python带有自动内存管理,使开发人员无需手动处理未分配的对象。与CPU / IO绑定相比,标准的无状态Python Web服务器通常可能受内

欢迎大家来到IT世界,在知识的湖畔探索吧!

更多互联网新鲜资讯、工作奇淫技巧关注原创【飞鱼在浪屿】(日更新)


许多大型软件-如Instagram,YouTube和Dropbox Web应用程序-最初都是Python应用程序。由于Python的易用性和高质量的开源库,因此使用Python构建Web应用程序极其容易。

使用malloc_trim管理Python程序GC问题


动机

Python带有自动内存管理(带有垃圾回收),使开发人员无需手动处理未分配的对象。这对于更快地交付功能非常有帮助,但是会使应用程序受到内存管理系统和垃圾收集算法的影响。例如,GC暂停在Java等其他托管内存语言中臭名昭著。

速度可能不是Python应用程序的决定性因素,因为它很少用于对性能敏感的应用程序,但是自动内存管理还有其他更微妙的后果。与CPU / IO绑定相比,标准的无状态Python Web服务器(在CPython上运行)通常可能受内存限制。这是因为每个对象都是在堆上分配的,而大型的开源库(例如numpy)仅通过导入在程序中占用约50mb的空间。因此,运行该应用程序的基础架构成本直接与其内存效率-进而与其内存管理紧密相关。这对于其他动态语言(例如PHP和Ruby)也是如此。

一种更有效地运行Python Web服务器的方法是将应用程序的多个进程打包到单个主机(或Pod)上,然后为每个进程设置内存限制(可能通过cgroups设置),以免妨碍其他进程。当进程开始超过其限制时,它会杀死OOM(内存不足)。最后,如果可以降低这些限制,则可以在同一硬件上运行更多进程。这也通过绕过进程而不是线程来启用并发,从而克服了CPython的全局解释器锁(GIL)的局限性。

运行时间长的应用程序还有另一个障碍-即使没有内存泄漏,Python应用程序的内存使用也会在进程生命周期内增加。Python的内存分配器(pymalloc)分配了大型对象,即使由于底层碎片和空闲列表的增长而释放了底层内存,也不一定将它们返回给OS。pymalloc在背后使用malloc,而malloc / free不喜欢过于频繁地将内存返回内核。那么,OOM杀死只是长时间运行的Python应用程序中。

随后,目标变成了两个方面-减少峰值内存使用,以便可以在同一硬件上运行尽可能多的进程,并减少OOM终止的频率,从而不影响应用程序的可用性和可操作性。这意味着OOM终止应该被分开进行(所有进程不应该一起重启),OOM终止应该在足够的间隔(一个小时或更长时间)发生,这样调试进程状态(例如自检应用程序状态)应该不会很困难,并且可以推送/重启由于担心同时进行标准重启和OOM终止,因此该应用程序不需要太多的容量。


malloc_trim

这就是malloc_trim进入的地方。malloc_trim是一个libc函数,它告诉libc将可用内存释放回操作系统。这可能会使随后的分配稍微慢一些,但是如前所述,对于非性能敏感的应用程序,这并不是太多的问题。


如何使用?

malloc_trim非常容易设置。简化的代码示例:

import ctypes
import os
import psutil


def trim_memory() -> int:
  libc = ctypes.CDLL("libc.so.6")
  return libc.malloc_trim(0)

def should_trim_memory() -> bool:
  # check if we're close to our OOM limit
  # through psutil
  process = psutil.Process(os.getpid())
  return process.memory_info().rss > MEMORY_THRESHOLD

def trim_loop() -> None:
  
  while True:
    time.sleep(jitter(30, 60)) # jitter between 30 and 60s
    if not should_trim_memory(): 
      continue

    ret = trim_memory()
    print("trim memory result: ", ret)

def main() -> None:
  # run web server
  
  thread = Thread(name="TrimThread", target=trim_loop)
  thread.daemon = True
  thread.start()

欢迎大家来到IT世界,在知识的湖畔探索吧!

本质上,您在后台运行一个线程来定期要求libc达到阈值时进行清理,例如定期垃圾回收循环。


备择方案

一种替代方法是切换到jemalloc,这是一种尝试减少碎片的替代内存分配器。jemalloc不需要malloc_trim。但是它有缺点,并且并非在所有情况下都有效。

还有一些补充方法,例如__slots__,可以减少Python应用程序的内存使用。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/35688.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信