python可变类型和不可变类型,内存管理
1. python中创建可变对象和不可变对象时的内存分配
首先看两个简单创建可变对象和不可变对象的例子:
a = 1, b = 1
a和b的id是否相等?
a = 1000, b = 1000
a和b的id是否相等?
a = ‘a’, b = ‘a’
a和b的id是否相等?
a = (1,) b=(1, )
a和b的id是否相等?
a=[1], b=[1]
a和b的id是否相等?
在notebook中测试如下:
从结果中可以看到:
- 对于小整数其在解释器加载的时候就已经为其分配了内存,其内存地址都是相等的,但对于超过该范围的就要创建对象的时候申请内存空间,其id是不一样的;
- 对于小字符串,其创建的对象不会申请两个内存地址,对于复杂一点的字符串,在创建的时候会重写申请内存地址
- 对于元组和列表对象,不管位数如何其内存地址都是不一样的,其在创建对象的时候都会新申请一个内存地址
总结: 在python中不管是创建两个相同的可变对象和不可变对象,其都要向解释器申请内存空间,其id是不一样的;但是python对于一些整数类型(-5~256)和小字符串类型的对象做了优化,其在解释器加载的时候就已经分配了内存,在创建对象的时候只要为该对象增加一个引用即可,所以id是一样的;而对于变量赋值,只是给对象增加了一个引用,其内存地址肯定是一样的.
再看一个下面的例子:
nums = [12, 13, 15, 18, 20, "hello", ["world", "python"], (1, 2)]
a = 20
# nums[4] 和a是否同一个内存地址?
print(a is nums[4])
b = "hello"
# nums[5] 和b是否同一个内存地址?
print(b is nums[5])
c = ["world", "python"]
# nums[6] 是否和c同一个内存地址?
print(c is nums[6])
print(nums[6][0] is c[0])
# 两个列表中的world字符串是否相同?
d = (1, 2)
# nums[7] 和d是否同一个内存地址?
print(d is nums[7])
结果如下:
True
True
False
True
False
可以看到对于小的字符串和整数其都指向同一个内存地址,而对于列表和元组则新分配了一个内存地址
2.程序执行
一个软件程序要想执行,必须要先把硬盘上所需的数据加载到内存中,cup再去内存中取程序执行的数据进行运算和处理,如下图:
程序运行时内存分配:
软件的程序在计算机中的执行,主要是通过数据单元、控制单元、执行单元共同协作,完成数据的交互达到程序处理数据的目的,在软件的执行过程中,由于系统内存和CPU的资源有限,所以有效的分解软件中的各项数据,将不同的数据加载到不同的内存部分以有效的运行程序,同时可以达到在一个计算机中有效运行更多软件的目的;内存中主要包括以下几个内存区:
- 栈内存区: 该区主要存放变量对象及其引用,存取速度较快,适用于小数据的快速存储
- 方法区:主要用于存放加载程序需要的代码数据,二进制数据,方法和函数数据等
- 静态资源(常量区):主要存储静态数据,常量数据;对于python中的不可变对象一般也会存储在该内存区.
- 堆内存区:该区主要存放对象数据和类型,存储数据稳定,一般存储较为重要的数据;对于python中的可变对象一般会存储到该区
如下图:
3.python 中的可变和不可变对象
可变对象:当可变对象中的元素发生改变如重新赋值,该对象的内存地址不会发生改变;如列表,字典,集合
不可变对象:当可变对象中的元素发生改变,该对象的内存地址也会改变,即重新创建了一个内存空间来存放新数据;如数值类型,字符串,元组.
4.python 中的内存申请
python内存池: 内存池的概念就是预先在内存中申请一定数量的,大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够就再申请新的内存,这样做可以显著的减少内存碎片,提升效率
python中的内存管理机制–pymalloc: python内存管理机制都有两套实现方法,第一是针对小对象,小于256bits时,pymalloc会直接在内存池中申请内存空间;当大于256bits,则会直接执行new/malloc的行为申请内存空间;这样可以防止频繁调用系统的new/malloc来创建和销毁小对象.
python中的内存释放: 垃圾回收机制.