美文网首页
Python内存分析

Python内存分析

作者: 阿猫阿狗py | 来源:发表于2018-08-26 16:08 被阅读115次

    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方法进行处理
    `

    相关文章

      网友评论

          本文标题:Python内存分析

          本文链接:https://www.haomeiwen.com/subject/gvnmiftx.html