python分配内存_CPython内存分配

本文主要探讨了CPython的内存管理机制,包括内存分配的层级结构、各层级的功能、何时调用malloc以及对象删除后的内存处理。内容涉及到Python内存管理的C API、对象特定的分配器、原始内存分配器等,并提及了列表动态扩展的内存分配策略。
摘要由CSDN通过智能技术生成

其中大部分内容在C API文档的

Memory Management章节中得到回答.

一些文档比您要求的更容易.有关详细信息,您必须转到源代码.没有人会愿意这样做,除非你选择一个特定的版本. (至少2.7.5,2.7.6,3.3.2之前,3.3.3之前和3.4之前对于不同的人来说将是有趣的.)

obmalloc.c文件的来源是您的许多问题的良好起点,顶部的评论有一个不错的小ASCII艺术图:

Object-specific allocators

_____ ______ ______ ________

[ int ] [ dict ] [ list ] ... [ string ] Python core |

+3 | | |

_______________________________ | |

[ Python`s object allocator ] | |

+2 | ####### Object memory ####### | |

______________________________________________________________ |

[ Python`s raw memory allocator (PyMem_ API) ] |

+1 | | |

__________________________________________________________________

[ Underlying general-purpose allocator (ex: C library malloc) ]

0 | |

=========================================================================

_______________________________________________________________________

[ OS-specific Virtual Memory Manager (VMM) ]

-1 | |

__________________________________ __________________________________

[ ] [ ]

-2 | | | |

how many different allocaters are there in CPython?

根据文件,“几”.你可以计数内建和stdlib类型的数据,然后添加一些通用的,如果你真的想要的.但我不知道会告诉你什么. (这将是相当的版本特定的.IIRC,确切的数字甚至在3.3树中更改,因为有一个实验,新的风格的字符串是否应该使用三个不同的分配器或一个).

what is the function of each?

3级的特定于对象的分配器是针对特殊用途的值得优化的情况.正如文件所说:

For example, integer objects are managed differently within the heap than strings, tuples or dictionaries because integers imply different storage requirements and speed/space tradeoffs.

在这之下,有2类(和1.5和2.5)的各种通用的支持分配器 – 至少是一个对象分配器,一个竞技场分配器和一个小块分配器等等,但除了第一个是私有实现细节之外意味着私有甚至C-API;显然所有这些都是Python代码的私有).

而在此之下,有一个原始的分配器,其功能是在更高级别的分配器需要时询问操作系统的更多内存.

when is malloc acutally called?

原始内存分配器(或其堆管理器)应该是唯一一个调用malloc的东西. (事实上​​,它可能不一定会调用malloc;它可能会使用诸如mmap或VirtualAlloc之类的函数,但关键是它是唯一要求操作系统进行内存的操作.)Python内核中有一些例外,但它们很少有关系.

文档明确地说,更高级别的代码不应该尝试在从malloc获取的内存中对Python对象进行操作.

但是,除了Python对象之外,还有很多使用malloc的stdlib和扩展模块.

例如,1000×1000 int32值的numpy数组不会分配1百万个Python int,因此它不必经过int allocator.相反,它只是mallocs一个100万C int的数组,并根据需要在Python对象中包装它们访问它们.

How much memory does python allocate for itself at startup?

这是平台特定的,有点难以从代码中找出.但是,当我在64位Mac上启动一个新的python3.3解释器时,它的起始于13.1MB的虚拟内存,几乎立即扩展到201MB.所以,这应该是一个粗略的球场指南.

are there rules governing which data structures get first “dibs” on this memory?

不是真的,不行一个恶意或buggy对象特定的分配器可以立即抓住所有预分配的内存和更多,没有什么可以阻止它.

What happens to the memory used by an object when it is deleted (does python still hold on to the memory to allocate to another object in the future, or does the GC free up the memory for another process, say Google Chrome, to use)?

它可以返回到特定于对象的分配器,它可以将其保存在freelist上,或者将其释放到原始分配器中,该分配器保持自己的freelist.原始分配器几乎不会将内存释放回操作系统.

这是因为通常没有理由将内存释放回现代操作系统.如果您有大量未使用的页面,则操作系统的虚拟机将仅在另一个进程需要时才将其打印出来.当有一个很好的理由,它几乎总是应用程序特定的,最简单的解决方案是使用多个进程来管理你的巨大的短期内存需求.

When is a GC triggered?

这取决于你的意思是“一个GC”.

CPython使用refcounting;每次您释放对对象的引用(通过重新绑定变量或集合中的插槽,使变量超出范围等),如果是最后一个引用,它将立即被清除.这在文档的Reference Counting部分进行了说明.

然而,引用计数有一个问题:如果两个对象相互引用,即使所有外部引用都消失,它们仍然不会被清除.所以,CPython一直有一个循环收集器,周期性地移动对象来寻找彼此引用的对象的周期,但没有外部引用. (这是一个更复杂一点,但这是基本的想法.)这在gc模块的文档中有完整的解释.收藏者可以在明确要求的时候运行,当自由职业者越来越少,或者长时间没有运行的时候;这是动态的,在某种程度上是可配置的,所以很难给出“何时”的具体答案.

lists are dynamic arrays, which means they need a contiguous piece of memory. This means that if I try to append an object into a list, whose underlying-C-data-structure array cannot be extended, the array is copied over onto a different part of memory, where a larger contiguous block is available. So how much space is allocated to this array when I initialize a list?

这个代码主要在listobject.c年以前.这很复杂有一些特殊情况,例如timsort用于创建临时中间列表和非就地排序的代码.但是最终,一些代码决定了它需要N个指针的空间.

这也不是特别有趣.大多数列表从未扩展或扩展远远超出原始大小,因此在开始时额外分配会浪费静态列表的内存,并且对大多数日益增长的列表来说无益.所以,Python是保守的.我相信它是通过查看其内部freelist开始的,它不会比N个指针大得多(它也可能会整合相邻的释放列表存储;我不知道是否这样做),所以它可能会偶尔过度分配,但一般来说没有.确切的代码应在PyList_New.

无论如何,如果列表分配器的freelist中没有空格,它将下降到对象分配器,依此类推;它可能会达到0级,但通常不会.

how much extra space is allocated to the new array, which now holds the old list and the appended object?

这是在list_resize处理的,这是有趣的部分.

避免list.append是二次方法的唯一方法是过度分配几何.超过一个因素(如1.2)过度分配浪费了前几次扩张的时间太多;使用太大的因素(如1.6)浪费太多的空间,用于非常大的阵列. Python通过使用从2.0开始的序列来处理这个问题,但是快速收敛到大约1.25的地方.根据3.3来源:

The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, …

你没有特别询问排序,但我知道这是什么促使你.

请记住,timsort主要是一种合并排序,对尚未排序的小型子列表进行插入排序.所以,它的大部分操作涉及分配一个大约2N的新列表,并释放两个大小为N的列表.所以,当它们就地复制时,它几乎可以像空间和分配一样高效.有最多的O(日志N)浪费,但这通常不是使复制排序更慢的因素.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值