以引用计数为主,分代回收、标记清除为辅的垃圾回收方式
以及对小整型进行缓存和简单字符驻留的内存池机制
ptython 中的每个对象都维护一个引用计数 ob_refz字段
当有新的引用指向改对象的时候,引用计数+1
当无效的引用发生的时候,引用计数-1
最后引用技术为0,销毁对象
>>> from sys import getrefcount
>>> a = 1000
>>> getrefcount(a) #实际是引用了1次
2
>>> b = a
>>> getrefcount(a)
3
>>> c = []
>>> c.append(b)
>>> getrefcount(a)
4
>>> getrefcount(a)
3
>>> d = 2000
>>> e = 2000
>>> getrefcount(d)
2
>>> getrefcount(e)
2
>>> d = e = 600
>>> getrefcount(d)
3
>>> getrefcount(e)
3
根据引用计数的规律,出现循环引用的情况,内存是取法通过引用计数来释放
这种情况就会造成内存泄漏
内存泄漏: 有一部分内存被占用无法释放,进程无法访问
后果:造成内存溢出(oom—out of memory),内存不够,程序需要的内存大于系统的空闲内存
>>> x = [1]
>>> y = [2]
>>> x.append(y)
>>> y.append(x)
>>> getrefcount(x)
3
>>> getrefcount(y)
3
>>> del x
>>> del y
•当Python的某个对象的引用计数降为0时,可以被垃圾回收
• 调用gc.collect()
• GC达到阀值时
• 程序退出时
• GC作为现代编程语言的自动内存管理机制,专注于两件事
• 找到内存中无用的垃圾资源
• 清除这些垃圾并把内存让出来给其他对象使用。
GC彻底把程序员从资源管理的重担中解放出来,让他们有更多的时间放在业务逻辑上。但这并不意味 着码农就可以不去了解GC,毕竟多了解GC知识还是有利于我们写出更健壮的代码
>>> import gc
>>> print (gc.get_threshold())
(700, 10, 10) #
>>> gc.collect() #调用gc.collect()回收
2
>>> gc.collect() #再次调用就没有要回收的了
0
启动垃圾回收的时候确定扫描哪些对象
这一策略的基本假设是:存活时间越久的对象,越不可能在后面的程序中变成垃圾。
• Python将所有的对象分为0,1,2三代。
• 所有的新建对象都是0代对象。
• 当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象。
• 垃圾回收启动时,一定会扫描所有的0代对象。
• 如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。
• 当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描。
主要解决循环引用
标记-清除机制,顾名思义,首先标记对象(垃圾检测),然后清除垃圾(垃圾回收)。 主要用于解决循环引用。
对于[-5,256] 这样的小整数,系统已经初始化好,可以直接拿来用。而对于其他的大整数,系统则提 前申请了一块内存空间,等需要的时候在这上面创建大整数对象。
为了检验两个引用指向同一个对象,我们可以用is关键字。is用于判断两个引用所指的对象是否相同。 当触发缓存机制时,只是创造了新的引用,而不是对象本身。
>>> str1 = "abcxyz" 不包含特殊字符的字符串,会放到驻留区
>>> getrefcount(str1)
2
>>> str2 = "abcxyz"
>>> getrefcount(str1)
3
>>> id(str1)
139788884979192
>>> id(str2)
139788884979192
>>> str3 = "#" 单个特殊字符也是会放在驻留区的
>>> str4 = "#"
>>> id(str3)
139788884976056
>>> id(str4)
139788884976056
>>> str5 = "abc 123" 包含了特殊字符(空格),不放在驻留区
>>> str6 = "abc 123"
>>> id(str5)
139788853780696
>>> id(str6)
139788853780808
不属于内存管理
只会发生在容器类型里面包含其他可变容器类型的情况
浅拷贝可能会造成修改拷贝之后的值
浅拷贝只会拷贝第一层的地址,深拷贝则不会
>>> a = {"name":"sc","score":[80,90,100]}
>>> b = a.copy() 浅拷贝,拷贝a里面每个元素(列表算一个元素)的引用地址
>>> b {'name': 'sc', 'score': [80, 90, 100]}
>>> b["score"].append(110)
>>> b {'name': 'sc', 'score': [80, 90, 100, 110]}
>>> a {'name': 'sc', 'score': [80, 90, 100, 110]}
>>>> id(a)
139788853767048
>>> id(b)
139788853766328
>>> id(a["score"])
139788884386760
>>> id(b["score"])
139788884386760
容器里面包含一个可变的容器,才会有深拷贝
b = copy.deepcopy(a) 只有这种是深拷贝,其他情况都是浅拷贝
>>> import copy
>>> b = copy.deepcopy(a) 只有这种是深拷贝,其他情况都是浅拷贝
>>> a
{'name': 'sc', 'score': [80, 90, 100, 110]}
>>> b
{'name': 'sc', 'score': [80, 90, 100, 110]}
>>> id(a)
139788853767048
>>> id(b)
139788853766760
>>> id(a["score"])
139788884386760
>>> id(b["score"])
139788846326728
>>> b["score"].append(120)
>>> a
{'name': 'sc', 'score': [80, 90, 100, 110]}
>>> b
{'name': 'sc', 'score': [80, 90, 100, 110, 120]}
>>> lst = [[]]*3 拷贝了同一个地址
>>> lst
[[], [], []]
>>> lst[0].append(1)
>>> lst
[[1], [1], [1]]
>>> print(id(lst[0]),id(lst[1]),id(lst[2]))
139788846326792 139788846326792 139788846326792