python如何管理内存?

茗茗之中-徐瑞


介绍

内存管理是有效分配,重新分配和协调内存的过程,以便所有不同的进程都能平稳运行并可以最佳地访问不同的系统资源。内存管理还涉及清除不再访问的对象的内存。

在Python中,内存管理器通过定期运行以清理,分配和管理内存来负责此类任务。与C,Java和其他编程语言不同,Python通过使用引用计数来管理对象。这意味着内存管理器会跟踪对程序中每个对象的引用数。当对象的引用计数降至零(这意味着不再使用该对象)时,垃圾回收器(内存管理器的一部分)会自动从该特定对象释放内存。

用户不必担心内存管理,因为内存的分配和取消分配过程是全自动的。回收的内存可以由其他对象使用。

Python垃圾回收

如前所述,Python删除程序中不再引用的对象以释放内存空间。Python释放不再使用的内存块的过程称为垃圾收集。Python 垃圾收集器(GC)在程序执行期间运行,如果引用计数减少为零,则将触发Python 垃圾收集器。如果为对象分配了新名称或将其放置在诸如元组或字典之类的容器中,则引用计数会增加。同样,当重新分配对对象的引用,对象的引用超出范围或删除对象时,引用计数也会减少。

内存是一个堆,其中包含程序中使用的对象和其他数据结构。此堆空间的分配和取消分配由Python内存管理器通过使用API函数控制。

内存中的Python对象

Python中的每个变量都充当一个对象。对象可以是简单的(包含数字,字符串等),也可以是容器(字典,列表或用户定义的类)。此外,Python是一种动态类型化的语言,这意味着我们无需在程序中使用它们之前就声明变量或其类型。

例如:

如果看上面程序的前两行,对象x是已知的。当我们删除对象x并尝试使用它时,会收到一条错误消息,指出x未定义变量。

您会看到Python中的垃圾回收是完全自动化的,并且程序员无需担心,与C之类的语言不同。

修改垃圾收集器

Python垃圾收集器具有三代对象分类。生命周期起点的一个新对象是第一代垃圾收集器。随着对象在垃圾回收中幸存下来,它将被提升到下一代。3代垃圾收集器中的每一个都有一个阈值。具体地说,当超过分配数量的阈值减去解分配数量时,该生成将运行垃圾回收。

与上一代相比,前几代也更经常收集垃圾。这是因为较新的对象比旧的对象更有可能被丢弃。

该gc模块包括更改阈值,手动触发垃圾收集过程,禁用垃圾收集过程等功能。我们可以使用以下get_threshold()方法检查不同代垃圾收集器的阈值:

样本输出:(700, 10, 10)

如您所见,这里第一代的门槛为700,其他两代的门槛为10。

我们可以使用模块的set_threshold()方法来更改触发垃圾收集过程的阈值gc:

gc.set_threshold(900, 15, 15)

在上面的示例中,我们增加了所有3代的阈值。增加阈值将减少运行垃圾收集器的频率。通常,作为开发人员,我们无需过多考虑Python的垃圾回收,但这在为目标系统优化Python运行时时可能很有用。关键好处之一是Python的垃圾回收机制自动为开发人员处理了许多底层细节。

为什么要执行手动垃圾收集?

我们知道Python解释器会跟踪对程序中使用的对象的引用。在Python的早期版本(直到1.6版)中,Python解释器仅使用引用计数机制来处理内存。当引用计数降至零时,Python解释器会自动释放内存。这种经典的引用计数机制非常有效,只是当程序具有引用周期时它无法工作。如果一个或多个对象相互引用,则会发生一个引用循环,因此引用计数永远不会达到零。

让我们考虑一个例子。

上面的代码创建了一个引用循环,其中对象list引用了自身。因此,list当函数返回时,对象的内存将不会自动释放。参考循环问题无法通过参考计数来解决。但是,可以通过更改Python应用程序中垃圾回收器的行为来解决此参考周期问题。

为此,我们可以使用模块的gc.collect()功能gc。

结论

在本文中,我们讨论了如何通过使用引用计数和垃圾回收策略来自动处理Python中的内存管理。没有垃圾回收,就不可能在Python中实现成功的内存管理机制。另外,程序员不必担心删除已分配的内存,因为Python内存管理器会注意这一点。这样可以减少内存泄漏并提高性能。


大魔王Hacker


Python管理内存:

python官网:

https://docs.python.org/3.5/c-api/memory.html

python3.5版本

Memory Management

Overview

Python中的内存管理涉及包含所有Python对象和数据结构的私有堆。此私有堆的管理由Python内存管理器在内部确保。Python内存管理器有不同的组件,它们处理各种动态存储管理方面,如共享,分段,预分配或缓存。

