前言
Python的内存管理机制就是引用计数器机制和垃圾回收机制和内存池机制的混合机制
PS:Python中的变量名没有类型,类型属于对象(因为变量引用对象,所以类型随对象),变量引用什么类型的对象,变量就是什么类型的。
PS:Python的内存管理机制:引入计数、垃圾回收、内存池机制。Python内部使用引用计数,来保持追踪内存中的对象一、引入计数机制
Python内部记录了对象有多少个引用,即引用计数,当对象被创建时就创建了一个引用计数,当对象不再需要时,这个对象的引用计数为0时,它被垃圾回收。
-
引用计数增加的情况
- 创建一个对象,例例如:a = ‘carry’,引⽤用计数 1。
- 赋值 b=a, 引⽤用计数加1
- 浅拷⻉里的引用,函数的传参foo(x)等
- 作为容器对象的一个元素:a=[1,x,’22’]
-
引用计数减少的情况
- 一个本地引⽤离开了它的作⽤域。⽐如上面的foo(x)函数结束时,x指向的对象引用减 1。
- 对象的别名被显式的销毁:del x ;或者del y
- 对象的⼀个别名被赋值给其他对象:x=123
- 对象从⼀个窗⼝对象中移除:carry.remove(x)
- 窗⼝对象本身被销毁:del carry,或者窗⼝对象本身离开了作用域。
-
引用计数优缺点
-
优点
简单、实时性:只用引用计数为0,内存就直接释放了。不受时间限制。 -
缺点
- 维护引⽤用计数消耗资源
- 出现循环引⽤(标记清除、分代回收)
-
二、垃圾回收机制
垃圾回收机制: ① 引用计数 , ②标记清除 , ③分带回收
垃圾回收的作用:从经过引用计数器机制后还没有被释放掉内存的对象中,找到循环引用对象,并释放掉其内存
一句话概括:引用计数为主,清除标记,分代回收为辅
引用计数也是一种垃圾收集机制, 而且也是一种最直观, 最简单的垃圾收集技术.当python某个对象的引用计数降为 0 时, 说明没有任何引用指向该对象, 该对象就成为要被回收的垃圾了.(如果出现循环引用的话, 引用计数机制就不再起作用了)1.标记清除
标记清除( Mark-Sweep )是针对循环引⽤问题的回收机制,作⽤的对象是容器类型的对象(⽐ 如:list、set、dict等)。
原理是:通过根节点对象(不会被删除的对象)把所有活动对象打上标记,然后回收没有被标记的非活动对象。
如果两个对象的引用计数都为 1 , 但是仅仅存在他们之间的循环引用,那么这两个对象都是需要被回收的, 也就是说 它们的引用计数虽然表现为非 0 , 但实际上有效的引用计数为 0 ,.所以先将循环引用摘掉, 就会得出这两个对象的有效计数.
2.分代回收
分代回收是建立在标记清除基础上的一种辅助回收容器器对象的GC机制。
无论开发的程序类型如何,规模如何,都有这样的相同之处:一些比例的内存生存周期都很短,而另一些内存的⽣存周期比较长,可能会伴随着整个程序的开始和结束。
所以分代回收就根据系统中内存存活时间把它们划分成不同的集合:一共分成三个集合,每个集合称为一个代。
它们的垃圾收集频率随对象存活时间的增大而减小。
也就是说:对于存活时间越长的对象,就越不可能是垃圾,减少对其的收集频率。
而新创建的对象都在第一代,第一代集合总数达到上限后,会触发GC机制:可以回收的对象 所占的内存被释放,不能被回收的移到下一代。
三、内存池机制
内存池机制: python 中分为大内存和小内存: 256k为界限
大内存使用malloc 进行分配
小内存使用内存池是进行分配
python的内存池金字塔:
第3层: 最上层, 用户对python对象的直接操作
第1层和第2层: 内存池, 有python 的 接口函数 PyMen_Malloc 实现, 若请求分配的内存在1 - 256字节之间就使用内存池进行分配, 调用malloc 函数分配内存, 但是每次只会分配 256 k 的内存. 不会调用free 函数释放内层. 将该内存块留在内存池中便下次使用
第 0 层: 大内存 . 若请求分配的内存大于 256 k , malloc函数分配, free函数释放内存
第 - 1 -2 层: 操作系统进行操作
Python内存池金字塔:
注意点
- 当重复创建若干变量,变量内容相同时,Python不会为相同的内容创建存储空间,而是使用引用计数的方式处理。
- 字符串之间如果存在空格,则不共用数据。
- 修改不可变类型,实际是创建了新的对象,指向了另一个内存空间。
- 字符串之间如果存在空格的话,则数据不共用。
案例:
a = ‘hello python’
b = ‘hello python’
#id(a) 不等于 id(b)
如果是:
a = ‘hello_python'
b = ‘hello_python'
则:是指向同一块内存空间