python变量的内存机制
python变量的内存机制
作为一门简单易用的语言,且配备海量的库,python可谓是程序员手中的掌中宝,编程本身就是一种将人类思维转化为计算机思维的技术,如果不需要去追求极致的运行效率同时又不限制于计算机内存空间,python无疑是目前最方便的语言了。
作为一个合格的程序员,自然是要知其然并知其所以然,除了能够应用python来放飞自我之外,同时也要探究python其内部的运行原理,首当其冲的python编程中必须要用到的变量以及背后的运行机制。
注:以下示例在linux平台下编写,使用python2.7
引用机制
python的变量-内存模型更像是C++中的引用机制,python中的每个变量不一定占用内存空间,变量更像是一份内存的引用,通过这个变量可以访问到内存中的数据,举个例子:
>>>a=10
>>>b=a
>>>c=[1,2,3,4]
>>>d=c
>>>print "%x%x" %(id(a),id(b))
>>>print "%x%x" %(id(c),id(d))
输出结果:
b51080.b51080
7f28bf69b758.7f28bf69b758
其中id()是python的系统函数,返回对象的内存起始地址。
从结果可以看出,a与b,c与d变量对应的地址事实上为同一个地址,也就是当我们使用变量a和b时,使用的是同一个对象,而a,b是这个对象的引用,我们可以通过系统函数sys.getrefcount()来查看一个对象的引用数量:
>>>import sys
>>>a=257
>>>print sys.getrefcount(a)
>>>b=a
>>>print sys.getrefcount(a)
输出结果:
2
3
显然,这个结果并不在我们的预料当中,由于a和b在同一个地址,结果应该是1、2,为什么是2,3呢?
这是因为在sys.getrefcount()函数调用时,a作为参数也被引用了一次,所以出现了2、3的结果。
缓存小数据机制
上面讲了python变量赋值时的内存机制,事情就这么完美结束了吗?
并没有!!!
我们再来看一个例子:
>>> a=10
>>> b=10
>>> print "%x.%x" %(id(a),id(b))
输出结果:
b51080.b51080
看到这个结果,我缓缓摘下我的眼镜,拿95%浓度的医用酒精仔仔细细擦了三遍之后再戴上看,没看错!这两个变量还是同一个地址内容的引用,这一次两个变量的初始化是独立的,并非赋值初始化,为什么两个变量还是同一个地址的引用呢?
答案是:
在Python中,Python会有一个缓存对象的机制,以便重复使用。当我们创建多个等于1的引用时,实际上是让所有这些引用指向同一个对象,以达到节省资源的目的
原来是这样!!!
但是仔细一想,这不对吧?如果每个数据都进行缓存,那岂不是对内存空间的极度浪费?还是说内存回收机制会过一段时间回收一次垃圾内存?
我们再来看下面一个例子:
>>> a=100
>>> b=100
>>> print "%d%d" %(id(a),id(b))
>>> a=256
>>> b=256
>>> print "%d%d" %(id(a),id(b))
>>> a=257
>>> b=257
>>> print "%d%d" %(id(a),id(b))
输出结果:
5223836.5223836
5225932.5225932
5241840.5241864
从结果来看,当a小于256时,这个值会被系统缓存循环利用,而当a>256时,系统并不会进行缓存(当然不仅仅是三次实验的结果,博主后续还试了很多值,就不一一列出了)
我们来用另一种方法来验证这个问题,即sys.getrefcount():
>>>import sys
>>>a=10
>>>print sys.getrefcount(a)
>>>a=257
>>>print sys.getrefcount(a)
输出结果为:
15
2
结果显而易见,10这个值被系统缓存,且在别处引用了多次,而257这个值为2(为什么为2而不是1在上面有解释)
那么问题又来了,如果是其他类型的数据呢?我们接着看
>>>a="downey"
>>>b="downey"
>>>print "%d%d" %(id(a),id(b))
结果为:
39422528.39422528
短字符串也会有缓存机制
然后是list:
>>>a=[1,2,3]
>>>b=[1,2,3]
>>>print "%d%d" %(id(a),id(b))
39704576.39745176
list并没有缓存机制,从这里可以看出,python的缓存机制并不针对所有变量类型
变量缓存结论
根据各种实验以及多方查证,结果表明:
- python的变量其实是一种堆内存的引用,可以理解为一个实体的标签,而在不同变量之间的拷贝复制(如a=b),他们所表示的对象实体是同一个
- python会对-5-256(包括256)的整型数据和短字符串进行缓存以节省多次分配销毁的开销
看到这里,喜欢思考的朋友们不禁就要问了,缓存这些整型数据和短字符串真的对性能有明显提升吗?python代码中能有多少个整型变量?
答案是:整型变量对应整型的内存对象,但是整型的内存对象并不仅仅对应整型的变量类型,容器中的整形元素可能也是整形变量的引用
如果你还有疑惑,我们来看看下面的例子:
>>> import sys
>>> a=1
>>> sys.getrefcount(a)
128
>>> b=[1,2,3]
>>> sys.getrefcount(a)
129
从打印的结果可以看出,整型变量a=1,表示a指向对象1,为1的引用,b[0]也被初始化为1,同样的,b[0]同时也是对象1的引用,对于所有容器而言,都是这种形式,看到这里,各位观众老爷们应该是有所理解了吧。
关于python变量内存机制对变量使用的影响可以参考这一篇博客:python函数调用时参数传递方式
内存回收
既然说到了内存机制,必然涉及到分配和回收的机制,内存分配就很简单,在定义对象的时候用到进行内存的分配,而内存的回收则没那么简单,因为在内存回收的过程中,python无法执行其他任务,所以频繁地内存回收会导致严重的效率问题,而内存回收间隔时间过长则会导致内存浪费严重,所以一般只有在特定时间内启动内存回收。
python运行时,会记录下来分配和释放的次数,只有当两个值的差大于某个数值时,即
分配次数-释放次数>触发回收的阈值
时,python进行垃圾回收,我们可以使用get_threshold()方法来获取阈值:
>>>import gc
>>>print gc.get_threshold()
输出结果:
(700,10,10)
这个700便是触发内存回收的阈值。但是后面的两个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代垃圾回收。
我们也可以手动地调整触发回收的阈值,聪明的朋友们可以猜到这个方法了,既然有get,必然相对应的就是set:
import gc
gc.set_threshold(600,8,7)
除了被动地等待系统回收,当然也可以手动地进行内存回收:
import gc
gc.collect()
其实java也好,python也好,每一种语言的内存机制将从根本上影响语言的执行效率,所以在内存的处理上会有很多更加复杂的细节,这里只是介绍了一个大体的框架,班门弄斧,欢迎路过的大神们指正和补充。
好了,关于python变量内存机制的问题就到此为止了,如果朋友们对于这个有什么疑问或者发现有文章中有什么错误,欢迎留言
个人邮箱:linux_downey@sina.com
原创博客,转载请注明出处!
祝各位早日实现项目丛中过,bug不沾身.
(完)
python变量的内存机制的更多相关文章
- Python变量与内存管理
Python变量与内存管理 –与C语言中的变量做对比,更好的理解Python的变量. 变量 变量在C语言中 全局变量:其存放在内存的静态变量区中. 局部变量:代码块中存放在内存的代码区当中,当被调 ...
- python变量的内存管理
python变量的内存管理 一.变量存在了哪里? 先让我们来看一段代码: height = 100 # 定义变量 # print(100) # print会自动帮你创建一个变量100,打印完之后,马上 ...
- python变量垃圾回收机制的入门使用
简介: Python是一款高层次的解释性语言:Python对于初学者来说(易于学习)Python有相对较少的关键字,结构简单,和一个明确定义的语法,学习起来更加简单.学习Python的目的就是为了能够 ...
- 详解Python变量在内存中的存储
这篇文章主要是对python中的数据进行认识,对于很多初学者来讲,其实数据的认识是最重要的,也是最容易出错的.本文结合数据与内存形态讲解python中的数据,内容包括: 引用与对象 可变数据类型与不可 ...
- python学习之内存机制
不可变对象(字符串.元组) 1. a = 1 首先在内存中创建对象1,并记录对象的引用计数为1次. id(a) 查看变量a引用的对象的内存地址 2. b = 1 内存中已存在对象1,变量b引用对象1, ...
- python中的内存机制
首先要明白对象和引用的概念 (例子:a=1, a为引用,1为对象,对象1的引用计数器为1,b=1此时内存中只有一个对象1,a,b都为引用,对象的引用计数器此时为2,因为有两个引用) a=1,b=1 i ...
- python内存机制与垃圾回收、调优手段
目录 一.python的内存机制 二.python的垃圾回收 1. 引用计数 1.1 原理: 1.2 优缺点: 1.4 两种情况: 2. 标记清除 2.1 原理: 2.2 优缺点: 3. 分代回收 3 ...
- Python变量的本质与intern机制
变量的存储 a = 'abc' 理解:①先在内存中生成一个字符串‘abc’ ②可以把比变量名a看做一个便利贴,然后将a贴到‘abc’中 ③注意顺序,是生成‘abc’,然后再创建a指向‘abc’ ...
- python 基础之变量存储缓存机制与数据驻留小数据池
一:变量存储的缓存机制 在同一文件(模块)里,变量存储的缓存机制 (仅对python3.x版本负责),那么 --对于Number (int bool float complex) 1.对于整型而言,- ...
随机推荐
- linux里安装使用svn
1.安装 sudo apt-get install subversion 2.checkout工程 svn checkout svn://192.168.0.3/测试工具 /home/testtool ...
- Kubernetes平台环境搭建
软件 版本 Linux操作系统 CentOS7.4 Kubernetes 1.12 Docker 18.xx-ce Etcd 3.x Flannel 0.10 角色 IP 组件 推荐配置 master ...
- Linux文件的基本操作函数
1.Linux文件的基本操作 Linux文件的基本操作主要包括了文件的创建.打开.读写和关闭等基本操作. 1.1.文件操作系统调用 (1)创建文件系统函数 int creat(const char * ...
- 【手写代码】计算1-n中总共有多少二进制1
#include<bits/stdc++.h> #include<vector> using namespace std; //时间复杂度:O(N) int f(int x) ...
- jmeter Dashboard Report
说明:详情参考:https://jmeter.apache.org/usermanual/generating-dashboard.html JMeter3.0以后引入了Dashboard Repor ...
- 图论 --- 三维空间bfs
<传送门> [题目大意] 给你一个三维的迷宫,让你计算从入口走到出口最少步数. [题目分析] 其实和二维迷宫还是一样的,还是用队列来做,由于BFS算法一般是不需要回溯的,所以我们就用不着还 ...
- sql语句修改数据库字段的长度
修改字段的长度 alter table [OtpimizeRoute_Test].[dbo].[T_QueueFile] alter column ListFileName nvarchar()
- 小白的C++之路——结构体
结构体与数组不同的是结构体能包含不同类型的数据,像一个综合性的仓库,更加灵活. #include <iostream> #include <string> #include & ...
- DRF框架(七) ——三大认证组件之频率组件、jwt认证
drf频率组件源码 1.APIView的dispatch方法的 self.initial(request,*args,**kwargs) 点进去 2.self.check_throttles(re ...
- mysql执行顺序与join连接
mysql加载顺序 手写顺序 SELECT DISTINCT <select list> FROM <left_table> join <join_type> JO ...