1. 对象池:
- 为了减小创建相同对象的开销,python将以下的数据类型的instance放在对象池中(常驻内存)。如果需要的对象已经创建过,就直接去池子中拿:(无须每次都创建和销毁)
- 小整数:[-5,257) #好像有变化了
- 单个英文字符
- 单个单词(以空格为分界线,有空格就是一个句子)
c1 = 'a'
c2 = 'a'
print(id(c1), id(c2),'
')
d1 = -6
d2 = -6
print(id(d1), id(d2),'
')
d3 = 5
d4 = 5
print(id(d3), id(d4),'
')
d5 = 258
d6 = 258
print(id(d5), id(d5), '
')
s1 = "hello"
s2 = "hello"
s3 = "hello "#有个空格
print(id(s1), id(s1), id(s3),'
')
senten1 = "hello world"
senten2 = "hello world"
print(id(senten1), id(senten2), '
')
2. 内存管理机制:
Garbage collection(垃圾回收)
- 为新生成的对象分配内存
- 识别那些垃圾对象
- 从垃圾对象那回收内存
- python采用的是引用计数机制为主, 标记-清除和分代收集两种机制为辅的策略
- 引用计数机制的原理(C代码)
python的每一个东西都是对象, 它们的核心就是一个结构体: PyObject
typedef struct_object {
int ob_refcnt; //referance count
struct_typeobject *ob_type;
} PyObject;
#define Py_INCREF(op) ((op)->ob_refcnt++) //增加计数
#define Py_DECREF(op) //减少计数
if (--(op)->ob_refcnt != 0)
;
else
__Py_Dealloc((PyObject *)(op))
- 引用计数的优缺点:
(1)优点:
- 简单
- 实时性: 一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。
(2)缺点:
- 维护引用计数消耗资源
- 循环引用
- 引用计数的计数值发生变化:
(1)导致引用计数+1的情况
- 对象被创建,例如a=23
- 对象被引用 例如b=a
- 对象被作为参数,传入到一个函数中,例如func(a)
- 对象作为一个元素,存储在容器中 例如list1=[a,a]
(2)导致引用计数-1的情况
- 对象的别名被显式销毁,例如del a
- 对象的别名被赋予新的对象,例如a=24
- 一个对象离开它的作用域 例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
对象所在的容器被销毁,或从容器中删除对象
3. 引用计数分析:
-
查看某对象的引用计数:
sys.getrefcount()
def p(str=''):
print(str,' ')import sys
a = 100 #a是小整数,放在对象池中(被各种对象引用),因而引用计数值应该很大
p(sys.getrefcount(a))b = 1000 #刚刚new了一个b指向1000所在的内存, 引用数加一,设为b0
p(sys.getrefcount(b)) #但是刚刚把b传入getrefcount(),所以此时引用值为b0+1a = b #再次引用了b,引用加1,即:
p(sys.getrefcount(b)) # b0+2c = [b] #再次引用了b,引用加1,即:
p(sys.getrefcount(b)) # b0+3del c
p(sys.getrefcount(b)) #b的引用应该减一
del c
p(sys.getrefcount(b)) #b的引用应该减一 -
测试循环引用的影响:
import gc
class ClassA():
def init(self):
print('object born,id:%s'%str(hex(id(self))))
def f2():
while True:
c1 = ClassA()
c2 = ClassA()
c1.t = c2 #发生了c1和c2的循环引用
c2.t = c1
del c1 #当需要删除互为循环引用的对象时,引用计数机制会出问题
del c2gc.disable() #把python的gc关闭
f2() #内存使用率会越来越大 -
垃圾回收的时机:
- 调用gc.collect()
- 当gc模块的计数器达到阀值的时候。
- 程序退出的时候
- gc模块:
- 垃圾回收后的对象会放在gc.garbage这个列表中
- gc.get_threshold() 获取的gc模块中自动执行垃圾回收的频率
- gc.set_threshold(threshold0[,threshold1[,threshold2]) 设置自动执行垃圾回收的频率
- gc.get_count() 获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表
- gc.collect([generation]) 显式进行垃圾回收,可以输入参数,0代表只检查第一代的对象,1代表检查一,二代的对象,2代表检查一,二,三代的对象,如果不传参数,执行一个full collection,也就是等于传2。返回不可达(unreachable objects)对象的数
- gc模块唯一处理不了的是循环引用的类都有__del__方法,所以项目中要避免定义__del__方法