Python内存分析

1. 内存分析

1.1 程序运行方式

Python执行一个程序:程序就从解释器申请内存

Python解释器:预加载->demo.py->demo01.pyc文件->内存->运行demo01.py程序代码

1.2 内存分配

1.2.1栈内存

栈内存(stack): 读取、加载。数据速度快但不是很稳定,适合存放经常临时分配、经常回收的数据变量。

1.2.2 堆内存

堆内存:读取、加载数据较慢;比较消耗资源,但是一旦数据存在,数据操作比较稳定适合存放的数据:对象

1.2.3 数据区

方法区|数据区[data]:专门加载程序运行的代码字节数据,方法数据、函数数据等等

1.2.4 静态存储区

静态存储区|常量区[static]:专门存放程序中的公共数据、静态数据的内存区域。

image.png

1.3不可变数据类型|可便数据类型

1.3.1 不可变数据类型

一般数据类型都是不可变数据类型,不可变数据类型是在定义了数据之后,修改变量的数据,变量不会修改原来内存的地址的数据,而是会指向新的地址,原有的数据保留,这样方便程序中基本数据的利用率。

例如:整数类型:-5->256,在解释器加载时,已经自动分配了这些数字的内存,超出-5->256范围的整数,在一个代码块中申请一次内存。

交互模式:一行命令就是一个代码块

IDE模式-开发工具:一个模块就是一个代码块

b = 12
print(id(b))
b = 13
print(id(b))
输出:
1796173168
1796173200

1.3.3 可变数据类型

对象在内存地址中存储的数据可变

a = list() # 堆内存中:存在一个对象 list(),一个变量a指向这个对象
print(id(a)) # 查看对象a的内存
print(a)
a.append("hello")
print(id(a))
print(a)    
输出:
1357340489992
[]
1357340489992
['hello']

可变类型|不可变类型 思考题:


nums = [12, 13, 14,"python",["hello","world"]]
a = 12
print(id(a),id(nums[0]))
b = "python"
print(id(b),id(nums[3]))
c = ["hello","world"]
print(id(c),id(nums[4]))
输出:
1796173168 1796173168
1324994797896 1324994797896
1324995705736 1324994820616

1.3 代码和代码块

Python中的最小运行单元是代码块,代码块的最小单元时一行代码

思考:a = ‘hello’在内存中会创建几块内存空间 =======>2

image.png

p = Person(‘tom’,18)在内存中,会创建几块内存空间 ===========>4

image.png

1.4 程序内存代码检测

为了便于检测代码内存使用率,社区开发了一个模块memory_profile

通过 pip install memory_profiler

使用方法:通过测试的函数或者类型前面添加@profile注解,让内存分析模块可以直接进行代码进行检测

from memory_profiler import profile

class Person:
    '''自定义类型'''
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
        
@profile(precision=10)
def main():
    '''入口函数'''
    p = Person('tom',18,'男')
    print(p)
    p2 = Person('tom',18,'男')
    print(p)
if __name__ == "__main__":
    main

2 操作符号

2.1 is 和 == 和 isinstance 的使用

1. a is b :判断两个变量a/b,他们指向的对象是否时同一个对象

2. a == b :判断两个变量a/b,他们指向的对象的数据内容是否一致

3.isinstance(a,b)判断a是否属于b类型

3 引用|深拷贝|浅拷贝

3.1 引用

如果程序中多个不同的地方都要使用同一个对象,通过对象的引用赋值,将同一个对象赋值给多个变量。

应用赋值并不会产生新的对象,而是让多个变量可以共同指向一个对象,通过多个变量都可以操作同一个对象的数据。

class Person:
    def __init__(self,name,fav):
        self.name = name
        self.fav = ["篮球","足球"]
a = Person("tom",["羽毛球"])
b = c = a
print(id(a))
print(id(b))
print(id(c))
输出:
2207567775728
2207567775728
2207567775728

3.2 浅拷贝

复制一个对象,复制对象中的属性数据的引用

方法:导入模块 copy

X = copy.copy(a) #拷贝了a对象,产生了一个对象x

class Person:
    def __init__(self,name,fav):
        self.name = name
        self.fav = ["篮球","足球"]
# a = Person("tom",["羽毛球"])
# b = c = a
# print(id(a))
# print(id(b))
# print(id(c))
import copy
a = Person("tom","lol")
x = copy.copy(a)
x.fav.append('aa')
print(id(a)) #2519924135976
print(id(x)) #2519924136872
print(a.fav) #['篮球', '足球', 'aa']
print(x.fav) #['篮球', '足球', 'aa']

3.3 深拷贝

和对象的浅拷贝不同,对象的深拷贝,是对象数据的直接拷贝,而不是简单的引用拷贝,主要是通过python内建标准模块copy提供的deepcopy函数可以完成对象深拷贝。

4 垃圾回收机制

自动回收无效对象数据,通过垃圾回收算法进行操作

垃圾回收:Garbage Collection : GC

Python中,以引用计数垃圾回收算法为主要回收机制

以标记-清除 和 分代回收为辅助回收机制

4.1 引用计数

1.查询指定对象的引用数量

Import sys

s = sys.getrefcount(p) #查询p的引用数量为s

  1. python是一个面向对象的弱类型语言,所有的对象都是直接或者间接继承自object类型,object类型的核心其实就是一个结构体对象
typedef struct_object { 
    int ob_refcnt; 
    struct_typeobject *ob_type; 
} PyObject;

在这个结构体中,ob_refcnt就是对象的引用计数,当对象被创建或者拷贝时该计数器就会增加1,当对象的引用变量被删除时就会减少1,当引用计数为0时,对象数据就会被释放。

4.2 标记清除

标记清除思想: 首先找到python中的一批根节点对象,通过根节点对象可以找到他们指向的子节点对象,如果搜索过程中有这个指向是从上往下的指向,表示这个对象是可达的,否则该对象是不可达的,可达部分的对象在程序中需要保留下来,不可达部分的对象在程序中时不需要保留的。

4.3 分代回收

在python内部处理机制中,定义了三个不同的链表数据结构[第0代,第一代,第二代]。Python为了提高程序执行效率,将垃圾回收机制进行了阈值限定,0代回收最为密集,其次是1代,最后是2代

4.4 垃圾回收处理

Python中的gc模块提供了垃圾回收处理的各项功能机制,必须import gc才能使用

gc.set_debug(flags):设置gc的debug日志,一般为gc.DEBUG_LAKE

gc.collect([generation]):显示进行垃圾回收处理,可以输入参数~参数表示回收的对象代数,0表示只检查第0 代对象,1表示检查第0、1代对象,2表示检查0,1,2代对象,如果不传递参数,执行FULL COLLECT,也就是默认传递2

gc.set_threshold(threshold0[,threshold2[,threshold3]]):设置执行垃圾回收机制的频率

gc.get_count():获取程序对象引用的计数器

gc.get_threshold():获取程序自动执行GC的引用计数阈值

在程序开发过程中需要注意:

l 项目代码中尽量避免循环引用

l 引入gc模块,启用gc模块自动清理循环引用对象的机制

l 将需要长期使用的对象集中管理,减少GC资源消耗

l gc模块处理不了重写del方法导致的循环引用,如果一定要添加该方法,需要显式调用gc模块的garbage中对象的del方法进行处理
`

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容