面试:python内存管理原理及流程

你将得到:

  • 完整的面试回答
  • 一些结合业务的面试问题
  • 结合参考文献可以更容易理解原理

图什么的后续会补的啦~

面试的时候可以这么回答~

首先,在内存分配上,如果超过256KB的大变量 由c的malloc分配,如果没有超过256kb的小变量则使用内存池技术由pymalloc分配。

内存池技术是指预先在内存中申请一定数量的,大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够之后再申请新的内存。

这样做最显著的优势就是能够减少内存碎片,提升效率

其次,新建变量分配新的名字 或者放到容器 时,引用计数增加,使用del、重新赋值或者容器销毁时引用计数减少。引用计数为0时启动析构函数,该析构函数__del__()同样使用内存池技术,避免频繁申请和释放内存。

再次,当存在循环引用 时,del无法使引用计数归零从而造成内存泄漏 ,此时则引入垃圾回收的标记-清除机制Mark-Sweep

它会从根集(如全局命名空间、调用栈等)出发,遍历所有活动对象,标记通过一系列引用所能到达的对象为可达(reachable)的对象。

然后启动清除机制,再次遍历,将所有未被标记为可达的对象视为不可达(unreachable)的对象,它们将会被free释放内存。

但是由于启动标记-清除机制时应用程序是暂停的,引入了垃圾回收的分代回收Generational Collection机制 ,以空间换时间的方式提高回收效率。分代回收的思想是,存活时间越长的对象越有可能继续存活,因此随着代的增加,回收的频率也逐渐降低 。分代回收可以减少垃圾回收的总体开销,因为频繁回收的主要集中在生命周期短的对象(第0代)

具体地,

新建变量都被列为第0代,

如果第一次gc扫描时没有被清除则进入第1代,

同理,在对第1代gc扫描时没有被清除的进入了第2代。

而gc扫描的启动是根据分代回收阈值参数设置,

当 <math xmlns="http://www.w3.org/1998/Math/MathML"> 新建变量 − 被释放的变量 ≥ 第 0 代 g c 扫描的阈值 新建变量-被释放的变量\geq 第0代gc扫描的阈值 </math>新建变量−被释放的变量≥第0代gc扫描的阈值 时,则会启动第0代的gc扫描,

当第0代的gc扫描启动的次数达到第1代回收阈值时,则会启动第1代的gc扫描,

同理,当第1代的gc扫描启动的次数达到第2代回收阈值时,则会启动第2代的gc扫描,即全代扫描。

某段时间内如何使特定对象不被垃圾回收?

除了使用gc之外,还可以:

当需要某段时间内某些对象不被垃圾回收,那么在循环引用的基础上可以使用另一个变量去引用它们其中之一,则时间段内三者均不会被垃圾回收.也即

<math xmlns="http://www.w3.org/1998/Math/MathML"> c → a ↔ b c \rightarrow a \leftrightarrow b </math>c→a↔b

在时间段结束后,删除或给另一个变量重新赋值等方法减少引用计数,循环引用过的变量则会在后续gc扫描时被垃圾回收。

<math xmlns="http://www.w3.org/1998/Math/MathML"> c ↛ a ↔ b c \nrightarrow a \leftrightarrow b </math>c↛a↔b

如何手动触发垃圾回收?

python 复制代码
import gc
# 可以手动触发全代垃圾回收
gc.collect() 

# 只触发0代的垃圾回收
collected_gen0 = gc.collect(0)
print(f"Collected {collected_gen0} objects from generation 0.")

其他面试问题

  1. 性能优化问题

    • 在处理大数据集时,如何通过Python的内存管理策略来优化你的程序性能?
    • 描述一种场景,你需要手动控制垃圾回收。你会如何实施,并解释为什么这样做有助于提升应用性能?
  2. 内存泄漏定位

    • 如果你怀疑一个Python应用有内存泄漏,你会如何定位问题源头?请描述你的步骤和使用的工具。
    • 你能否给出一个例子,说明如何使用gc模块来识别和解决循环引用导致的内存泄漏问题?
  3. 内存分配策略

    • 在设计一个需要高频率创建和销毁大量小对象的应用时,你会如何优化内存使用?
    • Python中有哪些机制可以帮助减少内存碎片?你在实际开发中是如何应用这些机制的?
  4. 垃圾回收机制对业务的影响

    • 描述一种业务场景,其中Python的自动垃圾回收可能会导致性能问题。你会如何预防或解决这些问题?
    • 在实时数据处理系统中,垃圾回收可能引起的延迟是一个问题。讨论你可以采用的几种策略来最小化这种延迟。
  5. 分代垃圾回收的具体应用

    • Python的分代垃圾回收机制如何影响对象的生命周期管理?在什么情况下,调整这些参数可能会提高程序的效率?
    • 你有没有实际例子,你通过调整垃圾回收阈值来解决内存问题或改善性能?

