python-内存管理


内存剖析

占用内存

	int 1			4字节
	float b=1.0		4字节
	char c='_'		1字节
	1024字节=1KB

堆和栈

C语言中,内存分配方法有2种:

  • 第一种:int a = 1; char s[10] = “hello”; 在栈区申请内存
  • 第二种:malloc()在堆区申请内存, free()释放内存

Python语言中:所有的对象都在堆区申请内存。

Python的内存管理器负责申请、释放内存。

引用计数

每个对象都存有指向该对象的引用总数,即引用计数(reference count)。
可以使用内置函数getrefcount()查看该对象的引用计数。
注:每使用一次getrefcount(),当前对象就又被引用一次,当前对象引用计数都会加一。

from sys import getrefcount

a = [1, 2, 3] # 这里引用计数加一
print(getrefcount(a)) # 这里引用计数再加一,输出2

垃圾回收

当Python的某个对象的引用计数为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为1。如果引用被删除,对象的引用计数为0,那么该对象就会被垃圾回收。

+1的情况

  • 赋值 、引用 x = obj obj = MyClass()
  • 被其他对象使用 objects = [] objects.append(obj)
  • 传给函数

-1的情况

  • 改变引用对象: x = obj x = 1
  • 删除 del x
  • 函数调用结束

由于垃圾回收会降低python的工作效率,所以Python只会在特定条件下,自动启动垃圾回收。当Python运行时,会记录其中分配对象和取消分配对象的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。

使用get_threshold()方法查看阈值,python默认是(700, 10, 10),可以使用set_threshold()方法重新设置。

import gc
print(gc.get_threshold()) # (700, 10, 10)

上面的(700, 10, 10)这几个值是什么意思呢,这就要说到分代回收了。

分代回收

Python将所有的对象分为0,1,2三代。所有的新建对象都是0代对象。当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象。垃圾回收启动时,一定会扫描所有的0代对象。如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描。

这两个次数即上面get_threshold()返回的(700, 10, 10)返回的两个10。也就是说,每10次0代垃圾回收,会配合1次1代的垃圾回收;而每10次1代的垃圾回收,才会有1次的2代垃圾回收。

有一个问题:b=[1,2],a=[1,b],b.append(a),像这样的循环引用会造成内存泄漏,删除变量后他们还会互相引用,这里就用到了标记-清除机制。

标记-清除

把所有对象找出来,先检测被全局变量引用的标黑,在被全局变量间接引用的标黑,依次类推,直到没有被全局变量引用会被查出,即使有计数(如循环引用)也会被删除

总结

python的垃圾回收是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。

内存池机制

小整数池

在python中,所有变量赋值整数-5到256之间的数据相同的值id都是相同的 ,-5到256以外即使相同的值id也不同

字符串驻留池:intern机制

解释器会专门分配一块专门放纯单词字符组成的字符串(数字、字母、下划线)
a=‘123_a’
b=‘123_a’
a和b的id是相同的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值