Python内存管理机制

赋值语句内存分析

使用id()方法可以查询变量内存地址。小数值变量会采用相同的地址保存,大数值变量会采用不同的地址保存,如下图。
在这里插入图片描述
以下例子有助于理解内存管理:

def extend_list(val, l=[]):
    print('^^^^^^^^^^^^^^^^^^^^^^^^^^^^^')
    print(l, id(l))
    l.append(val)
    print(l, id(l))
    return l


list1 = extend_list(10)
list2 = extend_list(123, [])
list3 = extend_list('a')

print("+++++++++++++++++++++++++++")
print(list1)
print(list2)
print(list3)

输出为:

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[] 4828328
[10] 4828328
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[] 6660680
[123] 6660680
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[10] 4828328
[10, ‘a’] 4828328
+++++++++++++++++++++++++++
[10, ‘a’]
[123]
[10, ‘a’]

垃圾回收机制

在这里插入图片描述

example 1

import time


class Cat(object):

    def __init__(self):
        print('对象产生了: {0}'.format(id(self)))

    def __del__(self):
        print('对象删除了: {0}'.format(id(self)))


def f0():
    """ 自动回收内存 """
    while True:
        c1 = Cat()
        time.sleep(0.5)


def f1():
    """ 一直在被引用,不会被回收 """
    l = []
    while True:
        c1 = Cat()
        l.append(c1)
        print(len(l))


if __name__ == '__main__':
    f0()

输出为:
对象产生了: 5502624
对象产生了: 7642104
对象删除了: 5502624
对象产生了: 5502624
对象删除了: 7642104
对象产生了: 7642104
对象删除了: 5502624
对象产生了: 5502624
对象删除了: 7642104
对象产生了: 7642104
对象删除了: 5502624

上述程序内存占用如下:
内存消耗比较缓和,没有不断增大。由于类Cat的对象创建后没有被引用,垃圾回收机制及时回收没有引用的对象,故程序运行内存占用并没有变大。
在这里插入图片描述

example 2

import time


class Cat(object):

    def __init__(self):
        print('对象产生了: {0}'.format(id(self)))

    def __del__(self):
        print('对象删除了: {0}'.format(id(self)))


def f0():
    """ 自动回收内存 """
    while True:
        c1 = Cat()
        time.sleep(0.5)


def f1():
    """ 一直在被引用,不会被回收 """
    l = []
    while True:
        c1 = Cat()
        l.append(c1)
        print(len(l))
        time.sleep(0.5)


if __name__ == '__main__':
    f1()

输出为:
对象产生了: 6420128
1
对象产生了: 35167152
2
对象产生了: 35167008
3
对象产生了: 35166864
4
对象产生了: 35333576
5
对象产生了: 35333120
6
对象产生了: 35332880
7
对象产生了: 35332544
8
对象产生了: 35334056
9
对象产生了: 35335280
10
对象产生了: 35335232
11

上述程序内存占用如下:
内存占用不断变大,导致程序一直。这是由于所建对象一直被列表l引用,故垃圾回收机制无法回收对象,导致内存占用不断变大。终止程序后列表引用的对象才逐个被删除并释放内存占用。
在这里插入图片描述

垃圾内存回收机制

在这里插入图片描述
getrefcount函数可以查询某个对象的引用计数,且可能会造成引用次数加一。
del关键字是删除对象的某个引用,而不是删除对象。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

example 3

import gc, sys
import objgraph

print(gc.get_threshold())

class Persion(object):
    pass

class Cat(object):
    pass

p = Persion()  # 创建Person类对象,并用变量p引用
c = Cat()  # 创建Cat类对象,并用变量c引用
p.name = 'Susan'
p.pet = c

c.master = p
print(sys.getrefcount(p))
print(sys.getrefcount(c))

del p  # 删除p对Person类对象的引用
del c  # 删除c对Cat类对象的引用

# 手动执行垃圾回收
gc.collect() # 回收p和c引用的Persion类对象和Cat类对象
print(objgraph.count('Persion'))
print(objgraph.count('Cat'))

输出为:
(700, 10, 10)
3
3
0
0

内存管理机制

在这里插入图片描述
在这里插入图片描述

python内存管理机制的理解

一、对象的引用计数机制
Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数。
引用计数增加的情况:
1、一个对象分配一个新名称
2、将其放入一个容器中(如列表、元组或字典)
引用计数减少的情况:
1、使用del语句对对象别名显示的销毁
2、引用超出作用域或被重新赋值
sys.getrefcount( )函数可以获得对象的当前引用计数
多数情况下,引用计数比你猜测得要大得多。对于不可变数据(如数字和字符串),解释器会在程序的不同部分共享内存,以便节约内存。
二、垃圾回收
1、当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。
2、当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含一个对其他对象的应用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。
三、内存池机制
1、Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。

2、Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。
3、Python中所有小于256个字节的对象都是用pymalloc实现的分配器,而大的对象则使用系统的malloc,另外Python对象比如整数浮点数和list都有独立的私有内存池,对象间不共享他们的内存池,也就是说你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。

版权声明:本文为u012777971原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u012777971/article/details/105008468