内存池技术

一切皆对象,关于这个概念的理解,除了要把握“一切”之外,还要关注“对象”二字,这暗示着在内存中存在一片区域,这篇区域就是我们心心念念的对象。对象有自己的生命周期,想必你已经了解了python的垃圾回收技术。

如果对象频繁的创建和销毁,就会产生很多内存碎片,最终会影响的系统的性能。而实际应用中,我们确实在做这样的事情,尤其是对小整数的使用,比如1,2, 5 这些int类型的数据,几乎每一次使用for循环都会用到他们,ok,现在我们掌握了两个关键信息

  1. 小的整数频繁被使用,频繁被销毁
  2. 频繁的创建对象销毁对象将产生内存碎片

那么,python的发明及维护人员允许这样的事情发生么?显然不能!

1. 小整数对象

如果没有什么特殊的机制,小整数的频繁创建与销毁将影响系统的性能,因此,python提供了对象池技术。int类型数据是不可变对象,这意味着它可以被共享,在python启动之后,就会在内存中申请一片内存,将频繁使用的小整数存储在这里,在整个程序运行期间,这些小整数都一直存在,不会被销毁,对他们的使用,仅仅增加了他们的引用计数而已。

有多少整数被缓存了呢,这个范围很小,[-5, 257), 你可能会对这个小整数范围表示有异议,这很正常,每个人对频繁使用的小整数有自己的理解,所以,如果你希望扩大这个范围,可以修改python的源码,然后重新编译,不过这很费力气。

我们可以在python交互式解释器里来验证这个小整数缓存池是否真实的存在

>>> a = 256
>>> b = 256
>>> id(a)
4511188896
>>> id(b)
4511188896
>>> a = 257
>>> b = 257
>>> id(a)
4515645744
>>> id(b)
4515645840

观察a,b两个变量的内存地址,当他们都赋值为256时,他们的内存地址是相同的,而都赋值为257时,内存地址便不同,小整数缓存池的范围恰好是[-5, 257).

2. 字符串驻留

对于字符串的使用,同样存在着内存的困扰。假设有100个变量,都赋值为'python',我们真的要在内存当中创建出100个字符串对象么,他们的值完全相同啊!

显然不能这样做,实在是太浪费内存了。为此,python提供了intern机制。简单来说,python内部维护了一个字典(interned),当一个字符串需要驻留时,就去interned中查看这个字符串是否已经存在,如果存在则增加字符串的引用计数,如果不存在则增加到字典中。

使用驻留技术,有如下两个好处:

  1. 节省内存
  2. 字符串比较时,驻留字符串的比较速度远远高于非驻留字符串

2.1 什么时候发生驻留

以下代码,一定要在交互式解释器中运行,在pycharm等编辑器中执行,或由于编辑器支持不够好,或由于编辑器本身做了优化,可能得不到与理论相符的结果

2.1.1 编译时发生驻留,运行时不驻留

s1 = 'py' + 'thon'
print(s1 is 'python')

a = 'py'
b = 'thon'
print(a+b is 'python')

输出结果为

True
False

s1的值是在编辑阶段就计算出来的,因此会驻留,而a+b只有在运行阶段才会计算,因此没有发生驻留

2.1.2 只含大小写字母、数字、下划线时发生驻留

s1 = 'python'
s2 = 'python'
print(s1 is s2)

s1 = 'pyth on'
s2 = 'pyth on'
print(s1 is s2)

输出结果为

True
False

2.1.3 字符串长度为0或1

空字符串和长度为1的字符串默认都会驻留,python认为这样的字符串都是经常被使用的字符串

2.1.4 被sys.intern指定驻留

from sys import intern

s1 = intern('python!')
s2 = intern('python!')

print(s1 is s2)

2.1.5 用乘法(*) 得到的字符串

这部分是比较复杂的规则,先来看乘数是1的情况

  1. 字符串只包含下划线,数字,字母,默认驻留
  2. 字符串长度小于等于1,默认驻留
>>> s1 = 'uwesdfwe'
>>> s2 = s1*1
>>> s1 is s2
True

如果程序大于2,规则如下

  1. 字符串只包含下划线,数字,字母且长度小于等于20,默认驻留
  2. 含有其他字符时,不论长度是多少,都不驻留
>>> s1 = 'pythonpythonpython'
>>> s2 = 'python'*3
>>> s1 is s2
True
>>> s1 = '&&&'
>>> s2 = '&'*3
>>> s1 is s2
False

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案