在最底层,原始内存分配器确保私有堆中有足够的空间通过与操作系统的内存管理器交互来存储所有与Python相关的数据。在原始内存分配器之上,几个特定于对象的分配器在同一个堆上操作,并实现适合于每个对象类型的特性的不同的内存管理策略。例如,整数对象在堆中的管理方式与字符串,元组或字典不同,因为整数意味着不同的存储要求和速度/空间权衡。Python内存管理器因此将一些工作委托给特定于对象的分配器,但确保后者在私有堆的边界内操作。

重要的是要理解Python堆的管理由解释器本身执行,并且用户不能控制它,即使她经常操纵到该堆内部的内存块的对象指针。Python内存管理器通过本文档中列出的Python / C API函数根据需要执行Python对象和其他内部缓冲区的堆空间分配。

为了避免内存损坏,扩展编写器不应尝试对C库导出的函数操作Python对象:malloc(),calloc(),realloc()和free()。这将导致C分配器和Python内存管理器之间的混合调用具有致命后果,因为它们实现不同的算法并对不同的堆执行操作。但是,可以使用C库分配器为单独的目的安全地分配和释放内存块,如以下示例所示:


PyObject *res;

char *buf = (char *) malloc(BUFSIZ); /* for I/O */

if (buf == NULL)

return PyErr_NoMemory();

...Do some I/O operation involving buf...

res = PyBytes_FromString(buf);

free(buf); /* malloc'ed */

return res;


在这个例子中,I / O缓冲器的存储器请求由C库分配器处理。Python内存管理器仅涉及作为结果返回的字符串对象的分配。

然而,在大多数情况下,建议从Python堆分配内存,特别是因为后者在Python内存管理器的控制下。例如,当解释器使用以C编写的新对象类型扩展时,这是必需的。使用Python堆的另一个原因是希望通知 Python内存管理器有关扩展的内存需求模块。即使当所请求的存储器专门用于内部的高度特定目的时,将所有存储器请求委托给Python存储器管理器也会导致解释器作为整体具有更准确的其存储器占用的映像。因此,在某些情况下,Python内存管理器可能或可能不会触发适当的操作,如垃圾容器,内存压缩或其他预防性过程。注意,通过使用C库分配器,如前面的例子所示,为I / O缓冲区分配的内存完全转储Python内存管理器。

也可以看看 PYTHONMALLOCSTATS环境变量可用于在创建新对象竞技场时和关闭时打印内存分配统计信息。

Raw Memory Interface

以下函数集是系统分配器的包装器。这些函数是线程安全的,GIL不需要保持。

The default raw memory block allocator uses the following functions: malloc(), calloc(), realloc() and free(); call malloc(1) (or calloc(1, 1)) when requesting zero bytes.

版本3.4中的新功能。

void* PyMem_RawMalloc(size_t n)

分配n字节,如果请求失败,则返回类型为void*的指针到分配的内存,或NULL。

如果可能,请求零字节返回一个明确的非NULL指针,如同PyMem_RawMalloc(1)被调用。内存不会以任何方式初始化。

void* PyMem_RawCalloc(size_t nelem, size_t elsize)

分配字节大小为elsize的nelem元素,并将void*类型的指针返回到分配的内存,或NULL 如果请求失败。存储器初始化为零。

如果可能,请求零大小零元素或零大小的元素返回一个非NULL指针,如同PyMem_RawCalloc(1, 1) t3 >。

版本3.5中的新功能。

void* PyMem_RawRealloc(void *p, size_t n)

调整p指向n字节指向的存储器块大小。内容将不变为旧和新尺寸的最小值。

如果p是NULL,则该调用相当于PyMem_RawMalloc(n);否则如果n等于零,则调整存储器块的大小,但不释放,返回的指针为非NULL。

Unless p is NULL, it must have been returned by a previous call to PyMem_RawMalloc(), PyMem_RawRealloc() or PyMem_RawCalloc().

如果请求失败,PyMem_RawRealloc()返回NULL和p仍然是前一个内存区域的有效指针。

void PyMem_RawFree(void *p)

Frees the memory block pointed to by p, which must have been returned by a previous call to PyMem_RawMalloc(), PyMem_RawRealloc() or PyMem_RawCalloc(). 否则,或者如果PyMem_Free(p)之前被调用,则发生未定义的行为。

如果p为NULL,则不执行操作。

Memory Interface

以下函数设置,建模在ANSI C标准之后,但指定请求零字节时的行为,可用于从Python堆分配和释放内存。

