Python中的内存分配

504 阅读3分钟

Memory allocation in Python

在这篇文章中,我们深入介绍了Python中的内存分配,以及分配内存的类型、内存问题、垃圾回收等。

目录

  1. 什么是内存分配?
  2. Python内存分配如何工作?
  3. 良好的Python内存管理实践。
  4. Python内存问题

什么是内存分配?

它是指在计算机内存中为程序分配一个内存块的过程。理解内存分配是编写快速高效程序的关键,尽管现在的计算机往往有大量的内存。

Python内存分配是如何工作的?

在后面的章节之前,重要的是要了解在Python中所有的东西都是一个对象。
对于我们大多数Python开发者来说,这并不奇怪,这
是因为内存分配(内存管理的一个子集)是自动为我们完成的。这意味着你不会看到mallocfree函数(C程序员熟悉的)散布在python应用程序中。

python的内存管理是由Python内存管理器(解释器的一部分)完成的。

为了优雅地处理内存管理,Python内存管理器使用引用计数算法。基本上,它跟踪每个为程序分配的内存块的引用计数。

引用基本上是我们在程序中使用的变量。它们是对内存块的引用:

   # Remember 5 is object of class <int>
   # So variables(references) x, y, z are all referring to the same object in memory
   x = 5
   y = x
   z = x
   
   # Assiging new values to the variables
   x = None
   y = None
   z = None
   
   # As we re-assign x to None, the reference count decrements by one and the same happens
   # with the rest of the re-assignments

python解释器有一个垃圾收集器,如果对该内存的引用计数为零,它就会将之前分配的内存去掉。

内存类型

堆内存
Python使用一个私有堆来存储所有的Python对象和数据结构体。作为开发者,我们对私有堆的控制权为零,然而,有一些方法可以优化我们程序的内存效率。

arr = [1, 2, 3] # Stored on the heap

def func():
    a = 10 # The value (object) 10 is stored on the heap and the function stack frame will only hold a reference to that value. 
    pass

为了优化内存管理,堆被进一步细分。

竞技场
Python有几个内存分配器,每个分配器都针对特定情况进行了优化,即为小对象和大对象分配。其中一个是pymalloc,它为小对象 (<= 512B) 进行了优化。pymalloc使用*C malloc()*函数来分配内存池,然后用它来处理后续的内存请求。

简而言之,竞技场是用来处理内存请求的,而不需要重新分配新的内存。

池子
在竞技场中,我们有一个池,它的大小与操作系统的页面大小相同,但默认情况下,python假定页面大小为4KB。
池被分割成块,每个池由块组成,这些块根据被请求的内存大小而对应于相同的大小等级。

Memory allocation in Python

每个池都有自由块指针(单链表),指向池中的自由块。

池可以有3种状态:
已使用。该池有可用的数据块。
池中的所有块都已被分配并包含数据。
。池中没有数据,当请求时,可以为块分配任何大小的类别。

区块
也许我们已经在前面的段落中暗示了区块,但为了补充,区块可以有3种状态。
未被触及。没有被分配过
free。区块已被分配但被释放,它现在包含相关的数据
**。**已经被分配并包含相关数据

堆栈内存
这个内存空间只为函数调用而分配。它持有对函数局部变量的引用(参数也包括在内)。

在前面的语句中,我强调了引用这个词,因为实际的值是存储在私有堆中的。

堆栈是后进先出(LIFO)的数据结构*,也就是说,最后进入堆栈的项目是第一个出来的项目。我希望你能明白一些递归的工作原理*(一堆堆的堆栈框架)**。

当函数被调用时,一个堆栈框架被分配,而当函数返回或退出时,堆栈框架被销毁。

良好的python内存管理实践

良好的内存管理的本质是利用较少但足够的内存,使我们的程序能够与其他程序一起运行。你可以通过遵守以下几点来优化你的python程序的内存使用。

a.避免列表分片:

arr = [1, 2, 3, 4, 4, 6]

new_arr = arr[2:] # O(M) where M is the length of sliced array
# Instead use indicies to track the list portions you need
first_pos = 2
last_pos = 5

b.避免字符串串联:

    s = "Hello"
    new_s = s + " World!" # O(N + M)Highly costly for even large strings
    # Where N and M are lengths of two strings being concatenated.
    # Use join()
    
    new_str = ''.join(list(s)) # join() is O(N) where N is the length of the string to be concatenated, In our case it is <s>.

Python的内存问题

因此,在某些情况下,Python内存管理器可能会或可能不会触发适当的动作,如垃圾收集、内存压缩或其他预防性程序。
*来自Python 3内存管理文档

由于Python内存管理器在某些时候未能清除内存,程序的性能会下降,因为一些未使用的引用没有被释放。这就是所谓的内存泄漏

为了解决内存泄漏问题,我们可以使用tracemalloc,这是python 3.4中引入的一个内置模块。

软件领域已经转变为编写能够工作的最佳代码,而不仅仅是能够工作的代码。在编写软件的同时,考虑到它在解决预定问题上的功效,使我们能够直观地看到软件的极限。