原理

Python 使用一种名为"自动内存管理"的机制,主要包括以下几个方面:

  • 引用计数 :Python 内部使用引用计数机制来跟踪每个对象有多少引用指向它 sys.getrefcount(obj)。当某个对象的引用计数为0时,就列入了垃圾回收队列。
    • 引用计数增加 的情况:
      • 一个对象被分配给一个新的名字(例如:a=[1,2])
      • 将其放入一个容器中(如列表、元组或字典)(例如:c.append(a))
    • 引用计数减少 的情况:
      • 使用del语句对对象别名显式的销毁(例如:del b)
      • 对象所在的容器被销毁或从容器中删除对象(例如:del c )
      • 引用超出作用域或被重新赋值(例如:a=[3,4])
  • 垃圾回收 :Python的垃圾回收机制采用引用计数机制为主,标记-清除和分代回收机制为辅的策略。
    • 标记-清除机制 用来解决计数引用带来的循环引用而无法释放内存的问题,即两个或更多对象互相引用,导致它们的引用计数永远不会达到零,进而导致内存泄漏的问题。循环引用只有在容器对象才会产生,比如字典,元组,列表等。
      • 标记阶段,遍历所有活动对象,并标记所有可达(reachable)的对象。可达的对象即是那些从根集(如全局命名空间、调用栈等)出发,通过一系列引用所能到达的对象
      • 清除阶段,所有未被标记为可达的对象被视为不可达(unreachable),这些对象将被垃圾回收器清理。
    • 分代回收机制 Generational Collection是为提升垃圾回收的效率。它是基于这样一种统计事实:"对象存在时间越长,越可能不是垃圾,应该越少去收集 " 这样在执行标记-清除算法时可以有效减小遍历的对象数,从而提高垃圾回收的速度,是一种以空间换时间的方法策略。
      • Python将所有的对象分为年轻代(第0代)、中年代(第1代)、老年代(第2代)三代。所有的新建对象默认是 第0代对象。当在第0代的gc扫描中存活下来的对象将被移至第1代,在第1代的gc扫描中存活下来的对象将被移至第2代。gc扫描次数(第0代>第1代>第2代)

      • 当某一代中被分配的对象与被释放的对象之差达到某一阈值时,就会触发当前一代的gc扫描。当某一代被扫描时,比它年轻的一代也会被扫描,因此,第2代的gc扫描发生时,第0,1代的gc扫描也会发生,即为全代扫描。

        比如gc.get_threshold()即分代回收机制的参数阈值设置为(700,10,10)时,代表着当新分配的对象数量减去释放的对象数量等于700时触发第0代gc扫描,如果触发了10次第0 代gc扫描,则会启动1次第1代扫描,进而如果触发了10次第1代gc扫描,则会启动第2代扫描,即全代扫描。

  • 内存池PyMalloc技术:Python 使用内存池技术来管理小对象 的内存分配。通过预分配内存块来管理小对象,减少系统调用,提高内存分配效率。内存池的作用就是预先在内存中申请一定数量的,大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够之后再申请新的内存。这样做最显著的优势就是能够减少内存碎片,提升效率。
    • Level+3层:对于python内置的对象(比如int,dict等)都有独立的私有内存池,对象之间的内存池不共享,即int释放的内存,不会被分配给float使用
    • Level+2层:当申请的内存大小小于256KB时,内存分配主要由 Python 对象分配器(Python's object allocator)实施 , 也就是使用内存池技术
    • Level+1层:当申请的内存大小大于256KB时,由Python原生的内存分配器进行分配,本质上是调用C标准库中的malloc/realloc等函数

参考文献

面试必备:Python内存管理机制

相关推荐
李重楼13 分钟前
前端性能优化之 HTTP/2 多路复用
前端·面试
lecepin2 小时前
AI Coding 资讯 2025-09-17
前端·javascript·面试
数据智能老司机3 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
闰五月4 小时前
JavaScript作用域与作用域链详解
前端·面试
数据智能老司机4 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机4 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机4 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
顾林海4 小时前
Android编译插桩之AspectJ:让代码像特工一样悄悄干活
android·面试·性能优化
poemyang4 小时前
技术圈的“绯闻女孩”:Gossip是如何把八卦秘密传遍全网的?
后端·面试·架构
c8i4 小时前
drf初步梳理
python·django