【浅析】Python的内存管理机制

news/2024/7/20 14:55:22 标签: python, 内存管理, 软件

python的内存管理分为三个方面:" style="">

python的内存管理分为三个方面:" style="">python内存管理分为三个方面:

  • 引用计数
  • 垃圾回收
  • 内存池机制

python01/#浅析引用计数" class="headerlink" title="浅析引用计数" style="margin:0px; padding:0px; border:0px; font-style:inherit; font-variant:inherit; font-weight:inherit; font-size:undefined; line-height:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,102,153)" rel="noopener noreferrer">浅析引用计数

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

引用计数增加的情况:

1.对象被创建:x=4
2.另外的别人被创建:y=x
3.被作为参数传递给函数:foo(x)
4.作为容器对象的一个元素:a=[1,x,’33’]

引用计数减少情况

1.一个本地引用离开了它的作用域。比如上面的foo(x)函数结束时,x指向的对象引用减1。
2.对象的别名被显式的销毁:del x ;或者del y
3.对象的一个别名被赋值给其他对象:x=789
4.对象从一个窗口对象中移除:myList.remove(x)
5.窗口对象本身被销毁:del myList,或者窗口对象本身离开了作用域。

如何获取一个变量的引用计数


>> import sys
>> x = 1
>> sys.getrefcount(x)
599
>> y = x
>> sys.getrefcount(x)
600
>> del y
>> sys.getrefcount(x)
599

python01/#垃圾回收" class="headerlink" title="垃圾回收" style="margin:0px; padding:0px; border:0px; font-style:inherit; font-variant:inherit; font-weight:inherit; font-size:undefined; line-height:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,102,153)" rel="noopener noreferrer">垃圾回收

python的垃圾回收机制以引用计数为主,标记-清除和分代收集为辅。

  1. 引用计数
    优点:“实时性”,任何内存,一旦没有指向它的引用,就会立即被回收。
    缺点:
    (1). 效率底下:引用计数机制带来的计数操作,与引用赋值成正比。频繁的技术操作,会给CPU带来大量消耗。
    (2). 循环引用:也就是两个对象相互引用,这样的话,两个对象的引用计数永远不会为0,及它们永远不被清除。

  2. 标记-清除
    标记-清除是为了解决循环引用的问题。可以包含其他对象引用的容器对象(比如:list,set,dict,class,instance)都可能产生循环引用。
    2.1 假设
    如果两个对象的引用计数都为1的话,但仅仅存在它们之间的相互引用,那么,我们可以认为这两个对象的实际引用计数为0.如果我们将这个引用循环去掉,那么它们的实际引用计数才会显现。
    案例:有循环引用的A,B两个对象,从A出发,因为它有一个对B的引用,则将B的引用计数减1;然后顺着引用达到B,因为B有一个对A的引用,同样将A的引用减1,这样,就完成了循环引用对象间环摘除。
    问题:如果A,B间没有循环引用,但A引用了B,B没有以用A,贸然的将B计数引用减1,而A没有被回收,这将导致在未来的某个时刻出现一个对B的悬空引用,类似与C的空指针异常。这就要求我们必须在A没有被删除的情况下复原B的引用计数,那么维护引用计数的复杂度将成倍增加。
    2.2 标记-清除的原理
    原理:
    我们并不改动真实的引用计数,而是将集合中对象的引用计数复制一份副本,改动该对象引用的副本。对于副本做任何的改动,都不会影响到对象生命走起的维护。
    这个计数副本的唯一作用是寻找root object集合(该集合中的对象是不能被回收的)。当成功寻找到root object集合之后,首先将现在的内存链表一分为二,一条链表中维护root object集合,成为root链表,而另外一条链表中维护剩下的对象,成为unreachable链表。之所以要剖成两个链表,是基于这样的一种考虑:现在的unreachable可能存在被root链表中的对象,直接或间接引用的对象,这些对象是不能被回收的,一旦在标记的过程中,发现这样的对象,就将其从unreachable链表中移到root链表中;当完成标记后,unreachable链表中剩下的所有对象就是名副其实的垃圾对象了,接下来的垃圾回收只需限制在unreachable链表中即可。
    效率分析:
    从垃圾收集机制来看,这种垃圾收集机制所带来的额外操作实际上与系统中总的内存块的数量是相关的,当需要回收的内存块越多时,垃圾检测带来的额外操作就越多,而垃圾回收带来的额外操作就越少;反之,当需回收的内存块越少时,垃圾检测就将比垃圾回收带来更少的额外操作。

  3. 分代回收
    3.1 理论:
    无论使用何种语言开发,无论开发的是何种类型,何种规模的程序,都存在这样一点相同之处。即:一定比例的内存块的生存周期都比较短,通常是几百万条机器指令的时间,而剩下的内存块,起生存周期比较长,甚至会从程序开始一直持续到程序结束。
    3.2 原理:
    将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。也就是符合马太福音,存活久的让它继续存活下去。

