许多大型软件-如Instagram,YouTube和Dropbox Web运用程序-最初都是Python运用程序。由于Python的易用性和高质量的开源库,因此利用Python构建Web运用程序极其随意马虎。
动机
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 ctypesimport osimport psutildef 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_THRESHOLDdef 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()
实质上,您在后台运行一个线程来定期哀求libc达到阈值时进行清理,例如定期垃圾回收循环。
备择方案一种替代方法是切换到jemalloc,这是一种考试测验减少碎片的替代内存分配器。jemalloc不须要malloc_trim。但是它有缺点,并且并非在所有情形下都有效。
还有一些补充方法,例如__slots__,可以减少Python运用程序的内存利用。