• python之内存池技术


    Python之内存池技术


    问题:如果对象频繁的创建和销毁, 就会产生很多内存碎片,最终会影响系统的性能。而实际应用中,我们确实在做这样的操作,尤其是对小整数的使用,

    比如1,2,3这些int类型的数据,几乎每一次使用for循环都会用到它们。即:

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

    小整数对象

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    观察上面的结果,可以发现在缓存范围内,其内存地址不变,反之,则变。

    字符串驻留

    对于字符串同样也是如此。假设有100个变量,都赋值为python,难道真得要在内存当中创建100个对象?为此,python提供了intern机制。简单来说,python内部维护一个字典(interned),当一个字符串需要驻留时,就去interned中查看这个字符串是否已经存在,如果存在则增加引用计数,不存在的话就增加到字典中。

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

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

    什么时候发生驻留

    python交互式解释器里来验证会准确些

    1. 编译时发生驻留,运行时不驻留

      s1 = 'py' + 'thon'
      print(s1 is 'python')
      
      a = 'py'
      b = 'thon'
      
      print(a+b is 'python')
      
      # 输出结果
      # True
      # False
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      结果差异原因:s1值是在编译阶段就计算出来的,因此会驻留,而a+b只有在运行阶段才会计算,因此没有发生驻留

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

      s1 = 'python'
      s2 = 'python'
      print(s1 is s2)
      
      a1 = 'pyth on'
      b2 = 'pyth on'
      print(a1 is b2)
      
      # 输出结果
      # True
      # False
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    3. 字符串长度为0或1

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

    4. sys.intern指定驻留

      from sys import intern
       
      s1 = intern('python!')
      s2 = intern('python!')
      
      print(s1 is s2)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    5. 用乘法(*)得到的字符串

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

      1. 字符串只包含下划线,数字,字母,默认驻留

      2. 字符串长度小于等于1,默认驻留

        >>> s1 = "hello"
        >>> s2 = s1*1
        >>> s1 is s2
        True
        
        • 1
        • 2
        • 3
        • 4

      如果乘数大于2,规则如下:

      1. 字符串只包含下划线,数字,字母且长度小于等于20,默认驻留

      2. 含有其它字符时,不论长度是多少,都不驻留

        >>> s1 = "pythonpythonpython"
        >>> s2 = "python"*3
        >>> s1 is s2
        True
        >>> s1 = "&&&"
        >>> s2 = "&" * 3
        >>> s1 is s2
        False
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
  • 相关阅读:
    ssm学生成绩管理
    特拉斯成为英国首相后 “英镑危机”的风险正在上升
    第1关:构造函数与析构函数的实现
    MySQL基础【学习至数据的导入导出】
    C++ 深度解析教程(十七)C 语言异常处理、C++ 中的异常处理、C++中的类型识别
    5-4 jmu-报数游戏 (15分)
    计算机毕业设计Java教师业绩考核和职称评审系统(源码+系统+mysql数据库+lw文档)
    冒泡,选择,插入,希尔排序
    卡特兰数和算法
    前端路由中的meta、matched是什么?有哪些作用?
  • 原文地址:https://blog.csdn.net/qsloony/article/details/125560156