python01/#内存池机制" class="headerlink" title="内存池机制" style="margin:0px; padding:0px; border:0px; font-style:inherit; font-variant:inherit; font-weight:inherit; font-size:undefined; line-height:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,102,153)" rel="noopener noreferrer">内存池机制

Python的内存机制以金字塔层次:

  1. 内存分配层次:

      -1,-2层主要有操作系统进行操作,   第0层是C中的malloc,free等内存分配和释放函数进行操作;
      第1层和第2层是内存池,有Python的接口函数PyMem_Malloc函数实现,当对象小于256K时有该层直接分配内存;    第3层是最上层,也就是我们对Python对象的直接操作;

  2. 原因
    • 在 C 中如果频繁的调用 malloc 与 free 时,是会产生性能问题的.再加上频繁的分配与释放小块的内存会产生内存碎片。
  3. 具现化
    Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。
      Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的 malloc。另外Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。
      在Python中,许多时候申请的内存都是小块的内存,这些小块内存在申请后,很快又会被释放,由于这些内存的申请并不是为了创建对象,所以并没有对象一级的内存池机制。这就意味着Python在运行期间会大量地执行malloc和free的操作,频繁地在用户态和核心态之间进行切换,这将严重影响 Python的执行效率。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。这也就是之前提到的 Pymalloc机制。
      Pymalloc 关于释放内存方面,当一个对象的 引用计数变为0时,python就会调用它的析构函数。在析构时,也采用了内存池机制,从内存池来的内存会被归还到内存池中,以避免频繁地释放动作。
      Pymalloc分配一系列256KB的内存块,称之为arena。每个arena分割为4KB大小的内存池Pool,每个Pool再切分为固定大小的Block。在内存分配时,分配给进程的就是这些Blocks。
最后打一个广告,喜欢的这篇博文的欢迎大家转发,收藏,关注我~谢谢~

http://www.niftyadmin.cn/n/855595.html

相关文章

网络营销中可以说的秘密

当网站流量有80%以上来自搜索引擎时,当SEO成为一种必须时,你真的了解它吗? 近两年,一本名为《细节决定成败》的畅销书开始让人们意识到从行为的 细节中总结经验。其实,这个思路对于企业的经营而言同样适用。网络营销是企业经经营…

HTML中input文本框与label属性的关系

HTML中input文本框与label属性的关系前言一、label属性是什么?二、label属性的使用方法一方法二总结前言 这篇文章的目的是为了解释我在JS常用事件里面的问题:为什么input文本框需要label属性。也可以作为HTML页面设计的一个知识点作为学习和理解&#…

Js事件触发列表与解说

一般事件事件浏览器支持描述onClickHTML: 2|3|3.2|4 Browser: IE3|N2|O3鼠标点击事件,多用在某个对象控制的范围内的鼠标点击onDblClickHTML: 2|3|3.2|4 Browser:IE4|N4|O鼠标双击事件onMouseDownHTML: 2|3|3.2|4 Browser:IE4|N4|O鼠标上的按钮被按下了onMouseUpHTM…

如何利用Python判断一个字符串是合法ip

这是京东2018年校招面试,考的是正则表达式,在这里尝试解答一下 #!/usr/bin/pythonimport os,sys def check_ip(ipaddr):import sysaddripaddr.strip().split(.) #切割IP地址为一个列表#print addrif len(addr) ! 4: #切割后列表必须有4个参数print &qu…

JS定时器原理及案例

JS定时器原理及案例前言一、定时器二、定时器的使用1.定时器2.清除定时器三、案例1.倒计时2.使用和清除定时器3.发送短信总结前言 JS中定时器并不算难点,但是我还是拿出来讲,过于经典的案例往往是最为基础的知识点,仅供参考学习。 一、定时器…

JS全选反选及节点操作实现收件箱案例

JS全选反选及节点操作实现收件箱案例前言一、全选反选之商品选择二、节点操作之留言板(添加、删除)三、综合案例1.收件箱2.文本上下移动添加及删除功能总结前言 JavaScript中的节点操作是一处重要的知识点,在很多的地方都有它的身影&#xf…

利用Python实现腾讯校园招聘状态查询

# _*_ coding:utf-8 -*-# filename:utf-8 import requests import timedef query_state(counter0):post_data {type:query_result,idcard: ,phone: }post_data[idcard] 身份证后四位post_data[phone] 1手机号r requests.post(http://m.join.qq.com/query/result,datapost_d…

JS实现验证码倒计时验证案例

JS实现验证码倒计时验证案例前言验证案例1.代码2.代码功能解析1.验证码2.验证3.倒计时总结前言 这是一个将验证和倒计时综合起来的案例,案例也不难理解,在页面的设计中,需要这样的代码时可直接套用,仅供参考学习。 验证案例 1.代…