The default memory block allocator uses the following functions: malloc(), calloc(), realloc() and free(); call malloc(1) (or calloc(1, 1)) when requesting zero bytes.

警告使用这些功能时,必须保持GIL。

void* PyMem_Malloc(size_t n)

如果请求失败,则分配n字节,并向分配的内存返回类型为void*的指针,或返回NULL。

如果可能,请求零字节返回一个明确的非NULL指针,如同已经调用PyMem_Malloc(1)。内存不会以任何方式初始化。

void* PyMem_Calloc(size_t nelem, size_t elsize)

分配字节大小为elsize的nelem元素,并将void*类型的指针返回到分配的内存,或NULL 如果请求失败。存储器初始化为零。

如果可能,请求零大小零元素或大小零字节的元素返回一个明确的非NULL指针,如同PyMem_Calloc(1, 1) t3 >。

版本3.5中的新功能。

void* PyMem_Realloc(void *p, size_t n)

调整p指向n字节指向的存储器块大小。内容将不变为旧和新尺寸的最小值。

如果p是NULL,则调用相当于PyMem_Malloc(n);否则如果n等于零,则调整存储器块的大小,但不释放,返回的指针为非NULL。

Unless p is NULL, it must have been returned by a previous call to PyMem_Malloc(), PyMem_Realloc() or PyMem_Calloc().

如果请求失败,PyMem_Realloc()返回NULL和p仍然是前一个内存区域的有效指针。

void PyMem_Free(void *p)

Frees the memory block pointed to by p, which must have been returned by a previous call to PyMem_Malloc(), PyMem_Realloc() or PyMem_Calloc(). 否则,或者如果PyMem_Free(p)之前被调用,则发生未定义的行为。

如果p为NULL,则不执行操作。

为方便起见,提供了以下面向面向的宏。请注意,TYPE是指任何C类型。

TYPE* PyMem_New(TYPE, size_t n)

与PyMem_Malloc()相同,但分配(n * sizeof(TYPE)) t3 >字节的内存。返回一个转换为TYPE*的指针。内存不会以任何方式初始化。

TYPE* PyMem_Resize(void *p, TYPE, size_t n)

与PyMem_Realloc()相同,但是内存块调整为(n * sizeof(TYPE) t6>字节。返回一个转换为TYPE*的指针。返回时,p将是指向新存储区的指针,或者在发生故障时NULL。

这是一个C预处理器宏; p总是重新分配。保存p的原始值以避免在处理错误时丢失内存。

void PyMem_Del(void *p)

与PyMem_Free()相同。

此外,提供以下宏集直接调用Python内存分配器,而不涉及上面列出的C API函数。但是,请注意,它们的使用不保留跨Python版本的二进制兼容性,因此在扩展模块中已被弃用。

PyMem_MALLOC(size)

PyMem_NEW(type, size)

PyMem_REALLOC(ptr, size)

PyMem_RESIZE(ptr, type, size)

PyMem_FREE(ptr)

PyMem_DEL(ptr)

Customize Memory Allocators

版本3.4中的新功能。

PyMemAllocatorEx

用于描述内存块分配器的结构。该结构有四个字段:

在版本3.5中已更改: PyMemAllocator结构已重命名为PyMemAllocatorEx,并添加了新的calloc字段。

PyMemAllocatorDomain

用于标识分配器域的枚举。域:

PYMEM_DOMAIN_RAW: functions PyMem_RawMalloc(), PyMem_RawRealloc(), PyMem_RawCalloc() and PyMem_RawFree()

PYMEM_DOMAIN_MEM: functions PyMem_Malloc(), PyMem_Realloc(), PyMem_Calloc() and PyMem_Free()

PYMEM_DOMAIN_OBJ:functions PyObject_Malloc(),PyObject_Realloc(),PyObject_Calloc()和PyObject_Free()

void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

获取指定域的内存块分配器。

void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

设置指定域的内存块分配器。

当请求零字节时,新的分配器必须返回一个不同的非空指针。

对于PYMEM_DOMAIN_RAW域,分配器必须是线程安全的:调用分配器时,GIL不会保持。

如果新的分配器不是一个钩子(不调用前面的分配器),必须调用PyMem_SetupDebugHooks()函数重新安装调试钩子在新的分配器。

void PyMem_SetupDebugHooks(void)

设置挂钩以检测以下Python内存分配器函数中的错误:

PyMem_RawMalloc(),PyMem_RawRealloc(),PyMem_RawCalloc(),PyMem_RawFree()

PyMem_Malloc(),PyMem_Realloc(),PyMem_Calloc(),PyMem_Free()

PyObject_Malloc(),PyObject_Realloc(),PyObject_Calloc(),PyObject_Free()

新分配的内存用字节0xCB填充,释放的内存用字节0xDB填充。附加检查:

detect API violations, ex: PyObject_Free() called on a buffer allocated by PyMem_Malloc()

在缓冲区开始之前检测写(缓冲区下溢)

检测写缓冲区结束后(缓冲区溢出)

如果Python没有被编译为调试模式,该函数不执行任何操作。

Customize PyObject Arena Allocator

对于小于512字节的分配,Python有一个pymalloc分配器。这个分配器针对寿命短的小对象进行了优化。它使用固定大小为256 KB的称为“arenas”的内存映射。对于大于512字节的分配,它返回到PyMem_RawMalloc()和PyMem_RawRealloc()。pymalloc是PyObject_Malloc()使用的默认分配器。

默认的竞技场分配器使用以下功能:

VirtualAlloc()和VirtualFree()

mmap()和munmap()如果可用,

malloc()和free()。

版本3.4中的新功能。

PyObjectArenaAllocator

用于描述竞技场分配器的结构。该结构有三个字段:

PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)

