
python内存管理、引用、深浅拷贝、垃圾回收机制
- 对象引用
-
- 引用计数
- 可变和不可变的引用
- 小整数池和大整数池
- intern机制
- 深浅copy
-
- 垃圾回收
对象引用
引用计数
- 变量:通过变量指针引用对象
-
- 对象:类型已知,每个对象都包含一个头部信息(头部信息:类型标识赫然引用计数器)
- 例如:
a = 10
b = a
可变和不可变的引用
- python中,可变的数据类型有:列表,字典和set;不可变的类型有:数值(int,float,bool),字符串和元组
- 不可变类型,修改变量的值的时候,指针指向的内存地址由修改前指向修改后
- 可变类型,修改变量的值的时候,是在原对象上修改对象本身的数据,不会更改指针的指向
print("*修改不可变类型的数值,指针的指向发生变化,对象本身不会变化*")
a = 1
b = 2
print(id(a),id(b)) print("修改可变类型,指针无变化,对象本身的数据变化")
data = [1,3]
print(id(data))
data.append(2)
print(id(data))
==========================run_result===========================
*修改不可变类型的数值,指针的指向发生变化,对象本身不会变化*
1797103774000 1797103774032
修改可变类型,指针无变化,对象本身的数据变化
1797109137600
1797109137600
小整数池和大整数池
- python中,自动将 -5 ~ 256 之间的整数进行了缓存,当将这些整数赋值给变量的时候,并不会重新创建对象,而是使用创建好的缓存对象(小整数池)
- python会将一定规则的字符串在字符串驻留池中,创建一份,当将这些字符串赋值给变量时,并不会重新创建对象,而是使用字符串驻留池中创建好的对象(大整数池)
-
- 在字符串驻留池中的字符串或者-5 ~ 256之间的数值,赋值给不同的变量后,其内存地址(id查看)一致,使用is语句判断时结果为True
- 在字符串驻留池之外的字符串或者-5 ~ 256之外的数值,赋值给不同的变量后,其内存地址(id查看)不一致,使用is语句判断时结果为TFalse
- 判断两个对象是否是一个有两种方式:
-
-
- 实现必须是使用ipython查看
In [1]: a = 257In [2]: b = 257In [3]: print(id(a),id(b))
4334648272 4334648720
In [4]: c = 10In [5]: d = 1In [7]: print(id(c),id(d))
4297966096 4297965808
In [8]: d = 10In [10]: print(id(c),id(d))
4297966096 4297966096
*is判断
In [1]: a = 10In [2]: b = 10In [3]: a is b
Out[3]: TrueIn [4]: x = 2000In [5]: y = 2000In [6]: x is y
Out[6]: False
intern机制
- 创建新的变脸指向字符串对象A时,会现在缓存的池子中查找是否已经A对象,如果有,直接拿过来引用,如果没有则新建
- 这样的好处是:避免繁琐的创建和销毁内存,提升效率

