注 1:变量名没有类型,类型属于对象(因为变量引用对象,所以类型随对象),变量引用什么类型的对象,变量就是什么类型
var1 = "hello world"
var2 = var1
id(var1)
id(var2)
注 2:id() 是 python 的内置函数,用于返回对象的身份,即对象的内存地址
# 短字符串
a = "hello"
b = "hello"
print(a is b) # True
# 整数
a = 12
b = 12
print(a is b) # True
# 长字符串
a = "id()是python的内置函数,用于返回对象的身份,即对象的内存地址"
b = "id()是python的内置函数,用于返回对象的身份,即对象的内存地址"
print(a is b) # False
注 3:is 是用来判断两个引用所指的对象是否相同,==是用来判断两个变量对应的值是否相同
由运行结果可知:
1、python 缓存了整数和短字符串,因此每个对象在内存中只存在一份,引用所指对象就是相同的,即使使用赋值语句,也只是创造新的引用,而不是对象本身
2、python 没有缓存长字符串、列表以及其他对象,可以由多个相同的对象,使用赋值语句创建新的对象
3、python 中对大于 256 的整数,会重新分配对象空间地址保存对象;对于字符串来说,如果不包含空格的字符串,则不会重新分配对象空间,对于包含空格的字符串则会重新分配
python 中 每个对象都有指向该对象的引用总数 -- 引用计数
查看对象的引用次数:sys.getrefcount()
import sys
a = [1, 2, 3]
sys.getrefcount(a) # 2
b = a
sys.getrefcount(a) # 3
sys.getrefcount(b) # 3
注 4:当使用某个引用作为参数,传递给 getrefcount() 时,参数实际上创建了一个临时的引用,因此,getrefcount() 所得到的结果,会比期望的多 1
python 的一个容器对象(列表、字典、集合等),可以保护多个对象,实际上,容器对象中包含的并不是元素对象本身,而是指向各个元素对象的引用
引用计数增加
引用计数减少
当 python 中的对象越来越多,占据越来越多的内存,启动垃圾回收(garbage collection),将没用的对象清除
a = [123]
del a
注 5:
1、垃圾回收时,python 不能进行其他的任务,频繁的垃圾回收将大大降低 python 的工作效率
2、python 只会在特定条件下,自动启动垃圾回收(垃圾对象少就没必要回收)
3、当 python 运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallccation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动
import gc
gc.get_threshold() # (700, 10, 10)
阈值分析:
每 10 次 0 代垃圾回收,会配合 1 次 1 代的垃圾回收;而每 10 次的 1 代垃圾回收,才会有 1 次的 2 代垃圾回收
通过 gc.collect() 可手动启动垃圾回收
分代回收
python 将所有的对象分为 0,1,2 三代。所有创建的对象都是 0 代对象。当某一代对象经历过垃圾回收,仍然存活,那么它就会被归入下一代对象。垃圾回收启动时,一定会扫描所有的 0 代对象。如果 0 代经过一定次数垃圾回收,那么久启动对 0 代和 1 代的扫描清理。当 1 代也经历了一定次数的垃圾回收后,那么会启动对 0,1,2,即对所有对象进行扫描
如果两个对象的引用计数都为 1,但是仅仅存在他们之间的循环引用,那么这两个对象都是需要被回收的。也就是说,它们的引用计数虽然表现为非 0,但实际上有效的引用计数为 0,所有先将循环引用摘掉,就会得出这两个对象的有效计数
1、python 使用引用计数和垃圾回收来释放 python 对象的内存
2、引用计数的优点是原理简单,但是会增加 python 运行时间;缺点是无法处理循环引用
3、python 标记清除用于处理循环引用
4、gc module 是 python 垃圾回收机制的接口模块,可以通过该模块启停垃圾回收、调整回收触发的阈值
以上内容摘自:https://blog.csdn.net/weixin_39653717/article/details/110371108