精华内容
下载资源
问答
  • Python内存管理机制

    2018-10-23 17:39:06
    实验中遇到了Python内存泄漏的问题,网上了一些资源对Python内存管理机制和内存优化进行了学习,先把内存管理机制进行了总结,内容很多是参考和复制参考文献中的内容。 1、变量与对象关系 在Python中,变量...

    实验中遇到了Python内存泄漏的问题,网上找了一些资源对Python 的内存管理机制和内存优化进行了学习,先把内存管理机制进行了总结,内容很多是参考和复制参考文献中的内容。

    1、变量与对象关系

    在Python中,变量不同于C语言中的变量机制,对C语言来说,我们创建一个变量A时就会为为该变量申请一个内存空间,并将变量值 放入该空间中,当将该变量赋给另一变量B时会为B也申请一个新的内存空间,并将变量值放入到B的内存空间中,所以A和B的指针不一致,下图:

    但是在Python中,变量是附在对象上的标签。当变量被绑定在一个对象上的时候,该变量的引用计数就是1。

    在Python中变量通过变量指针引用对象,变量指针指向具体对象的内存空间,取对象的值。而对象,类型已知,每个对象都包含一个头部信息(头部信息:类型标识符和引用计数器)。变量单独来说只是一个符号或标签,没有太多的意义,当它绑定到对象上时,才会有具体作用,变量引用什么类型的对象,变量就是什么类型的。

    一些需要注意的细节 

    • 当一个变量“引用”另一个变量时,两个变量所指的对象是一个地址和数值。
    >>> var1 = 123
    >>> var2 = var1
    >>> id(var1)
    1749839904
    >>> id(var2)
    1749839904
    • 当一个变量var4“引用”另一个变量var3,var3赋予新的对象时,var3的id改变,var4任然不变。并且对比上图代码,我们可以知道对于某些类型对象,其在Python中的地址是不变的,比如123,数值型变量引用时其id地址就是1749839904,有兴趣的可以试试将123当做列表等类型,查看其Id有什么变化。
    >>> var3 = 123
    >>> var4 = var3
    >>> id(var3)
    1749839904
    >>> id(var4)
    1749839904
    
    >>> var3 = 456
    >>> id(var3)
    48910864
    
    >>> id(var4)
    1749839904
    
    •  通过is进行引用所指判断,is是用来判断两个引用所指的对象是否相同。
    • Python缓存了整数和短字符串,每个对象在内存中只存有一份,引用所指对象就是相同的,即使使用赋值语句,也只是创造新的引用,而不是对象本身;
    • Python没有缓存长字符串、列表及其他对象,可以由多个相同的对象,可以使用赋值语句创建出新的对象。
    >>> var5 = 1
    >>> var6 = 2
    >>> print(var5 is var6)
    True
    >>> a = "hello"
    >>> b = "hello"
    >>> print(a is b)
    True
    >>> var7 = "hello world"
    >>> var8 = "hello world"
    >>> print(var7 is var8)
    False
    >>> var9 = []
    >>> var10 =[]
    >>> print(var9 is var10)
    False
    >>> c=256
    >>> d=256
    >>> print(c is d)
    True
    >>> e=257
    >>> f=257
    >>> print(e is f)
    False
    

    2、引用计数

    Python采用了类似Windows内核对象一样的方式来对内存进行管理。每个对象都有指向该对象的引用总数——引用计数。我们使用sys.getrefcount()查看对象的引用计数。

    • 普通引用
    >>> import sys
    >>> a  = [1,2,3]
    >>> sys.getrefcount(a)
    2
    >>> b = a
    >>> sys.getrefcount(a)
    3
    >>> sys.getrefcount(b)
    3

     当使用某个引用作为参数,传递给sys.getrefcount()时,参数实际上创建了一个临时的引用。因此,sys.getrefcount()得到的结果,会多1。

    • 容器对象

    Python的一个容器对象(比如:表、词典等),可以包含多个对象。

    >>> a = [1,2,3,4]
    >>> b = a
    >>> print(a is b)
    True
    >>> a[0] = 5
    >>> a
    [5, 2, 3, 4]
    >>> print(a is b)
    True
    >>> b
    [5, 2, 3, 4]

     在Python中,容器对象中包含的并不是元素对象本身,而是指向各个元素对象的引用。

    下面一些情况下引用计数增加:

    1.对象被创建:x=4

    2.另外的别人被创建:y=x

    3.被作为参数传递给函数:foo(x)

    4.作为容器对象的一个元素:a=[1,x,'33']

    引用计数减少:

    1.一个本地引用离开了它的作用域。比如上面的foo(x)函数结束时,x指向的对象引用减1。

    2.对象的别名被显式的销毁:del x ;或者del y

    3.对象的一个别名被赋值给其他对象:x=789

    4.对象从一个窗口对象中移除:myList.remove(x)

    5.窗口对象本身被销毁:del myList,或者窗口对象本身离开了作用域。

    3、垃圾回收

    Python不同于C等语言,可以不用事先声明变量类型而直接对变量进行赋值。对Python来说,对象的类型和内存都是在运行时确定的,所以说Python是动态语言。

    当Python中的对象越来越多,占据越来越大的内存,就会启动垃圾回收,将没用的对象清除。当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾。比如某个新建对象,被分配给某个引用,对象的引用计数变为1。如果引用被删除,对象的引用计数为0,那么该对象就可以被垃圾回收。

    当内存中有不再使用的部分时,垃圾收集器就会把他们清理掉。它会去检查那些引用计数为0的对象,然后清除其在内存的空间。当然除了引用计数为0的会被清除,还有一种情况也会被垃圾收集器清掉:当两个对象相互引用时,他们本身其他的引用已经为0。

    • 使用del
    >>> var10 = [1234]
    >>> del var10
    >>> id(var10)
    Traceback (most recent call last):
      File "<pyshell#47>", line 1, in <module>
        id(var10)
    NameError: name 'var10' is not defined

    del var10,已经没有任何引用指向[1234],该表引用计数变为0,Python扫描到这个引用计数为0的对象,就将它所占据的内存清空。 所以使用id函数时,会报错说var10没有被定义。

    一些注意

    1、垃圾回收时,Python不能进行其它的任务,频繁的垃圾回收将大大降低Python的工作效率;

    2、Python只会在特定条件下,自动启动垃圾回收(垃圾对象少就没必要回收)

    3、当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。

    gc模块中查看阈值的方法:

    >>> import gc
    >>> gc.get_threshold()
    (700, 10, 10)

    700是垃圾回收启动的阈值。每10次0代垃圾回收,会配合1次1代的垃圾回收;而每10次1代的垃圾回收,才会有1次的2代垃圾回收;对于Pyhon中分代的理解:Python将所有的对象分为0,1,2三代;所有的新建对象都是0代对象;当某一代对象经历过垃圾回收,依然存活,就被归入下一代对象。

    也可以手动启动垃圾回收,直接调用gc.collect()函数。

    >>> gc.collect()
    6
    

    4、内存池机制

    Python中有分为大内存和小内存(256K界限):

    • 大内存使用malloc进行分配
    • 小内存使用内存池进行分配
    • Python的内存池(金字塔)

    Python的内存机制以金字塔行:

    -1,-2层主要有操作系统进行操作。

    第0层是C中的malloc,free等内存分配和释放函数进行操作。

    第1层和第2层是内存池,有Python的接口函数PyMem_Malloc函数实现,当对象小于256K时有该层直接分配内存。

    第3层是最上层,也就是我们对Python对象的直接操作。

    在 C 中如果频繁的调用 malloc 与 free 时,是会产生性能问题的,再加上频繁的分配与释放小块的内存会产生内存碎片。Python 在这里主要干的工作有:

    如果请求分配的内存在1~256字节之间就使用自己的内存管理系统,否则直接使用 malloc。

    这里还是会调用 malloc 分配内存,但每次会分配一块大小为256k的大块内存。

    对于简单的Python对象,例如数值、字符串,元组(tuple不允许被更改)采用的是复制的方式(深拷贝),即是当将另一个变量B赋值给变量A时,虽然A和B的内存空间仍然相同,但当A的值发生变化时,会重新给A分配空间,A和B的地址变得不再相同。而对于像字典(dict),列表(List)等,改变一个就会引起另一个的改变,也称之为浅拷贝。

    参考资料

    [1]https://www.cnblogs.com/geaozhang/p/7111961.html

    [2]https://www.cnblogs.com/aomi/p/7353411.html

    [3]https://blog.csdn.net/github_38976972/article/details/79633214?utm_source=blogxgwz5

    [4]https://blog.csdn.net/github_38976972/article/details/79633214?utm_source=blogxgwz5

    展开全文
  • 去年自己写过一个程序时,不太确定自己的内存使用量,就想写工具来打印程序或函数的内存使用量。这里将上次找到的2个内存检测工具的基本用法记录一下,今后分析Python程序内存使用量时也是需要的。 memory_...
  • 什么是内存管理器(what) Python作为一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言,与大多数编程语言不同,Python中...如果大家在学习中遇到困难,想一个python学习交流环境,可以加入我们的...

     

    什么是内存管理器(what)

    Python作为一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言,与大多数编程语言不同,Python中的变量无需事先申明,变量无需指定类型,程序员无需关心内存管理,Python解释器给你自动回收。开发人员不用过多的关心内存管理机制,这一切全部由python内存管理器承担了复杂的内存管理工作。

    内存不外乎创建和销毁两部分,本文将围绕python的内存池和垃圾回收两部分进行分析。

    如果大家在学习中遇到困难,想找一个python学习交流环境,可以加入我们的python裙,裙号930900780,可领取python学习资料,会节约很多时间,减少很多遇到的难题。

    Python内存池

    为什么要引入内存池(why)

    当创建大量消耗小内存的对象时,频繁调用new/malloc会导致大量的内存碎片,致使效率降低。内存池的作用就是预先在内存中申请一定数量的,大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够之后再申请新的内存。这样做最显著的优势就是能够减少内存碎片,提升效率。

    python中的内存管理机制为Pymalloc

    内存池是如果工作的(how)

    首先,我们看一张CPython(python解释器)的内存架构图:

    [图片上传失败...(image-95c04f-1610157099583)]

    • python的对象管理主要位于Level+1~Level+3层
    • Level+3层:对于python内置的对象(比如int,dict等)都有独立的私有内存池,对象之间的内存池不共享,即int释放的内存,不会被分配给float使用
    • Level+2层:当申请的内存大小小于256KB时,内存分配主要由 Python 对象分配器(Python’s object allocator)实施
    • Level+1层:当申请的内存大小大于256KB时,由Python原生的内存分配器进行分配,本质上是调用C标准库中的malloc/realloc等函数

    关于释放内存方面,当一个对象的引用计数变为0时,Python就会调用它的析构函数。调用析构函数并不意味着最终一定会调用free来释放内存空间,如果真是这样的话,那频繁地申请、释放内存空间会使Python的执行效率大打折扣。因此在析构时也采用了内存池机制,从内存池申请到的内存会被归还到内存池中,以避免频繁地申请和释放动作。

    垃圾回收机制

    Python的垃圾回收机制采用引用计数机制为主,标记-清除和分代回收机制为辅的策略。其中,标记-清除机制用来解决计数引用带来的循环引用而无法释放内存的问题,分代回收机制是为提升垃圾回收的效率。

    引用计数

    Python通过引用计数来保存内存中的变量追踪,即记录该对象被其他使用的对象引用的次数。

    Python中有个内部跟踪变量叫做引用计数器,每个变量有多少个引用,简称引用计数。当某个对象的引用计数为0时,就列入了垃圾回收队列。

    >>> a=[1,2]>>> import sys>>> sys.getrefcount(a)  ## 获取对象a的引用次数2>>> b=a>>> sys.getrefcount(a)3>>> del b  ## 删除b的引用>>> sys.getrefcount(a)2>>> c=list()>>> c.append(a) ## 加入到容器中>>> sys.getrefcount(a)3>>> del c  ## 删除容器,引用-1>>> sys.getrefcount(a)2>>> b=a>>> sys.getrefcount(a)3>>> a=[3,4]  ## 重新赋值>>> sys.getrefcount(a)2复制代码
    

    注意:当把a作为参数传递给getrefcount时,会产生一个临时的引用,因此得出来的结果比真实情况+1

    • 引用计数增加的情况:
    1. 一个对象被分配给一个新的名字(例如:a=[1,2])
    2. 将其放入一个容器中(如列表、元组或字典)(例如:c.append(a))
    • 引用计数减少的情况:
    1. 使用del语句对对象别名显式的销毁(例如:del b)
    2. 对象所在的容器被销毁或从容器中删除对象(例如:del c )
    3. 引用超出作用域或被重新赋值(例如:a=[3,4])

    引用计数能够解决大多数垃圾回收的问题,但是遇到两个对象相互引用的情况,del语句可以减少引用次数,但是引用计数不会归0,对象也就不会被销毁,从而造成了内存泄漏问题。针对该情况,Python引入了标记-清除机制

    标记-清除

    标记-清除用来解决引用计数机制产生的循环引用,进而导致内存泄漏的问题 。 循环引用只有在容器对象才会产生,比如字典,元组,列表等。

    顾名思义,该机制在进行垃圾回收时分成了两步,分别是:

    • 标记阶段,遍历所有的对象,如果是可达的(reachable),也就是还有对象引用它,那么就标记该对象为可达
    • 清除阶段,再次遍历对象,如果发现某个对象没有标记为可达(即为Unreachable),则就将其回收
    >>> a=[1,2]>>> b=[3,4]>>> sys.getrefcount(a)2>>> sys.getrefcount(b)2>>> a.append(b)>>> sys.getrefcount(b)3>>> b.append(a)>>> sys.getrefcount(a)3>>> del a>>> del b复制代码
    
    • a引用b,b引用a,此时两个对象各自被引用了2次(去除getrefcout()的临时引用)

    • 执行del之后,对象a,b的引用次数都-1,此时各自的引用计数器都为1,陷入循环引用

    • 标记:找到其中的一端a,因为它有一个对b的引用,则将b的引用计数-1

    • 标记:再沿着引用到b,b有一个a的引用,将a的引用计数-1,此时对象a和b的引用次数全部为0,被标记为不可达(Unreachable)

    • 清除: 被标记为不可达的对象就是真正需要被释放的对象

    上面描述的垃圾回收的阶段,会暂停整个应用程序,等待标记清除结束后才会恢复应用程序的运行。为了减少应用程序暂停的时间,Python 通过“分代回收”(Generational Collection)以空间换时间的方法提高垃圾回收效率。

    分代回收

    分代回收是基于这样的一个统计事实,对于程序,存在一定比例的内存块的生存周期比较短;而剩下的内存块,生存周期会比较长,甚至会从程序开始一直持续到程序结束。生存期较短对象的比例通常在 80%~90%之间。 因此,简单地认为:对象存在时间越长,越可能不是垃圾,应该越少去收集。这样在执行标记-清除算法时可以有效减小遍历的对象数,从而提高垃圾回收的速度,是一种以空间换时间的方法策略

    Python将所有的对象分为年轻代(第0代)、中年代(第1代)、老年代(第2代)三代。所有的新建对象默认是 第0代对象。当在第0代的gc扫描中存活下来的对象将被移至第1代,在第1代的gc扫描中存活下来的对象将被移至第2代。

    gc扫描次数(第0代>第1代>第2代)

    当某一代中被分配的对象与被释放的对象之差达到某一阈值时,就会触发当前一代的gc扫描。当某一代被扫描时,比它年轻的一代也会被扫描,因此,第2代的gc扫描发生时,第0,1代的gc扫描也会发生,即为全代扫描。

    >>> import gc >>> gc.get_threshold() ## 分代回收机制的参数阈值设置(700, 10, 10)复制代码
    
    • 700=新分配的对象数量-释放的对象数量,第0代gc扫描被触发
    • 第一个10:第0代gc扫描发生10次,则第1代的gc扫描被触发
    • 第二个10:第1代的gc扫描发生10次,则第2代的gc扫描被触发

    思考

    在标记-清除中,如果对象c也引用a,执行del操作后,会发生什么?

    对象a,b,c的引用关系如下图所示:

    >>> a=[1,2]>>> b=[3,4]>>> c=a>>> a.append(b)>>> b.append(a)复制代码
    

    • ref_count表示引用计数
    • 对象a,b,c全部为reachable

    执行del之后,引用关系如下图所示:

    >>> del a>>> del b复制代码
    

    • a,b,c的ref_count减1

    执行gc扫描

    • 标记: a引用b,将b的ref_count减1到0,b引用a,将a的ref_count减1到1,将b放在unreachable下
    • 再循环:因为a是可达的,所以会递归地将从a节点出发可以达到的所有节点标记为reachable下,即为:
    • 清除:unreachable下没有可清除的对象,因此a,b,c对象不会被清除

    总结

    总体而言,python通过内存池来减少内存碎片化,提高执行效率。主要通过引用计数来完成垃圾回收,通过标记-清除解决容器对象循环引用造成的问题,通过分代回收提高垃圾回收的效率。

    展开全文
  • 什么是内存管理器(what) Python作为一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言,与大多数编程语言不同,Python中...如果大家在学习中遇到困难,想一个python学习交流环境,可以加入我们的pyt

    什么是内存管理器(what)

    Python作为一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言,与大多数编程语言不同,Python中的变量无需事先申明,变量无需指定类型,程序员无需关心内存管理,Python解释器给你自动回收。开发人员不用过多的关心内存管理机制,这一切全部由python内存管理器承担了复杂的内存管理工作。

    内存不外乎创建和销毁两部分,本文将围绕python的内存池和垃圾回收两部分进行分析。

    面试必备:Python内存管理机制(建议收藏)

    如果大家在学习中遇到困难,想找一个python学习交流环境,可以加入我们的python裙,裙号930900780,可领取python学习资料,会节约很多时间,减少很多遇到的难题。

    Python内存池

    为什么要引入内存池(why)

    当创建大量消耗小内存的对象时,频繁调用new/malloc会导致大量的内存碎片,致使效率降低。内存池的作用就是预先在内存中申请一定数量的,大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够之后再申请新的内存。这样做最显著的优势就是能够减少内存碎片,提升效率。

    python中的内存管理机制为Pymalloc

    内存池是如果工作的(how)

    首先,我们看一张CPython(python解释器)的内存架构图:

    面试必备:Python内存管理机制(建议收藏)

     

    • python的对象管理主要位于Level+1~Level+3层
    • Level+3层:对于python内置的对象(比如int,dict等)都有独立的私有内存池,对象之间的内存池不共享,即int释放的内存,不会被分配给float使用
    • Level+2层:当申请的内存大小小于256KB时,内存分配主要由 Python 对象分配器(Python’s object allocator)实施
    • Level+1层:当申请的内存大小大于256KB时,由Python原生的内存分配器进行分配,本质上是调用C标准库中的malloc/realloc等函数

    关于释放内存方面,当一个对象的引用计数变为0时,Python就会调用它的析构函数。调用析构函数并不意味着最终一定会调用free来释放内存空间,如果真是这样的话,那频繁地申请、释放内存空间会使Python的执行效率大打折扣。因此在析构时也采用了内存池机制,从内存池申请到的内存会被归还到内存池中,以避免频繁地申请和释放动作。

    垃圾回收机制

    Python的垃圾回收机制采用引用计数机制为主,标记-清除和分代回收机制为辅的策略。其中,标记-清除机制用来解决计数引用带来的循环引用而无法释放内存的问题,分代回收机制是为提升垃圾回收的效率。

    引用计数

    Python通过引用计数来保存内存中的变量追踪,即记录该对象被其他使用的对象引用的次数。

    Python中有个内部跟踪变量叫做引用计数器,每个变量有多少个引用,简称引用计数。当某个对象的引用计数为0时,就列入了垃圾回收队列。

    >>> a=[1,2]
    >>> import sys
    >>> sys.getrefcount(a)  ## 获取对象a的引用次数
    2
    >>> b=a
    >>> sys.getrefcount(a)
    3
    >>> del b  ## 删除b的引用
    >>> sys.getrefcount(a)
    2
    >>> c=list()
    >>> c.append(a) ## 加入到容器中
    >>> sys.getrefcount(a)
    3
    >>> del c  ## 删除容器,引用-1
    >>> sys.getrefcount(a)
    2
    >>> b=a
    >>> sys.getrefcount(a)
    3
    >>> a=[3,4]  ## 重新赋值
    >>> sys.getrefcount(a)
    2
    复制代码

    注意:当把a作为参数传递给getrefcount时,会产生一个临时的引用,因此得出来的结果比真实情况+1

    • 引用计数增加的情况:
    1. 一个对象被分配给一个新的名字(例如:a=[1,2])
    2. 将其放入一个容器中(如列表、元组或字典)(例如:c.append(a))
    • 引用计数减少的情况:
    1. 使用del语句对对象别名显式的销毁(例如:del b)
    2. 对象所在的容器被销毁或从容器中删除对象(例如:del c )
    3. 引用超出作用域或被重新赋值(例如:a=[3,4])

    引用计数能够解决大多数垃圾回收的问题,但是遇到两个对象相互引用的情况,del语句可以减少引用次数,但是引用计数不会归0,对象也就不会被销毁,从而造成了内存泄漏问题。针对该情况,Python引入了标记-清除机制

    标记-清除

    标记-清除用来解决引用计数机制产生的循环引用,进而导致内存泄漏的问题 。 循环引用只有在容器对象才会产生,比如字典,元组,列表等。

    顾名思义,该机制在进行垃圾回收时分成了两步,分别是:

    • 标记阶段,遍历所有的对象,如果是可达的(reachable),也就是还有对象引用它,那么就标记该对象为可达
    • 清除阶段,再次遍历对象,如果发现某个对象没有标记为可达(即为Unreachable),则就将其回收
    >>> a=[1,2]
    >>> b=[3,4]
    >>> sys.getrefcount(a)2
    >>> sys.getrefcount(b)2
    >>> a.append(b)>>> sys.getrefcount(b)3
    >>> b.append(a)>>> sys.getrefcount(a)3
    >>> del a
    >>> del b
    复制代码
    • a引用b,b引用a,此时两个对象各自被引用了2次(去除getrefcout()的临时引用)

    面试必备:Python内存管理机制(建议收藏)

     

    • 执行del之后,对象a,b的引用次数都-1,此时各自的引用计数器都为1,陷入循环引用

    面试必备:Python内存管理机制(建议收藏)

     

    • 标记:找到其中的一端a,因为它有一个对b的引用,则将b的引用计数-1

    面试必备:Python内存管理机制(建议收藏)

     

    • 标记:再沿着引用到b,b有一个a的引用,将a的引用计数-1,此时对象a和b的引用次数全部为0,被标记为不可达(Unreachable)

    面试必备:Python内存管理机制(建议收藏)

     

    • 清除: 被标记为不可达的对象就是真正需要被释放的对象

    上面描述的垃圾回收的阶段,会暂停整个应用程序,等待标记清除结束后才会恢复应用程序的运行。为了减少应用程序暂停的时间,Python 通过“分代回收”(Generational Collection)以空间换时间的方法提高垃圾回收效率。

    分代回收

    分代回收是基于这样的一个统计事实,对于程序,存在一定比例的内存块的生存周期比较短;而剩下的内存块,生存周期会比较长,甚至会从程序开始一直持续到程序结束。生存期较短对象的比例通常在 80%~90%之间。 因此,简单地认为:对象存在时间越长,越可能不是垃圾,应该越少去收集。这样在执行标记-清除算法时可以有效减小遍历的对象数,从而提高垃圾回收的速度,是一种以空间换时间的方法策略

    Python将所有的对象分为年轻代(第0代)、中年代(第1代)、老年代(第2代)三代。所有的新建对象默认是 第0代对象。当在第0代的gc扫描中存活下来的对象将被移至第1代,在第1代的gc扫描中存活下来的对象将被移至第2代。

    gc扫描次数(第0代>第1代>第2代)

    当某一代中被分配的对象与被释放的对象之差达到某一阈值时,就会触发当前一代的gc扫描。当某一代被扫描时,比它年轻的一代也会被扫描,因此,第2代的gc扫描发生时,第0,1代的gc扫描也会发生,即为全代扫描。

    >>> import gc 
    >>> gc.get_threshold() ## 分代回收机制的参数阈值设置
    (700, 10, 10)
    复制代码
    • 700=新分配的对象数量-释放的对象数量,第0代gc扫描被触发
    • 第一个10:第0代gc扫描发生10次,则第1代的gc扫描被触发
    • 第二个10:第1代的gc扫描发生10次,则第2代的gc扫描被触发

    思考

    在标记-清除中,如果对象c也引用a,执行del操作后,会发生什么?

    对象a,b,c的引用关系如下图所示:

    >>> a=[1,2]
    >>> b=[3,4]
    >>> c=a>>> a.append(b)
    >>> b.append(a)
    复制代码

    面试必备:Python内存管理机制(建议收藏)

     

    • ref_count表示引用计数
    • 对象a,b,c全部为reachable

    执行del之后,引用关系如下图所示:

    >>> del a
    >>> del b
    复制代码

    面试必备:Python内存管理机制(建议收藏)

     

    • a,b,c的ref_count减1

    执行gc扫描

    • 标记: a引用b,将b的ref_count减1到0,b引用a,将a的ref_count减1到1,将b放在unreachable下
    • 再循环:因为a是可达的,所以会递归地将从a节点出发可以达到的所有节点标记为reachable下,即为:
    • 清除:unreachable下没有可清除的对象,因此a,b,c对象不会被清除

    总结

    总体而言,python通过内存池来减少内存碎片化,提高执行效率。主要通过引用计数来完成垃圾回收,通过标记-清除解决容器对象循环引用造成的问题,通过分代回收提高垃圾回收的效率。

     

    最后多说一句,小编是一名python开发工程师,这里有我自己整理了一套最新的python系统学习教程,包括从基础的python脚本到web开发、爬虫、数据分析、数据可视化、机器学习等。想要这些资料的可以进裙930900780领取。

     

    本文章素材来源于网络,如有侵权请联系删除。

    展开全文
  • 部署的舆情系统,内存变大,原因。 一个小例子。 def func(): local_list = list(range(10000000)) func() time.sleep(200) 能够观察到,在sleep 200秒的时间内,程序的内存一直是200多M,虽然是函数...

    部署的舆情系统,内存变大,找原因。

    一个小例子。

    def func():

        local_list = list(range(10000000))

     

    func()

    time.sleep(200)

     

    能够观察到,在sleep 200秒的时间内,程序的内存一直是200多M,虽然是函数局部变量,执行后在外部无法使用了,但仍然占据大内存。

     

     

    再来一个

    global_list = list(range(10000000))

    del global_list

    time.sleep(200)

    能够观察到,在sleep 200秒的时间内,程序的内存一直也是200多M,引用计数变为0,但仍然占据大内存。

     

    引入gc模块

    在sleep之前插入 gc.collect()

    查看内存,内存直接下降到10M内了,这是我期待的。

     

    本篇的list代表一个巨大的内存的变量,以range(10000000)来指代。不要扯上什么xrange  range,我就是要用这个来简单指代一些巨大内存的变量。

    实际过程中指代的是下载 上传巨大的html源码,爬虫过程中如果遇到mp4 avi连接没有过滤,下载了mp4的字符串就会是一个巨大的字符串。

     

    之前有个地方,给njinx的网关接口上传结果,发现一直被njinx拒绝,请求不成功,而程序中遇到这种情况,一直等待60秒一直重试,导致永远不可能上传,内存永远降不下来。

     

    下一篇介绍,监控内存和释放内存。

     

    转载于:https://www.cnblogs.com/ydf0509/p/8053088.html

    展开全文
  • 首先说明,这些还仅限于我的理解阶段,可能有很多错误或者问题,...当某个数据要被存储时,首先系统根据该数据大小去已分配而未使用的内存,如果有,则把数据存储在这块内存区域。例如 188 数据(200) ----- 210
  • x =5之后,我们要了解它的过程:系统先是了一块内存,将5存储了进去,紧接着x指向了当前的这块内存 预测1:python下的变量是一个指针 >>> x = 4 >>> y = 5 >>> print x 4 >>> print y 5 >>> id(x) 10413988 >>> ...
  • 后来就到网上查找python内存分析的工具,查了一点资料发现python有个meliae库操作非常方便,就使用分析了一下,发现不是节点太多的原因0 0,是保存发送的t_id,用来标示返回的消息是那个发出的一个字典过大了。...
  • 本节内容:I/O操作概述文件读写实现原理与操作步骤文件打开模式Python文件操作步骤示例Python文件读取相关方法文件读写与字符编码一、I/O操作概述I/O在计算机中是指Input/Output,也就是Stream(流)的输入和输出。...
  • 内存断点原理:就是通过设置内存块页面的权限,使其成为保护页。这样当CPU访问该页面时,就会触发保护页的访问异常,产生中断。需要调用异常处理函数将权限恢复从而继续执行。 实现步骤: 1. 函数的地址还是...
  • 尽管那里有许多数据库,它们比DyDB强大且性能惊人,但是我最近在工作中发现了几种情况,当时我需要一个可以与Python对话的漂亮,简单的面向文档的数据库,但还没有被允许安装任何依赖项。 我剩下JSON,PickleDB和...
  • 内存中有主要有两种表现形式——栈和堆 栈的空间有限 一般存储较小的数据如临时变量 堆上的空间比较大 所以一般引用类型的变量都存储的堆 栈的回收速度快 堆的回收效率则不高,回收没有栈那么及时 栈上可以存储引用...
  • ** 注意设置环境变量(文件是操作系统的概念,操作系统优先在当前文件夹不到就到环境变量path)** ** python解释器不兼容** ** 解释器下载官网:python.org** 对Python感兴趣或者是正在学习的小伙伴,...
  • 本文结合数据与内存形态讲解python中的数据,内容包括: 引用与对象 可变数据类型与不可变数据类型 引用传递与值传递 深拷贝与浅拷贝 (id函数:你可以通过python的内置函数 id() 来查看对象...
  • 静态编译语言和动态解释型语言对于变量名的处理方式完全不同。对于静态编译语言:静态编译器或者链接器会以固定地址,或...我们可以将内存寻址比作顾客按照编号服务柜台。名字就是一个接待员,顾客只能通过接待...
  • 针对这个问题,我们首先需要了解Java和python的基本含义及应用范围。Java是面向对象的程序设计语言,是目前使用最为广泛的网络编程语言之一,它具有简单,面向对象,稳定,与平台无关,解释型,多线程,动态等特点。...
  • 题目类型:Python入门 通过率:147/157 (93.63%) 正确率:147/334 (44.01%) 更多题目 一、题目描述 输入一个正整数T,总共T组数据。 每组一行,输入一个正整数n,出小于等于n的完数。 完数就是除本身之外的...
  • 针对这个问题,我们首先需要了解Java和python的基本含义及应用范围。Java是面向对象的程序设计语言,是目前使用最为广泛的网络编程语言之一,它具有简单,面向对象,稳定,与平台无关,解释型,多线程,动态等特点。...
  • 题目:389. 不同 给定两个字符串 s 和 t,它们只包含小写字母。...python主要用在计算不重复的值,相同的都被消掉啦,消消乐~ class Solution: def findTheDifference(self, s: str, t: str) -> str
  • 这篇notebook展示了通过使用更合理的数据类型来减少dataframe的内存使用量 方法如下: 迭代每一个column 检查column是否为数字型 检查column是否可以用integer表示 出column下的最大值和最小值 选择适用于数据...
  • Python面试题

    2019-05-11 09:36:34
    背景:正在找Python相关工作 初衷:分享 基础篇 Python是如何进行内存管理的 Python引用了一个内存池(memory pool)机制,即Pymalloc机制(malloc:n.分配内存),用于管理对小块内存的申请和释放 内存池(memory pool...
  • python 基础

    2018-05-31 23:34:28
    python是解释型语言 即面向过程又面向对象 面向对象指对象和类 函数库丰富,写小程序,...2 堆,存储引用数据类型,存储的是内存地址,不直接代表值,变量存储内存地址存在栈里,由内存地址去堆中出数据   3...
  • python import

    2019-11-13 14:59:40
    1.python import时,不到文件 解决办法: imort sys sys.path 导入路径 sys.path.append(要导入文件的路径) 2.import 重新导入问题 因为python导入模块只会在内存中导入一份,如果修改了模块内容,要重新...
  • 7-2 奇葩 (20分) Python

    2020-12-12 11:25:03
    你的任务就是出这个奇葩。 输入格式: 输入首先在第一行给出一个正整数 n(≤10​4),随后一行给出 n 个满足题面描述的正整数。每个数值不超过 10​5,数字间以空格分隔。 输出格式: 在一行中输出那个奇葩数。...
  • python反射

    2019-01-12 13:50:00
    简单的说,在python中,能够通过一个对象,出其type,class,attribute或method的能力,称为反射或者自省 具有反射能力的函数有:type(),isinstance(),callable(),dir(),getattr() 例:有一个Point类,查看它...
  • 试题 算法训练 零钱 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述  有n个人正在饭堂排队买海北鸡饭。每份海北鸡饭要25元。奇怪的是,每个人手里只有一张钞票(每张钞票的面值为25、50、100元),而且饭堂...
  • 给定一个整数数组 nums和一个目标值 target,请你在该数组中出和为目标值的那两个整数,并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 示例: 给定...
  • Python Spyder 打不开报错,memory的alarm或者下载不了。 别用网上其他的各种方法,一般都是源是国外的,不对。 删除啥加啥文件,都不对。 conda啥pip啥也都不对。 网络问题的话导致安装失败。Anaconda默认的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 513
精华内容 205
关键字:

python找内存

python 订阅