In [1]: a = "abc"In [2]: b="abc"In [3]: c = "xyz"In [5]: print(id(a),id(b),id(c))
1331287095728 1331287095728 1331355521072
深浅copy
- 有一篇文章讲解的很好,再次不再赘述,只做总结:《链接》
- python中有三种copy,赋值、浅copy(
copy.copy
)和深copy(copy.deepcopy
)
- 从原理上说:
-
- 赋值其实就是将新赋值的变量的指针指向元素本身,和原变量指针指向的对象一致
-
- 浅copy,新创建内存地址放置原变量最外层的元素,不包含嵌套元素,嵌套元素可以理解为只存储了一个变量名,指针指向没有记录
-
- 深copy,新创建内存地址放置原变量全部的元素,包含可变类型中的嵌套元素都重新开辟内存地址放置
- 深浅copy一般在嵌套列表的时候,进行讨论
不可变类型
- 对于不可变元素(字符串、元组和数字),经过赋值、浅copy和深copy后,其内存地址和信息会一模一样
- 改变其中任意一个元素,被改变的元素对象本身的数据和内存地址都会发生变化
- 赋值、浅copy和深copy后的对象和内存地址不会随原变量的修改而修改
import copy
list0 = 'hello,world'
list1 = copy.copy(list0)
list2 = copy.deepcopy(list0)
list3 = list0print('==========修改前==========')
print("原字符串:{},内存地址:{}".format(list0, id(list0)))
print("浅copy字符串:{},内存地址:{}".format(list1, id(list1)))
print("深copy字符串:{},内存地址:{}".format(list2, id(list2)))
print("=获得的字符串:{},内存地址:{}".format(list3, id(list3)))list0 = list0 + 'good evening'
print('==========修改后==========')
print("原字符串:{},内存地址:{}".format(list0, id(list0)))
print("浅copy字符串:{},内存地址:{}".format(list1, id(list1)))
print("深copy字符串:{},内存地址:{}".format(list2, id(list2)))
print("=获得的字符串:{},内存地址:{}".format(list3, id(list3)))
*run_result
==========修改前==========
原字符串:hello,world,内存地址:1884431123824
浅copy字符串:hello,world,内存地址:1884431123824
深copy字符串:hello,world,内存地址:1884431123824
=获得的字符串:hello,world,内存地址:1884431123824
==========修改后==========
原字符串:hello,worldgood evening,内存地址:1884431168496
浅copy字符串:hello,world,内存地址:1884431123824
深copy字符串:hello,world,内存地址:1884431123824
=获得的字符串:hello,world,内存地址:1884431123824
可变类型
- 对于可变元素,赋值、浅copy和深copy生成的变量
-
-
- 浅copy和深copy,只是数据对象和原变量相等,但是内存地址不一致
- 修改原变量非嵌套元素的值:
-
-
- 浅copy和深copy的元素对象不会发生变化(下面代码中,列表中新增的元素7)
- 修改原变量中,嵌套元素的值:
-
-
-
import copy
list0 = [1,2,3,4,5,6,{"name":"age"}]
list1 = copy.copy(list0)
list2 = copy.deepcopy(list0)
list3 = list0print('==========修改前==========')
print("原字符串:{},内存地址:{}".format(list0, id(list0)))
print("浅copy字符串:{},内存地址:{}".format(list1, id(list1)))
print("深copy字符串:{},内存地址:{}".format(list2, id(list2)))
print("=获得的字符串:{},内存地址:{}".format(list3, id(list3)))
print("打印嵌套元素的信息:{},地址信息:{}".format(list0[6],id(list0[6])))list0[6]["age"]=20
list0.append(7)
print('==========修改后==========')
print("原字符串:{},内存地址:{}".format(list0, id(list0)))
print("浅copy字符串:{},内存地址:{}".format(list1, id(list1)))
print("深copy字符串:{},内存地址:{}".format(list2, id(list2)))
print("=获得的字符串:{},内存地址:{}".format(list3, id(list3)))
print("打印浅copy嵌套元素的信息:{},地址信息:{}".format(list1[6],id(list1[6])))
print("打印深copy嵌套元素的信息:{},地址信息:{}".format(list2[6],id(list2[6])))run_result*
==========修改前==========
原字符串:[1, 2, 3, 4, 5, 6, {'name': 'age'}],内存地址:2060355547648
浅copy字符串:[1, 2, 3, 4, 5, 6, {'name': 'age'}],内存地址:2060355547520
深copy字符串:[1, 2, 3, 4, 5, 6, {'name': 'age'}],内存地址:2060355547456
=获得的字符串:[1, 2, 3, 4, 5, 6, {'name': 'age'}],内存地址:2060355547648
打印嵌套元素的信息:{'name': 'age'},地址信息:2060350729088
==========修改后==========
原字符串:[1, 2, 3, 4, 5, 6, {'name': 'age', 'age': 20}, 7],内存地址:2060355547648
浅copy字符串:[1, 2, 3, 4, 5, 6, {'name': 'age', 'age': 20}],内存地址:2060355547520
深copy字符串:[1, 2, 3, 4, 5, 6, {'name': 'age'}],内存地址:2060355547456
=获得的字符串:[1, 2, 3, 4, 5, 6, {'name': 'age', 'age': 20}, 7],内存地址:2060355547648
打印嵌套元素的信息:{'name': 'age', 'age': 20},地址信息:2060350729088
打印浅copy嵌套元素的信息:{'name': 'age', 'age': 20},地址信息:1540305791872
打印深copy嵌套元素的信息:{'name': 'age'},地址信息:1540305792448
垃圾回收
- 垃圾回收机制,使用与句话来形容:引用计数机制为主,标记、清除和分代收集技术为辅的策略
- 引用计数:每个对象创建之后,都有一个引用计数,当引用计数为0时,name此时的垃圾回收机制就会将其销毁,回收内存空间
- 引用计数存在一个缺点:那就是当两个对象出现循环引用的时候,最终这两个对象始终不会被销毁,这样就会造成内存泄漏
- 下图中,
Li1
和Li2
不再指向列表,但是列表内部存在循环引用
