济宁500元网站建设企业app怎么做
web/
2025/9/30 20:14:29/
文章来源:
济宁500元网站建设,企业app怎么做,带后台的免费网站模板,免费商家入驻网店#x1f44f;作者简介#xff1a;大家好#xff0c;我是爱敲代码的小王#xff0c;CSDN博客博主,Python小白 #x1f4d5;系列专栏#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 #x1f4e7;如果文章知识点有错误… 作者简介大家好我是爱敲代码的小王CSDN博客博主,Python小白 系列专栏python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 如果文章知识点有错误的地方请指正和大家一起学习一起进步 如果感觉博主的文章还不错的话请三连支持一下博主哦 博主正在努力完成2023计划中以梦为马扬帆起航2023追梦人 python入门到实战专栏从入门到实战 Python爬虫开发专栏从入门到实战 Python办公自动化专栏从入门到实战 Python数据分析专栏从入门到实战 Python前后端开发专栏从入门到实战 目录
内存管理机制
Python缓存机制
垃圾回收机制
分代回收机制 内存管理机制 Python是由C语言开发的底层操作都是基于C语言实现Python中创建每个对象内部都会与C语言结构体维护一些值。 源码下载https://www.python.org/ 将压缩文件减压可以看到有很多文件主要关心两个Include、 Objects 在Include目录下object.h中可以查看创建对象的结构体。 #define _PyObject_HEAD_EXTRA \struct _object *_ob_next; \struct _object *_ob_prev;
typedef struct _object {_PyObject_HEAD_EXTRAPy_ssize_t ob_refcnt;PyTypeObject *ob_type;
} PyObject;
typedef struct {PyObject ob_base;Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;
在创建对象时每个对象至少内部4个值PyObject结构体(上一个 对象、下一个对象、类型、引用个数)。
有多个元素组成的对象使用PyVarObject里面由PyObject结构体(上一个对象、下一个对象、类型、引用个数)Ob_size(items元素元素个数)。
环状双向链表refchain
在python程序中创建的任何对象都会被放在refchain链表中。 类型封装结构体
f 3.14内部会创建
1.开辟内存
2.初始化ob_fval 3.14 值ob_type float 类型ob_refcnt 1 引用数量
3.将对象加入到双向链表refchain中_ob_next refchain中的上一个对象_ob_prev refchain中的下一个对象Python缓存机制 小整数对象池
一些整数在程序中的使用非常广泛Python为了优化速度使用了小整数对象池 避免为整数频繁申请和销毁内存空间。
Python 对小整数的定义是 [-5, 257) 这些整数对象是提前建立好的不会被垃圾回收。在一个 Python 的程序中所有位于这个范围内的整数使用的都是同一个对象。在一个 Python 的程序中无论这个整数处于LEGB(局部变量闭包全局内建模块)中的哪个位置 所有位于这个范围内的整数使用的都是同一个对象。
【示例】验证小整数池
In [8]: a 100In [9]: id(a)
Out[9]: 140705185112832#删除变量a
In [10]: del aIn [11]: b 100In [12]: id(b)
Out[12]: 140705185112832
大整数对象池
每一个大整数均创建一个新的对象。
【示例】验证大整数池
In [13]: a 257In [14]: b 257In [15]: id(a)
Out[15]: 1747036560496In [16]: id(b)
Out[16]: 1747036558352In [17]: a is b
Out[17]: Falseintern机制
每个单词(字符串)不夹杂空格或者其他符号默认开启intern机制共享内存靠引用计数决定是否销毁。
【示例】intern机制 a helloworldb helloworlda is ba hello worldb hello worlda is b
free_list机制
当一个对象的引用计数器为0时按理说应该回收但内存不会直接回收而是将对象添加到free_list链表中缓存。以后再去创建对象 时不再重新开辟内存而是直接使用free_list。
以上的free_list的代表floatlisttupledict。 f1 3.14id(f1)
2078136427568del f1f2 9.998id(f2)
2078136427568
f1 3.14会创建float类型并且加入refchain中。del f1 则减1 refchain移除按理会进行销毁但是实际中不会真正销毁而是 会添加到free_list。以后创建f29.998等只要是float类型则不会重新开辟内存会去free_list获取对象对象内部进行初始化 替换值3.14换成9.99再放到refchain中。不是所有的都放入 free_list。例如存储100个缓存则前面新创建的80个会放入 free_list。如果满了81之后则会销毁。
1、 float类型维护的free_list链表最多可缓存100个float对象。
#ifndef PyFloat_MAXFREELIST//定义free_list的最大长度
#define PyFloat_MAXFREELIST 100
#endif
2、 list类型维护的free_list数组最多可缓存80个list对象。
#ifndef PyList_MAXFREELIST#define
PyList_MAXFREELIST 80
#endif【示例】 v1 [11,22,33]id(v1)
2078137539904del v1v2 [a,b]id(v2)
20781375399043、 tuple类型维护一个free_list数组且数组含量20数组中元素 可以是链表且每个链表最多可以含钠2000个元组对象。元组的 free_list数据在存储数据时是根据元组可容纳的个数为索引找 到free_list数组中对应的链表并添加到链表中。 t1 (1,2,3)id(t1)
2078137564992del t1 #元组的数量是3,所以把这个对象缓存到
free_list[3]的链表中t2 (a,b,c)
#元组的数量也是3不会重新开辟内存而是去
free_list[3]对应的链表中拿到一个对象来使用id(t2)
2078137564992del t2t3 (11,22,33,44)id(t3)
2078138219472 4、 dict类型维护的free_list数组最多可缓存80个dict对象。
#ifndef PyDict_MAXFREELIST#define
PyDict_MAXFREELIST 80
#endif
【示例】 d1 {name:zs}id(d1)
2078137472640del d1d2 {age:30}id(d2)
2078137472640
垃圾回收机制 Python 内部采用 引用计数法 为每个对象维护引用次数并据此回收不再需要的垃圾对象。由于引用计数法存在重大缺陷循环引 用时有内存泄露风险因此 Python 还采用 标记清除法 来回收存在循环引用的垃圾对象。此外为了提高垃圾回收( GC )效率Python 还引入了 分代回收机制 。 引用计数法
Python采用了类似Windows内核对象一样的方式来对内存进行管理。每一个对象都维护这一个对指向该对对象的引用的计数。
引用计数 是计算机编程语言中的一种 内存管理技术 它将资源被引用的次数保存起来当引用次数变为 0 时就将资源释放。它管理 的资源并不局限于内存还可以是对象、磁盘空间等等。
Python 也使用引用计数这种方式来管理内存每个 Python 对象都包含一个公共头部头部中的 ob_refcnt 字段便用于维护对象被引用 次数。回忆对象模型部分内容我们知道一个典型的 Python 对象结构如下 当创建一个对象实例时先在堆上为对象申请内存对象的引用计数被初始化为 1 。以 Python 为例我们创建一个 float 对象保存 6.66并把它赋值到变量 f f 6.66f
6.66由于此时只有变量 f 引用 float 对象因此它的引用计数为 1 当我们把 pi 赋值给 f 后float 对象的引用计数就变成了 2 因为现在有两个变量引用它 ff fff
6.66 我们新建一个 list 对象并把 float 对象保存在里面。这样一来 float 对象有多了一个来自 list 对象的引用因此它的引用计数又加 一变成 3 了 l [f]l
[6.66] 标准库 sys 模块中有一个函数 getrefcount 可以获取对象引用计数 import syssys.getrefcount(pi)
4 咦引用计数不应该是 3 吗为什么会是 4 呢由于 float 对象被作为参数传给 getrefcount 函数它在函数执行过程中作为函数的局部变量存在创建了一个临时的引用因此又多了一个引用 随着 getrefcount 函数执行完毕并返回它的栈帧对象将从调用链中解开并销毁这时 float 对象的引用计数也跟着下降。因此当一个 对象作为参数传个函数后它的引用计数将加一当函数返回局部名字空间销毁后对象引用计数又减一。
引用计数就这样随着引用关系的变动不断变化着。当所有引用都消除后引用计数就降为零这时 Python 就可以安全地销毁对象 回收内存了 del ldel ffdel f 引用计数增加 1、 对象被创建 2 、如果有新的对象使用该对象 3 、作为容器对象的一个元素 4、 被作为参数传递给函数 引用计数减少 1、 对象的引用被显示的销毁 2、 新对象不再使用该对象 3 、对象从列表中被移除或者列表对象本身被销毁 4 、函数调用结束 引用计数机制的优点 1、简单 2、实时性一旦没有引用内存就直接释放了。 引用计数机制的缺点 1、维护引用计数消耗资源 2、循环引用的问题无法解决 a [1,2]
b [3,4]
a.append(b) #b的计数器2
b.append(a) #a的计数器2
del a
del b 标记-清除
引用计数法能够解决大多数垃圾回收的问题但是遇到两个对象相互引用的情况del语句可以减少引用次数但是引用计数不会归 0对象也就不会被销毁从而造成了内存泄漏问题。针对该情况 Python引入了标记-清除机制。
循环引用 引用计数这种管理内存的方式虽然很简单但是有一个比较大的瑕疵即它不能很好的解决循环引用问题。如上图所示对象 A 和对 象 B相互引用了对方作为自己的成员变量只有当自己销毁时 才会将成员变量的引用计数减 1。因为对象 A 的销毁依赖于对象 B 销毁而对象 B 的销毁与依赖于对象 A 的销毁这样就造成了我们 称之为循环引用Reference Cycle的问题这两个对象即使在外界已经没有任何指针能够访问到它们了它们也无法被释放。
class People:pass
class Cat:pass
#创建People的实例对象
p People()
#创建Dog的实例对象
c Cat()
#People的宠物属性指向Cat
p.pet c
#Cat的主人属性指向People
c.master p
#删除p和c对象
del p
del c
上述实例中对象p中的属性引用c而对象c中属性同时来引用p 从而造成仅仅删除p和c对象也无法释放其内存空间因为他们依然在被引用。深入解释就是循环引用后p和c被引用个数为2 删除p和c对象后两者被引用个数变为1并不是0而python只有 在检查到一个对象的被引用个数为0时才会自动释放其内存所以 这里无法释放p和c的内存空间。
主动思路一般分为两步垃圾识别 和 垃圾回收 。垃圾对象被识别出来后回收就只是自然而然的工作了因此垃圾识别是解决问题的关键。那么有什么办法可以将垃圾对象识别出来呢我们来考察一个一般化例子 这是一个对象引用关系图其中灰色部分是需要回收但由于循环引用而无法回收的垃圾对象绿色部分是被程序引用而不能回收的活跃对象。如果我们能够将活跃对象逐个遍历并标记那么最后没有被标记的对象就是垃圾对象。
遍历活跃对象第一步需要找出 根对象 ( root object )集合。所谓根对象就是指被全局引用或者在栈中引用的对象这部对象是不能被删除的。因此我们将这部分对象标记为绿色作为活跃对象遍历的起点。 根对象本身是 可达的 ( reachable )不能删除被根对象引用的对象也是可达的同样不能删除以此类推。我们从一个根对象出发沿着引用关系遍历遍历到的所有对象都是可达的不能删除。 这样一来当我们遍历完所有根对象活跃对象也就全部找出来了 而没有被标色的对象就是 不可达 ( unreachable )的垃圾对象可以被安全回收。循环引用的致命缺陷完美解决了 这就是垃圾回收中常用的 标记清除法 。
【示例】标记清除法 上图中小黑点变量表示根节点从根节点出发每个对象都有引用和被引用的情况如果该对象找不到根节点那么就会被清除如图1,2,3都有被小黑点变量引用4,5没有变量引用所以 4,5就会被清除。
分代回收机制
Python 程序启动后内部可能会创建大量对象。如果每次执行标记清除法时都需要遍历所有对象多半会影响程序性能。为此 Python引入分代回收机制——将对象分为若干“代”( generation ) 每次只处理某个代中的对象因此 GC 卡顿时间更短。
考察对象的生命周期可以发现一个显著特征一个对象存活的时间越长它下一刻被释放的概率就越低。我们应该也有这样的亲身体会经常在程序中创建一些临时对象用完即刻释放而定义为 全局变量的对象则极少释放。
因此根据对象存活时间对它们进行划分就是一个不错的选择。 对象存活时间越长它们被释放的概率越低可以适当降低回收频率相反对象存活时间越短它们被释放的概率越高可以适当提高回收频率。
对象存活时间释放概率回收频率长低低短高高
Python 内部根据对象存活时间将对象分为 3 代(见 Include/internal/mem.h )
#define NUM_GENERATIONS 3 随着时间的推进程序冗余对象逐渐增多达到一定阈值系统进行回收。
这 3 个代分别称为初生代、中生代 以及 老生代。当这 3 个代初始化完毕后对应的 gc_generation 数组大概是这样的 import gc
#python 中内置模块gc触发
print(gc.get_threshold()) #查看gc默认值
#输出(700, 10, 10)第一代链表
当第一代达到700就开始检测哪些对象引用计数变成0了把不是0的放到第二代链表里此时第一代链表就是空了当再次达到700 时就再检测一遍。
第二代链表
当第二代链表达到10就检测一次。
第三代链表
第三代链表检测10之后第三代链表检测一次。
import gc#返回一个元组分别获取这三代当前计数
gc.get_count() #返回一个元组分别获取这三代当前的收集阈值
gc.get_threshold()#设置阈值
gc.set_threshold()#关闭gc垃圾回收机制
gc.disable()
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/84638.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!