当咱们在定义变量的时候,变量会在内存中申请空间用来存储内存的值,当变量不在被引用的时候 内存地址须要释放,内存的释放机制,能够理解为垃圾回收机制。python
若是内存的值一直不被释放,会存在内存泄漏的风险算法
垃圾回收机制(简称GC)是Python解释器自带一种机制,python的Cpython解释器会帮咱们实现垃圾回收,app
在定义变量的时候,变量名 于变量的值是分开存储的,分别对应内存中的两块区域:堆区与栈区code
栈区:存放的是变量名与值内存地址的关联关系 存的是对应关系对象
堆区:变量值存放于堆区,内存管理回收的则是堆区的内容blog
x=10 x与10的对应关系存在栈区1-1 10这个值 存在堆区1内存
y=20 y 与 20的对应关系存在与站区 2-2 20这个值存在与堆区2资源
当咱们执行 x=y的时候内存管理
x栈区的对应关系变成了 2-2 其它的地方不变,io
直接引用:指的是从栈区出发直接引用到的内存地址。 也就是经过栈区的变量名能够直接找到堆区值的引用
间接引用: 指的是从栈区出发引用到堆区后,再经过进一步引用才能到达的内存地址。 没法经过栈区的名字一步实现找到堆区的值
直接引用举例
x=10
y=x
y就是直接引用的x的值
间接引用举例
list1 = ['24', 'b', 'luck']
list2=['ab','ac',list1]
列表list2对list1是直接引用 但list2对list2中的值是间接引用
list2要取出list1 中24这个值 须要list2的栈区须要先找到对应的list1的站区,而后在经过list1的栈区找到对应的list[0]的值
list1 = ['24', 'b', 'luck'] print(id(list1)) print(id(list1[0])) list2 = ['ab', 'ac', list1] print(id(list2[2])) print(list2[2][0]) print(id(list2[2][0]))
2365435328832 list1的id
2365405395696 24值的id
2365435328832 list 2中list1的id
24
2365405395696 list2中list1中24的id
课件list2中存的list1 是存了list1的栈区的内存地址
Python的GC模块 内存回收机制主要运用下面三个计数
引用计数(reference counting)来跟踪和回收垃圾。
在引用计数的基础上,经过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用的问题,
分代回收(generation collection)以空间换取时间的方式来进一步提升垃圾回收的效率
下面分别讲一下三种计数的实现
引用计数其实就是指的 变量的值被关联的次数,也就是有多少个直接或者间接指向改值的内存地址,计算个数 若是这个数为0 则回收改值的内存空间
举例
a=10 10 这个值的内存空间 有一个计数了
x=a 在内存中x这个栈区指向的堆区也是 10的内存空间 计数+1 变成2 了
del a
del x 当两个变量名都删除后 x a 的栈区没有了 10的内存空间的引用计数变为0 10的内存空间被回收。
标记清除是为了解决引用计数的漏洞
下面例子
x= ['xxx']
y=['yyy']
x.append(y) 列表y的引用计数变为2
y.append(x) 列表x的引用计数变为2
# x与y之间有相互引用 # x = ['xxx'的内存地址,列表2的内存地址] # y = ['yyy'的内存地址,列表1的内存地址]
del x 列表x的引用计数变为1 来自y的引用
del y 列表的y引用计数变为1 列表x的引用
如今没有变量名指向这两个内存地址的值 可是这两个内存地址由于彼此的相互引用,引用计数不为0 经过引用计数的方式是没法被清除的.
循环引用会致使 内存变量没法被访问到,同时经过标记清除的方式没法清除,形成大量无用的变量占用内存。
标记清除实现的算法:当应用程序内存耗尽的时候,中止整个程序,进行两项操做,一个是标记,一个操做是清除。
标记: 咱们知道当一个变量存在内存中的时候,会产生栈区和堆区 栈区存的是内存地址和内存值的对应关系
而堆区存的是真正的变量的值,当我访问堆区内容的时候,只能经过栈区直接或者间接的访问到堆区的内容
标记的作法是 凡事全部从栈区出发,能够访问到的堆区内容的值标记 不管是栈区-->堆区 仍是 栈区---->**---->堆区,只要是栈区出发,最终能够访问到的堆区的值都标记,栈区出发 没法访问到的值则标记为须要清除的值,
由于咱们没法绕过栈区 直接访问到堆区的值。
清除:遍历堆区中的全部对象值,将没有标记的对象所有清除
这样就解决了变量之间循环引用形成的内存泄漏问题
问题: 咱们知道标记-清除是要遍历整个程序内存中全部变量的值,整个遍历的过程会很是消耗资源的,同时全部的值都变量一次,效率上也不高,因此下面引用了分代回收机制 “ 空间换时间” 牺牲一部份内存空间换来更高的执行效率、分代回收机制
存活时间:遍历一边内存的值 存活没有被清除的记为存活时间,屡次扫描 仍然存活的 咱们称为老年代(变量建立的时间早,可是屡次扫描仍然在使用)
那有老年代 就是 青年代 青春代 新生代 以此建立时间愈来愈短。不一样的”代“ 扫描的权重值不一样,年轻的权重值高
把不一样的变量分代之后,年轻的变量扫描次数多 年老的扫描次数少,例如 新生代 半小时一次,青春带 2小时一次
到了 老年代 能够一天扫描一次。
经过分代回收机制,减小 遍历的次数和个数,提升 标记-清除的效率。