iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Python万字深入内存管理讲解
  • 918
分享到

Python万字深入内存管理讲解

2024-04-02 19:04:59 918人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录python内存管理一、对象池1.小整数池2.大整数池3.inter机制(短字符串池)二、垃圾回收2.1.引用计数2.1.1 引用计数增加2.1.2 引用计数减少2.2.标记清除

Python内存管理

一、对象池

1.小整数池

系统默认创建好的,等着你使用

概述:

整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。

Python 对小整数的定义是 [-5, 256] ,这些整数对象是提前建立好的,不会被垃圾回收。

在一个 Python 的程序中,无论这个整数处于LEGB(局部变量,闭包,全局,内建模块)中的哪个位置,所有位于这个范围内的整数使用的都是同一个对象。

# 交互式环境下:
>>> a = 100
>>> b = 100
>>> id(a)
140720433537792
>>> id(b)
140720433537792
>>> a is b
True
>>> 

我们可以看出a,b指向同一个内存地址。

2.大整数池

大整数池:默认创建出来,池内为空的,创建一个就会往池中存储一个

# python交互式环境
>>> a = 257
>>> b = 257
>>> id(a)
2085029722896
>>> id(b)
2085029722960
>>> a is b
False
>>> 

a , b 不是指向同一个内存地址。

python中对大于256的整数,会重新分配对象空间地址保存对象。

3.inter机制(短字符串池)

每个单词(字符串),不夹杂空格或者其他符号,默认开启intern机制,共享内存,靠引用计数决定是否销毁。

>>> s1 = 'hello'
>>> s2 = 'hello'
>>> id(s1)
2178093449264
>>> id(s2)
2178093449264
>>> s1 is s2
True
>>> 

字符串s1和s2中没有空格时,可以看出,这里的s1与s2指向同一个内存地址。

当我们在he和llo之间加一个空格

>>> s1 = 'he llo'
>>> s2 = 'he llo'
>>> id(s1)
2278732636592
>>> id(s2)
2278732636528
>>> s1 is s2
False
>>> 

这时的字符串s1和s2就没有指向同一个内存地址。

对于字符串来说,如果不包含空格的字符串,则不会重新分配对象空间,对于包含空格的字符串则会重新分配对象空间。

二、垃圾回收

概述:

python采用的是引用计数机制为主,隔代回收和标记清除机制为辅的策略

概述:
现在的高级语言如java,c\# 等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的方式。

自己管理 内存极其自由, 可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患。 

python里也同java一样采用了垃圾收集机制,不过不一样的是: 
python采用的是引用计数机制为主,隔代回收机制为辅的策略

2.1.引用计数

在Python中,每个对象都有指向该对象的引用总数---引用计数 

查看对象的引用计数:sys.getrefcount() 

注意:
    当使用某个引用作为参数,传递给getrefcount()时,参数实际上创建了一个临时的引用。
    因此, getrefcount()所得到的结果,会比期望的多1。

2.1.1 引用计数增加

a、对象被创建

b、另外变量也指向当前对象

c、作为容器对象的一个元素

d、作为参数提供给函数:test(x)

2.1.2 引用计数减少

a、变量被显式的销毁

b、对象的另外一个变量重新赋值

c、从容器中移除

d、函数被执行完毕

看代码:

# -*- coding: utf-8 -*-
import sys
class Test(object):
    def __init__(self):
        print('当前对象已经被创建,占用的内存地址为:%s' % hex(id(self)))
a = Test()
print('当前对象的引用计数为:%s' % sys.getrefcount(a))  # 2
b = a
print('当前对象的引用计数为:%s' % sys.getrefcount(a))  # 3
list1 = []
list1.append(a)
print('当前对象的引用计数为:%s' % sys.getrefcount(a))  # 4
del b
print('当前对象的引用计数为:%s' % sys.getrefcount(a))  # 3
list1.remove(a)
print('当前对象的引用计数为:%s' % sys.getrefcount(a))  # 2
del a
print('当前对象的引用计数为:%s' % sys.getrefcount(a))  # 报错
'''
Traceback (most recent call last):
  File "E:/Python Project/Python 高级编程/内存管理/垃圾收集.py", line 30, in <module>
    print('当前对象的引用计数为:%s' % sys.getrefcount(a))
NameError: name 'a' is not defined
'''

当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾。比如某个新建对象,被分配给某个引用,对象的引用计数变为1。如 为0,那么该对象就可以被垃圾回收。

2.2.标记清除

标记清除(Mark—Sweep)算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。

它分为两个阶段:

第一阶段是标记阶段,GC会把所有的活动对象打上标记

第二阶段是把那些没有标记的对象非活动对象进行回收。

对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。

>

在上图中,可以从程序变量直接访问块1,并且可以间接访问块2和3。程序无法访问块4和5。第一步将标记块1,并记住块2和3以供稍后处理。第二步将标记块2,第三步将标记块3,但不记得块2,因为它已被标记。扫描阶段将忽略块1,2和3,因为它们已被标记,但会回收块4和5。

