前言:
关于python的内存管理机制,我们可以从以下三个方面来解析:
引用计数
垃圾回收(标记清除、分代回收)
小整数对象池
1,引用计数:
1.1引用计数增加的情况:
- 创建一个对象,例例如:a = ‘carry’,引⽤用计数 1。
- 赋值 b=a, 引⽤用计数加1
- 浅拷⻉里的引用,函数的传参foo(x)等
- 作为容器对象的一个元素:a=[1,x,’22’]
1.2引用计数减少的情况:
- 一个本地引⽤离开了它的作⽤域。⽐如上面的foo(x)函数结束时,x指向的对象引用减 1。
- 对象的别名被显式的销毁:del x ;或者del y
- 对象的⼀个别名被赋值给其他对象:x=123
- 对象从⼀个窗⼝对象中移除:carry.remove(x)
- 窗⼝对象本身被销毁:del carry,或者窗⼝对象本身离开了作用域。
1.3引用计数优缺点:
优点:
- 简单、实时性:只用引用计数为0,内存就直接释放了。不受时间限制。
缺点:
- 维护引⽤用计数消耗资源
- 出现循环引⽤(标记清除、分代回收)
2,垃圾回收:
1,引⽤用计数为零时,释放其占⽤用的内存空间
2,出现了了循环引⽤用怎么办—标记清除、分代回收
2.1标记清除
标记清除Mark-Sweep是针对循环引⽤问题的回收机制,作⽤的对象是容器类型的对象(⽐ 如:list、set、dict等)。
原理是:通过根节点对象(不会被删除的对象)把所有活动对象打上标记,然后回收没有被标记的非活动对象。
2.2分代回收
分代回收是建立在标记清除基础上的一种辅助回收容器器对象的GC机制。
无论开发的程序类型如何,规模如何,都有这样的相同之处:一些比例的内存生存周期都很短,而另一些内存的⽣存周期比较长,可能会伴随着整个程序的开始和结束。
所以分代回收就根据系统中内存存活时间把它们划分成不同的集合:一共分成三个集合,每个集合称为一个代。
它们的垃圾收集频率随对象存活时间的增大而减小。
也就是说:对于存活时间越长的对象,就越不可能是垃圾,减少对其的收集频率。
而新创建的对象都在第一代,第一代集合总数达到上限后,会触发GC机制:可以回收的对象 所占的内存被释放,不能被回收的移到下一代。
3,小整数对象池
整数在Python中的使用广泛,为了优化速度,使用小整数对象池来优化,小整数对象池中的数据不会被垃圾回收。
范围 [-5,256] 单个字母也是同样原理
注意点:
- 当重复创建若干变量,变量内容相同时,Python不会为相同的内容创建存储空间,而是使用引用计数的方式处理
- 字符串之间如果存在空格,则不共用数据
- 修改不可变类型,实际是创建了新的对象,指向了另一个内存空间。
- 字符串之间如果存在空格的话,则数据不共用。例
a = ‘hello python’
b = ‘hello python’
#id(a) 不等于 id(b)
如果是:
a = ‘hello_python'
b = ‘hello_python'
则:是指向同一块内存空间