赋值语句内存分析
◆ 使用id()方法访问内存地址
◆ 使用is比较内存引用地址是否相等
在python之中,像数值、字符串、布尔型单纯的赋值,它们只要值相等,那么对应的变量名所在的地址也是相等的,而这就是python对内存管理的优化。相对的,如:[]
列表的复制,其所对应的地址却是不同的。
代码:
1 | def extend_list(val, l=[]): |
输出如下:
1 | --------------------- |
结论:对比list1、list2、list3的输出结果后,就能了解在函数中默认的列表,其在python的内存存储机制。list2不同的原因,就在于它使用的是新生成的列表,而list1和list3相同的原因,则是它们使用的是函数中默认的列表,在内存中,它们是同一个。
垃圾回收机制
◆ 以引用计数为主,分代收集为辅
◆ 如果一个对象的引用数为0,Python虚拟机就会回收这个对象的内存
◆ 引用计数的缺陷是循环引用的问题
代码(1):
1 | class Cat(object): |
输出如下:
1 | 对象产生了:1775928937992 |
代码(2):
1 | class Cat(object): |
输出如下:
1 | 对象产生了:1627687778248 |
结论:对上面两个代码和输出结果对比,能发现。第一个代码因为后续没有再对之前的生成进行使用,所以虽然不是立刻删除,但也是会马上的进行删除,这样,内存的使用空间不会一直的增长。相对的,第二个代码,它把生成的对象存放到了列表之中,这个列表对之前生成的所有对象进行了引用,所以其存在就不会被删除,内存空间早晚会溢出。
引用计数(reference count)
◆ 每个对象都存有指向该对象的引用总数
◆ 查看某个对象的引用计数
sys.getrefcount()
◆ 可以使用del关键词删除某个引用
代码:
1 | import sys |
输出如下:
1 | 5 |
结论:可以很明显的看出,引用数不一定是从1开始,这涉及到python当中的内存共享的机制。而每一次的直接引用或者间接引用都会使引用数增加,而对s的引用次数的观察,可发现多了1,这是因为当我们输出的时候,也会对其进行引用,所以引用次数会多一次。
垃圾回收
◆ 满足特定条件,自动启动垃圾回收
◆ 当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数
◆ 当两者的差值高于某个阈(yù)值时,垃圾回收才会启动
◆ 查看阈值gc.get_threshold()
gc — Garbage Collector interface
代码:
1 | import gc |
输出如下:
1 | (700, 10, 10) |
分代回收
◆ Python将所有的对象分为0,1,2三代
◆ 所有的新建对象都是0代对象
◆ 当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象
PS:即初始为0代,每次回收不掉,就对其进行代数的提升,最高为2代。而其有阈值存在,当0代回收次数到达一定的阈值,才会回收1代,而1代回收到一定的阈值,才会回收2代。而1代和2代的回收,会把前面的回收再执行。
手动回收
◆ gc.collect()手动回收
◆ objgraph模块中的count()记录当前类产生的实例对象的个数
代码(1):
1 | import gc, sys |
输出如下:
1 | (700, 10, 10) |
代码(2):
1 | # 上面代码注释掉这两行代码 |
输出如下:
1 | (700, 10, 10) |
结论:如果不进行手动回收,那么就会导致回收不掉的。
内存管理机制
◆ 内存池(memory pool)机制
当创建大量消耗小内存的对象时,频繁调用new/malloc会导致大量的内存碎片,致使效率降低。内存池的概念就是预先再内存中申请一定数量的,大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够了之后再申请新的内存。这样做最显著的优势就是能够减少内存碎片,提升效率。
◆ Python3中的内存管理机制——Pymalloc
针对小对象(<=512bytes),pymalloc会在内存池中申请内存空间
当>512bytes,则会PyMem_RawMalloc()和PyMem_RawRealloc()来申请新的内存空间
◆ 单位换算
1 Byte = 8 Bit (即 1B = 8b)
1 KB = 1024 Bytes
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB
1 PB = 1024 TB
1 EB = 1024 PB
1 ZB = 1024 EB
1 YB = 1024 ZB
PS:Bit意为”位”或”比特”,时计算机运算的基础,属于二进制的范畴;Byte意为”字节”,时计算机文件大小的基础计算单位。
结论
慕课网 时间_记录昨天 :
从三方面来说:引用计数、垃圾回收机制、内存池机制。可以根据一下描述进行理解
一、对象的引用计数机制
Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数。
引用计数增加的情况:
1、一个对象分配一个新名称
2、将其放入一个容器中(如列表、元组或字典)
引用计数减少的情况:
1、使用del语句对对象别名显示的销毁
2、引用超出作用域或被重新赋值
sys.getrefcount( )函数可以获得对象的当前引用计数
多数情况下,引用计数比你猜测得要大得多。对于不可变数据(如数字和字符串),解释器会在程序的不同部分共享内存,以便节约内存。
二、垃圾回收
1、当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。
2、当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含一个对其他对象的应用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。
三、内存池机制
1、Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。
2、Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。
3、Python中所有小于256个字节的对象都是用pymalloc实现的分配器,而大的对象则使用系统的malloc,另外Python对象比如整数浮点数和list都有独立的私有内存池,对象间不共享他们的内存池,也就是说你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。