资讯动态

什么是Python的“内存管理机制”

发布时间:2026/9/29 15:03:25 来源:尧图企业网站定制
什么是内存管理器what作为一个把解释性、编译性、互动性和面向对象这几种特性结合到一起去的高层次的脚本语言, 它和那种大多数的编程语言是不同的, 在它的体系里, 变量是不需要提前事先声明的, 变量也是完全不需要去指定具体的类型的, 对于程序员来说, 根本就不用操心什么内存管理的事情, 因为那个什么叫做解释器一样的东西, 会给你自动地进行回收处理的。开发人员是完全不用花费过多的精力去关心这个所谓内存管理机制的问题的, 因为这些所有比较复杂的内存管理工作, 全部都是交给了那个什么叫做内存管理器这个东西去承担了的。内存这一事项, 不外乎就是创建和销毁这两部分内容。所以这篇论文将围绕着内存 pool 以及垃圾回收这两个部分, 来进行相关的分析和论述工作。那么, 人们为什么必须要去引入所谓的内存池这样一个东西呢。当创建大量消耗小内存的对象时, 频繁调用new会导致大量的内存碎片, 致使效率降低。内存池的作用就是预先在内存中申请一定数量的, 大小相等的内存块留作备用。当有新的内存需求时, 就先从内存池中分配内存给这个需求, 不够之后再申请新的内存。这样做最显著的优势就是能够减少内存碎片, 提升效率。中的内存管理机制为关于内存池的工作机制how, 首先我们来看一张解释器的内存架构图。的对象管理主要位于Level1~Level3层在Level加3层这个层级里面, 对于像 int 和 dict 这些东西的内置对象, 它们都会有自己独立的、私有的内存池, 各个对象之间的内存池是不共享的, 这就意味着, 如果某个 int 对象释放了它所占用的内存, 那么这块被释放出来的内存, 是绝对不会被分配去给 float 使用的。在Level2这一层, 假如申请的内存大小比256KB要小, 那么内存这个分配工作, 主要就是由那个对象分配器来做的。在第Level1层上, 如果所申请的内存容量大于256KB的话, 就会交给原生的内存分配去完成这项工作, 而本质上呢, 这一操作就是直接呼叫C标准库里头的像等之类的函数。关于内存释放这个问题, 我们会注意到, 在一个对象的那个引用计数变成0的这种状态下的时候, 它就会去调用其自己的那个析构函数。然而, 这里必须说清楚的一个事实是, 调用这个析构函数的这一行为, 它并不代表着最终就一定能够调用到free这个操作去彻底地释放掉对应的内存空间。为什么要这么说呢? 因为假如真的就是按照那种每一次都去执行申请、然后每次又都去执行释放的这种方式来操作的话, 那么程序的这种整体的执行程序效率就会面临着被大幅度降低的一个风险。正因为意识到了这个潜在的问题的存在性, 所以在设计的时候, 系统就采用了一种类似于内存池机制的一种东西。具体的表现方式是这样的, 就是从那个所谓的内存池里面所预先申请到的那些内存在使用完毕之后, 是不会直接被彻底回收掉的, 而是要把它们归还到该内存池内部去的, 这样做的一个根本目的, 就是为了能够有效避免那些过于频繁的针对内存的申请和释放的这些动作的发生频率过高。垃圾回收机制这个垃圾回收机制采取了以引用计数机制为主, 同时搭配标记-清除机制以及分代回收机制为辅的策略。具体来说, 标记-清除机制被用来处理因为循环引用所导致的内存无法释放的问题, 而分代回收机制则是为了提升整体垃圾回收的效率。引用计数通过引用计数来保存内存中的变量追踪, 也就是记录该对象被其他使用的对象引用的次数。在系统中, 存在一个内部跟踪的变量, 该变量的名字叫做引用计数器。这个概念的核心含义是每个变量所拥有多少次的引用情况, 人们通常把这个内容简称为引用计数。一旦某个具体的对象其引用数量降到了零这个数字的状态时, 那么就会被列入到垃圾回收队列之中去处理。需要注意这样一种情况, 那就是当变量a作为参数在执行传递动作的时候, 系统内部会产生一个临时的引用关系存在, 所以最终得到的结果相比于真实存在的实际情况而言, 会多出一个一的数值表现。引用计数增加的情况将一个对象分配给一个全新的名字, 就像我们看到的这个情况, a等于。把它放到一个容器里面去, 这个容器可以是列表, 也可以是元组, 或者字典, 比如写成c.(a)这样的形式。在引用计数被减少的这种特定情况里面。可以使用del这种语句操作的方式针对对象别名进行显式的销毁行为, 比如对字母b采用del b这样的写法来实现目的。当该对象所在的容器遭到销毁, 或者从其中清除掉该对象的时候比如执行类似于del c这样的操作。引用超出作用域或被重新赋值例如a引用计数虽然能够处理大多数的垃圾回收相关情形, 然而一旦面临两个对象相互引用的状况, 就会出现麻烦, 尽管使用del语句能够对引用次数实施减少操作, 可是因为引用数量不会变为零点, 所以这些对象依然得不到销毁的结果, 最终就导致了内存泄漏方面的难题, 为了解决此种状况, 于是便引入了标记-清除这一种机制。先标记出来, 然后再清除掉。标记 - 清除这个办法, 是用来解决引用计数机制所产生出的那个循环引用问题的, 这个循环引用问题, 会引致内存泄漏的问题发生。这种循环引用的现象, 是只有当存在容器对象的时候才会产生的, 这些容器对象比如说有字典, 有元组, 还有列表等等都是这样的。顾名思义, 这个机制在进行垃圾回收操作的时候, 分成了两步来完成这两个步骤, 具体分别就是:在标记阶段会去遍历所有的对象, 如果某个对象是可达的, 说白了就是还有其它对象引用着它, 那么就会把这个对象给标记为可达。在进入清除这个阶段的时候, 我们要再次去全面地遍查所有对象, 如果在这一过程中发现某个特定的对象没有被标记成处于可达状态, 意思就是它是不达的, 那么我们就应该把这个对象给清理掉并回收起来。前面讲的那些垃圾清理步骤, 得把整个应用先停下来, 等把标记和清除这些事儿全弄完了, 才能让应用接着跑。想快点把这暂停的时间缩短点呢, 就用了一种叫“分代回收”的法子。这法子是用多占点内存空间的方式, 去换个时间效率, 好让清垃圾的速度变得更快些。分代回收分代回收这一机制, 是建立在一个已经得到统计证实的事实之上的。这个事实指出, 在程序运行期间, 存在着一定比例的内存块它们的生存周期是比较短的。与此同时, 剩下的那些内存块呢, 它们的生存周期会比较长一些。有的甚至从程序开始执行的那一瞬间起, 一直持续到整个程序结束之前。那些生存期比较短的对象, 它们所占的比例, 通常情况下大多是在百分之八十到百分之九十之间的。基于上述情况, 我们可以简单地进行这样一个认定和理解: 对象在内存中存在的时间越长, 它就不越可能是垃圾。既然它不是垃圾, 那么就应当越少去收集它。这种方法在执行标记以及清除算法的过程当中, 可以更加有效且切实地减少需要遍历的对象数量, 进而帮助提升垃圾回收操作的运行速度与工作效率, 整体而言这是一种通过增加空间占用以换取时间节省的手段和策略。把全部的对象划归为年轻代、也就是第0代, 中年代、即是第1代, 老年代、相当于第2代, 总共分成三代。所有新创建出来的对象, 默认情况下就是第0代对象这种身份。要是当在第0代里面执行垃圾回收扫描工作的时候, 那些对象还能够存活下去的话, 它们就会被打上标签并移动到第1代去, 若是再当在第1代的垃圾回收扫描环节里, 还有对象没能被淘汰而选择存活下来时, 它们就会顺理成章地被转移到第2代这个层级之中去。强引用垃圾收集器进行扫描的次数排序为, 第一级别是第零代对象, 第二级别是第一代对象, 第三级别是第二代对象。只要在某一代中, 被分配的对象数量和被释放的对象数量之间的差值达到了规定的阈值, 系统就会触发该代垃圾回收扫描任务。每当某一代执行扫描操作时, 所有比它更年轻的后代也必须一并参与扫描, 所以在全代会发生时, 第零代、第一代都会跟着一起扫, 这被称为全代扫描。image.png请大家思考一下, 在标记清除算法里边的情况是这样的。假设说对象C也拿着个指针指向对象A。那么当程序执行那个删除操作的时候。具体来说就是执行del操作之后。到底会引发什么样的结果呢? 然后关于对象A、对象B还有对象C它们彼此之间的引用关系。具体的图示内容如下边所示:在执行了del这个操作之后, 引用关系的现状显示在下面这张图里面。在执行了垃圾回收扫描操作之后, 进行了一次小结。从总体情况来讲, 采取内存池这种方式是为了把内存碎片化减少掉以此来让执行效率变得更高一些。主要依靠引用计数这个技术手段来把垃圾回收的工作给完成掉。同时通过标记与清除这种操作来解决因为容器对象存在循环引用而引发出来的问题。另外再配合着分代回收这个方法把垃圾回收整体的效率给提升上去。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