标记清除算法作为Python的辅助垃圾收集技术,主要处理的是一些容器对象,比如list、dict、tuple等,因为对于字符串、数值对象是不可能造成循环引用问题。Python使用一个双向链表将这些容器对象组织起来。不过,这种简单粗暴的标记清除算法也有明显的缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。

2.3.分代回收

因为, 标记和清除的过程效率不高。清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。还有一个问题就是:什么时候扫描去检测循环引用?

为了解决上述的问题,python又引入了分代回收。分代回收解决了标记清楚时什么时候扫描的问题,并且将扫描的对象分成了3级,以及降低扫描的工作量,提高效率。

  • 0代: 0代中对象个数达到700个,扫描一次。
  • 1代: 0代扫描10次,则1代扫描1次。
  • 2代: 1代扫描10次,则2代扫描1次。

隔代回收是用来解决交叉引用(循环引用),并增加数据回收的效率. 原理:通过对象存在的时间不同,采用不同的算法来 回收垃圾.

形象的比喻, 三个链表,零代链表上的对象(新创建的对象都加入到零代链表),引用数都是一,每增加一个指针,引用加一,随后 python会检测列表中的互相引用的对象,根据规则减掉其引用计数.

GC算法对链表一的引用减一,引用为0的,清除,不为0的到链表二,链表二也执行GC算法,链表三一样. 存在时间越长的 数据,越是有用的数据

2.3.1 分代回收触发时机?(GC阈值)

随着你的程序运行,Python解释器保持对新创建的对象,以及因为引用计数为零而被释放掉的对象的追踪。

从理论上说,这两个值应该保持一致,因为程序新建的每个对象都应该最终被释放掉。当然,事实并非如此。因为循环 引用的原因,从而被分配对象的计数值与被释放对象的计数值之间的差异在逐渐增长。一旦这个差异累计超过某个阈 值,则Python的收集机制就启动了,并且触发上边所说到的零代算法,释放“浮动的垃圾”,并且将剩下的对象移动到 一代列表。

随着时间的推移,程序所使用的对象逐渐从零代列表移动到一代列表。而Python对于一代列表中对象的处理遵循同样的 方法,一旦被分配计数值 与被释放计数值累计到达一定阈值,Python会将剩下的活跃对象移动到二代列表。

通过这种方法,你的代码所长期使用 的对象,那些你的代码持续访问的活跃对象,会从零代链表转移到一代再转移到二代。

通过不同的阈值设置,Python可 以在不同的时间间隔处理这些对象。

Python处理零代最为频繁,其次是一代然后才是二代。

2.3.2 查看引用计数(gc模块的使用)

# 引入gc模块
import gc 
# 常用函数: 
gc.get_count() 
# 获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表
gc.get_threshold() 
# 获取gc模块中自动执行垃圾回收的频率 
gc.set_threshold(threshold0[,threshold1,threshold2]) 
# 设置自动执行垃圾回收的频率 
gc.disable() 
# python3默认开启gc机制,可以使用该方法手动关闭gc机制 
gc.collect() 
# 手动调用垃圾回收机制回收垃圾

内存管理是使用计算机必不可少的一部分,无论好坏,Python几乎会在后台处理一切内存管理的问题。Python抽象出许多使用计算机的严格细节,这让我们在更高层次进行开发,而不用担心所有字节的存储方式和位置。

# -*- coding: utf-8 -*-
import gc
import sys
import time
class Test(object):
    def __init__(self):
        print('当前对象已经被创建,占用的内存地址为:%s' % hex(id(self)))
    def __del__(self):
        print('当前对象马上被系统GC回收')
# gc.disable()  # 不启用GC,在python3中默认启用
while True:
    a = Test()
    b = Test()
    a.pro = b  # a 和 b之间相互引用
    b.pro = a
    del a
    del b
    print(gc.get_threshold())  # 打印隔代回收的阈值
    print(gc.get_count())  # 打印GC需要回收的对象
    time.sleep(0.2)  # 休眠0.2秒方便查看

终端输出:

三、怎么优化内存管理

1.手动垃圾回收

先调用del a ; 再调用gc.collect()即可手动启动GC

2.调高垃圾回收阈值

gc.set_threshold 设置垃圾回收阈值(收集频率)。

将 threshold 设为零会禁用回收。

3.避免循环引用

四、总结

python采用的是引用计数机制为主,标记-清除和分代回收(隔代回收)两种机制为辅的策略

到此这篇关于Python万字深入内存管理讲解的文章就介绍到这了,更多相关Python内存管理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Python万字深入内存管理讲解