获取竞技场分配器。

PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)

设置竞技场分配器。

Examples

这是从Overview部分的示例,重写,以便使用第一个函数集从Python堆分配I / O缓冲区:

PyObject *res;

char *buf = (char *) PyMem_Malloc(BUFSIZ); /* for I/O */

if (buf == NULL)

return PyErr_NoMemory();

/* ...Do some I/O operation involving buf... */

res = PyBytes_FromString(buf);

PyMem_Free(buf); /* allocated with PyMem_Malloc */

return res;

相同的代码使用面向类型的函数集:


PyObject *res;

char *buf = PyMem_New(char, BUFSIZ); /* for I/O */

if (buf == NULL)

return PyErr_NoMemory();

/* ...Do some I/O operation involving buf... */

res = PyBytes_FromString(buf);

PyMem_Del(buf); /* allocated with PyMem_New */

return res;


注意,在上面的两个例子中,缓冲区总是通过属于同一集合的函数来操作。实际上,对于给定的存储器块,需要使用相同的存储器API族,使得将不同分配器混合的风险降低到最小。以下代码序列包含两个错误,其中一个标记为fatal,因为它混合了在不同堆上操作的两个不同的分配器。


char *buf1 = PyMem_New(char, BUFSIZ);

char *buf2 = (char *) malloc(BUFSIZ);

char *buf3 = (char *) PyMem_Malloc(BUFSIZ);

...

PyMem_Del(buf3); /* Wrong -- should be PyMem_Free() */

free(buf2); /* Right -- allocated via malloc() */

free(buf1); /* Fatal -- should be PyMem_Del() */


除了用于处理来自Python堆的原始内存块的函数之外,Python中的对象还通过PyObject_New(),PyObject_NewVar()和PyObject_Del()。

这些将在下一章中关于在C中定义和实现新的对象类型进行解释。


在推荐一个python内存管理的网址

https://realpython.com/python-memory-management/#memory-management-from-hardware-to-software

Memory Management in Python


不平常的芝麻


一、为什么进行内存管理

先理解一下为什么要进行内存管理,内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源。二、内存的分配方式

内存分配方式有四种:(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,存放全局变量和静态变量,这些内存在程序运行期间都存在。(2)在栈上创建。由编译器自动分配自动释放,用于存放局部变量和参数,栈内的对象先进后出,所以先创建的对象会后析构。栈由于是编译器自动管理的,所以栈内的对象不会存在内存泄露问题,并且效率很高,但是分配的内存容量有限。(3)从堆上分配,亦称动态内存分配。程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。(4)常量区:存放常量字符串,程序结束后由系统释放

三、python是怎么管理内存的:

python的内存管理是由私有的heap空间管理的,所有的python对象和数据结构都在一个专有的heap(堆),程序员没有访问该heap的权限,只有解释器才能对他进行操作。而python的heap空间分配是由内存管理模块进行的,其核心API会提供一些访问该模块的方法提供程序员使用。python自带的垃圾回收系统,它会回收并释放没有被使用的内存,让她们能够被其他程序使用(内存池。被释放后先回到内存池然后再被别的程序所运用)




馨满溢足的家


Python是通过引入了一个机制:引用计数,来进行管理内存。

python内部使用引用计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引用,即引用计数,当对象被创建时就创建了一个引用计数,当对象不再需要时,这个对象的引用计数为0时,它被垃圾回收。


分享到:


相關文章: