Python之内存池技术
问题:如果对象频繁的创建和销毁, 就会产生很多内存碎片,最终会影响系统的性能。而实际应用中,我们确实在做这样的操作,尤其是对小整数的使用,
比如1,2,3这些int
类型的数据,几乎每一次使用for
循环都会用到它们。即:
- 小整数频繁被使用与销毁
- 频繁的创建跟销毁对象将产生内存碎片
小整数对象
在python
里提供了对象池技术。int
类型数据是不可变对象,这意味着它可以被共享,在python
启动之后,就会在内存中申请一片内存,将频繁使用的小整数存储在这里,在整个程序运行期间,这些小整数都一直存在,不会被销毁,对它们的使用,仅仅增加了它们的引用计数而已。
那么有多少整数被缓存了呢?这个范围其实不大,只有[-5, 257]
。当然如果你想扩大这个范围,你可以选择修改python
源码来解决。
在python
交互式解释器里来验证:
>>> a = 12
>>> b = 12
>>> id(a)
140705565998496
>>> id(b)
140705565998496
>>> a = 257
>>> b = 257
>>> id(a)
2389141537712
>>> id(b)
2389141537904
观察上面的结果,可以发现在缓存范围内,其内存地址不变,反之,则变。
字符串驻留
对于字符串同样也是如此。假设有100个变量,都赋值为python
,难道真得要在内存当中创建100个对象?为此,python
提供了intern
机制。简单来说,python
内部维护一个字典(interned
),当一个字符串需要驻留时,就去interned
中查看这个字符串是否已经存在,如果存在则增加引用计数,不存在的话就增加到字典中。
使用驻留技术,有下面两个好处:
- 节省内存
- 字符串比较时,驻留字符串的比较速度远远高于非驻留字符串
什么时候发生驻留
在
python
交互式解释器里来验证会准确些
-
编译时发生驻留,运行时不驻留
s1 = 'py' + 'thon' print(s1 is 'python') a = 'py' b = 'thon' print(a+b is 'python') # 输出结果 # True # False
结果差异原因:
s1
值是在编译阶段就计算出来的,因此会驻留,而a+b
只有在运行阶段才会计算,因此没有发生驻留 -
只含大小写字母、数字、下划线时发生驻留
s1 = 'python' s2 = 'python' print(s1 is s2) a1 = 'pyth on' b2 = 'pyth on' print(a1 is b2) # 输出结果 # True # False
-
字符串长度为0或1
空字符串和长度为1的字符串默认都会驻留,
python
认为这样的字符串都是经常被使用的字符串 -
被
sys.intern
指定驻留from sys import intern s1 = intern('python!') s2 = intern('python!') print(s1 is s2)
-
用乘法
(*)
得到的字符串这部分是比较复杂的规则 ,先来看乘数是1的情况:
-
字符串只包含下划线,数字,字母,默认驻留
-
字符串长度小于等于1,默认驻留
>>> s1 = "hello" >>> s2 = s1*1 >>> s1 is s2 True
如果乘数大于2,规则如下:
-
字符串只包含下划线,数字,字母且长度小于等于20,默认驻留
-
含有其它字符时,不论长度是多少,都不驻留
>>> s1 = "pythonpythonpython" >>> s2 = "python"*3 >>> s1 is s2 True >>> s1 = "&&&" >>> s2 = "&" * 3 >>> s1 is s2 False
-