本文链接: https://www.lsjlt.com/news/119526.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Python万字深入内存管理讲解
    目录Python内存管理一、对象池1.小整数池2.大整数池3.inter机制(短字符串池)二、垃圾回收2.1.引用计数2.1.1 引用计数增加2.1.2 引用计数减少2.2.标记清除...
    99+
    2024-04-02
  • C/C++深入讲解内存管理
    目录C/C++内存分布C语言中的动态内存管理C++的内存管理operator new与operator delete函数operator new与operator dele...
    99+
    2024-04-02
  • C#内存管理CLR深入讲解(上篇)
    半年之前,PM让我在部门内部进行一次关于“内存泄露”的专题分享,我为此准备了一份PPT。今天无意中将其翻出来,觉得里面提到的关于CLR下关于内存管理部分的内存...
    99+
    2024-04-02
  • C#内存管理CLR深入讲解(下篇)
    《上篇》中我们主要讨论的是程序集(Assembly)和应用程序域(AppDomain)的话题,着重介绍了两个不同的程序集加载方式——独占方式和共享方式(中立域...
    99+
    2024-04-02
  • C语言深入细致讲解动态内存管理
    目录为什么存在动态内存管理动态内存函数的介绍mallocfreecallocrealloc常见的动态内存错误对NULL指针的解引用操作对动态开辟空间的越界访问对非动态开辟内存使用fr...
    99+
    2024-04-02
  • 深入理解JVM自动内存管理
    目录一、前言1.1 计算机==>操作系统==>JVM1.1.1 虚拟与实体(对上图的结构层次分析)1.1.2 Java程序执行(对上图的箭头流程分析)二、JVM内存空间与...
    99+
    2024-04-02
  • Python上下文管理器深入讲解
    目录引子概念上下文管理协议(Context Management Protocol)上下文管理器(Context Manager)引子 上下文管理器是一种简化代码的有力方式,其内部也...
    99+
    2022-12-21
    Python上下文管理器 Python上下文
  • Java内存模型的深入讲解
    目录内存模型硬件架构Java内存模型与硬件关联对象的可见性竞争条件总结Java内存模型展示了Java虚拟机是如何与计算机内存交互的,解决多线程读写共享内存时资源访问的问题。 内存模型...
    99+
    2024-04-02
  • 深入理解JavaScript内存管理和GC算法
    目录前言内存的生命周期JavaScript中的内存分配在JavaScript中使用内存释放内存JavaScript中的垃圾回收GC算法引用计数算法标记清除算法标记整理算法V8中的内存...
    99+
    2024-04-02
  • CPython 与内存管理:深入理解 Python 对象生命周期
    ...
    99+
    2024-04-02
  • Python超详细讲解内存管理机制
    目录什么是内存管理机制一、引用计数机制二、数据池和缓存什么是内存管理机制 python中创建的对象的时候,首先会去申请内存地址,然后对对象进行初始化,所有对象都会维护在一 个叫做re...
    99+
    2024-04-02
  • 深入探讨JavaScript中的内存管理
    内存管理是编程语言的基本能力,JavaScript 中的内存管理是通过 V8 完成的。V8 的实现遵循 ECMA-262 规范,而规范中没有阐述内存布局以及内存管理相关信息,所以它的原理取决于解释器的实现。唯一肯定的是不管任何编程语言,内存...
    99+
    2023-05-14
    前端 JavaScript
  • 深入探讨Go语言内存管理
    从现在开始,努力学习吧!本文《深入探讨Go语言内存管理》主要讲解了等等相关知识点,我会在编程网中持续更新相关的系列文章,欢迎大家关注并积极留言建议。下面就先一起来看一下本篇正文内容吧,希望能帮到你!...
    99+
    2024-04-05
  • 深入了解C语言的动态内存管理
    目录一、为什么会存在动态内存二、动态内存函数1.malloc和free2.calloc3.realloc三、动态内存函数常见错误2.对NULL指针进行解引用操作3.使用free释放一...
    99+
    2024-04-02
  • C语言深入讲解内存操作问题
    目录一、野指针二、野指针的由来三、基本原则四、小结-上 五、常见的内存错误六、内存操作的规则七、小结-下 一、野指针 指针变量中的值是非法的内存地址,进而形成野指...
    99+
    2024-04-02
  • C语言的动态内存管理的深入了解
    目录一、动态内存分配二、动态内存分配函数1、malloc()2、realloc()3、calloc()三、用free函数释放内存四、迷途指针总结一、动态内存分配 (1)用malloc...
    99+
    2024-04-02
  • C++深入浅出讲解内存四区与new关键字的使用
    目录写在前面内存四区程序运行前代码区全局区程序运行后栈区堆区new关键字new的基本语法利用new开辟数组写在前面 从本文开始我就要日常更新C++入门博文啦,从核心编程开始,之前的一...
    99+
    2024-04-02
  • 深入讲解Socket原理
    目录关于TCP/IP、UDP、Socket什么是TCP/IP、UDP?Socket在哪里呢?Socket是什么呢?你会使用它们吗?1、网络中进程之间如何通信?2、什么是Socket?...
    99+
    2024-04-02
  • C语言动态内存管理深入探讨
    目录1.动态内存开辟的原因2.动态内存函数的介绍2.1malloc和free2.2calloc2.3realloc3.常见的动态内存错误3.1对NULL指针的解引用操作3.2对动态开...
    99+
    2024-04-02
  • 深入了解Go语言内存管理的底层机制
    Go语言的内存管理是基于垃圾回收的机制,它使用了一个称为Go垃圾回收器的组件来自动管理内存的分配和释放。Go垃圾回收器使用了一个基于...
    99+
    2023-10-08
    Golang
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作