我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Python的内存管理举例分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Python的内存管理举例分析

这篇文章主要介绍“Python的内存管理举例分析”,在日常操作中,相信很多人在Python的内存管理举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python的内存管理举例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

语言的内存管理是语言设计的一个重要方面。它是决定语言性能的重要因素。无论是C语言的手工管理,还是Java的垃圾回收,都成为语言最重要的特征。这里以Python语言为例子,说明一门动态类型的、面向对象的语言的内存管理方式。

对象的内存使用

赋值语句是语言最常见的功能了。但即使是最简单的赋值语句,也可以很有内涵。Python的赋值语句就很值得研究。

a = 1

整数1为一个对象。而a是一个引用。利用赋值语句,引用a指向对象1。Python是动态类型的语言(参考动态类型),对象与引用分离。Python像使用“筷子”那样,通过引用来接触和翻动真正的食物——对象。

 引用和对象 

为了探索对象在内存的存储,我们可以求助于Python的内置函数id()。它用于返回对象的身份(identity)。其实,这里所谓的身份,就是该对象的内存地址。

 
a = 1  print(id(a)) print(hex(id(a)))

在我的计算机上,它们返回的是:

11246696 '0xab9c68'

分别为内存地址的十进制和十六进制表示。 

在Python中,整数和短小的字符,Python都会缓存这些对象,以便重复使用。当我们创建多个等于1的引用时,实际上是让所有这些引用指向同一个对象。

 
a = 1 b = 1  print(id(a)) print(id(b))

上面程序返回

11246696  11246696

可见a和b实际上是指向同一个对象的两个引用。

为了检验两个引用指向同一个对象,我们可以用is关键字。is用于判断两个引用所指的对象是否相同。

# Truea = 1 b = 1 print(a is b) # True a = "good" b = "good" print(a is b) # False a = "very good morning" b = "very good morning" print(a is b) # False a = [] b = [] print(a is b)

上面的注释为相应的运行结果。可以看到,由于Python缓存了整数和短字符串,因此每个对象只存有一份。比如,所有整数1的引用都指向同一对象。即使使用赋值语句,也只是创造了新的引用,而不是对象本身。长的字符串和其它对象可以有多个相同的对象,可以使用赋值语句创建出新的对象。 

在Python中,每个对象都有存有指向该对象的引用总数,即引用计数(reference count)。

我们可以使用sys包中的getrefcount(),来查看某个对象的引用计数。需要注意的是,当使用某个引用作为参数,传递给getrefcount()时,参数实际上创建了一个临时的引用。因此,getrefcount()所得到的结果,会比期望的多1。

from sys import getrefcount a = [1, 2, 3] print(getrefcount(a)) b = a print(getrefcount(b))

由于上述原因,两个getrefcount将返回2和3,而不是期望的1和2。 

对象引用对象

Python的一个容器对象(container),比如表、词典等,可以包含多个对象。实际上,容器对象中包含的并不是元素对象本身,是指向各个元素对象的引用。

我们也可以自定义一个对象,并引用其它对象:

class from_obj(object):     def __init__(self, to_obj):         self.to_obj = to_obj b = [1,2,3] a = from_obj(b) print(id(a.to_obj)) print(id(b))

可以看到,a引用了对象b。 

对象引用对象,是Python最基本的构成方式。即使是a = 1这一赋值方式,实际上是让词典的一个键值"a"的元素引用整数对象1。该词典对象用于记录所有的全局引用。该词典引用了整数对象1。我们可以通过内置函数globals()来查看该词典。 

当一个对象A被另一个对象B引用时,A的引用计数将增加1。

from sys import getrefcount a = [1, 2, 3] print(getrefcount(a)) b = [a, a] print(getrefcount(a))

由于对象b引用了两次a,a的引用计数增加了2。 

容器对象的引用可能构成很复杂的拓扑结构。我们可以用objgraph包来绘制其引用关系,比如

x = [1, 2, 3] y = [x, dict(key1=x)] z = [y, (x, y)] import objgraph objgraph.show_refs([z], filename='ref_topo.png')

Python的内存管理举例分析 

objgraph是Python的一个第三方包。安装之前需要安装xdot。

sudo apt-get install xdot sudo pip install objgraph

objgraph官网 

两个对象可能相互引用,从而构成所谓的引用环(reference cycle)。

 
a = [] b = [a] a.append(b)

即使是一个对象,只需要自己引用自己,也能构成引用环。

 
a = [] a.append(a) print(getrefcount(a))

引用环会给垃圾回收机制带来很大的麻烦,我将在后面详细叙述这一点。 

引用减少

某个对象的引用计数可能减少。比如,可以使用del关键字删除某个引用:

from sys import getrefcount a = [1, 2, 3] b = a print(getrefcount(b)) del a print(getrefcount(b))

del也可以用于删除容器元素中的元素,比如:

 
a = [1,2,3] del a[0] print(a)

如果某个引用指向对象A,当这个引用被重新定向到某个其他对象B时,对象A的引用计数减少:

from sys import getrefcount a = [1, 2, 3] b = a print(getrefcount(b)) a = 1 print(getrefcount(b))

垃圾回收

吃太多,总会变胖,Python也是这样。当Python中的对象越来越多,它们将占据越来越大的内存。不过你不用太担心Python的体形,它会乖巧的在适当的时候“减肥”,启动垃圾回收(garbage collection),将没用的对象清除。在许多语言中都有垃圾回收机制,比如Java和Ruby。尽管最终目的都是塑造苗条的提醒,但不同语言的减肥方案有很大的差异 (这一点可以对比本文和Java内存管理与垃圾回收

)。 

从基本原理上,当Python的某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾了。比如某个新建对象,它被分配给某个引用,对象的引用计数变为1。如果引用被删除,对象的引用计数为0,那么该对象就可以被垃圾回收。比如下面的表:

 
a = [1, 2, 3] del a

del a后,已经没有任何引用指向之前建立的[1, 2, 3]这个表。用户不可能通过任何方式接触或者动用这个对象。这个对象如果继续待在内存里,就成了不健康的脂肪。当垃圾回收启动时,Python扫描到这个引用计数为0的对象,就将它所占据的内存清空。 

然而,减肥是个昂贵而费力的事情。垃圾回收时,Python不能进行其它的任务。频繁的垃圾回收将大大降低Python的工作效率。如果内存中的对象不多,就没有必要总启动垃圾回收。所以,Python只会在特定条件下,自动启动垃圾回收。当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。

我们可以通过gc模块的get_threshold()方法,查看该阈值:

 
import gc print(gc.get_threshold())

返回(700, 10, 10),后面的两个10是与分代回收相关的阈值,后面可以看到。700即是垃圾回收启动的阈值。可以通过gc中的set_threshold()方法重新设置。 

我们也可以手动启动垃圾回收,即使用gc.collect()。 

分代回收

Python同时采用了分代(generation)回收的策略。这一策略的基本假设是,存活时间越久的对象,越不可能在后面的程序中变成垃圾。我们的程序往往会产生大量的对象,许多对象很快产生和消失,但也有一些对象长期被使用。出于信任和效率,对于这样一些“长寿”对象,我们相信它们的用处,所以减少在垃圾回收中扫描它们的频率。

小家伙要多检查 

Python将所有的对象分为0,1,2三代。所有的新建对象都是0代对象。当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象。垃圾回收启动时,一定会扫描所有的0代对象。如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描。

这两个次数即上面get_threshold()返回的(700, 10, 10)返回的两个10。也就是说,每10次0代垃圾回收,会配合1次1代的垃圾回收;而每10次1代的垃圾回收,才会有1次的2代垃圾回收。

同样可以用set_threshold()来调整,比如对2代对象进行更频繁的扫描。

 
import gc gc.set_threshold(700, 10, 5)

孤立的引用环

引用环的存在会给上面的垃圾回收机制带来很大的困难。这些引用环可能构成无法使用,但引用计数不为0的一些对象。

a = []  b = [a]  a.append(b)  del a del b

上面我们先创建了两个表对象,并引用对方,构成一个引用环。删除了a,b引用之后,这两个对象不可能再从程序中调用,就没有什么用处了。但是由于引用环的存在,这两个对象的引用计数都没有降到0,不会被垃圾回收。

Python的内存管理举例分析

孤立的引用环

为了回收这样的引用环,Python复制每个对象的引用计数,可以记为gc_ref。假设,每个对象i,该计数为gc_ref_i。Python会遍历所有的对象i。对于每个对象i引用的对象j,将相应的gc_ref_j减1。
Python的内存管理举例分析

遍历后的结果

在结束遍历后,gc_ref不为0的对象,和这些对象引用的对象,以及继续更下游引用的对象,需要被保留。而其它的对象则被垃圾回收。 

到此,关于“Python的内存管理举例分析”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Python的内存管理举例分析

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

下载Word文档

猜你喜欢

Python的内存管理举例分析

这篇文章主要介绍“Python的内存管理举例分析”,在日常操作中,相信很多人在Python的内存管理举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python的内存管理举例分析”的疑惑有所帮助!接下来
2023-06-17

C#内存管理举例分析

本篇内容主要讲解“C#内存管理举例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C#内存管理举例分析”吧!C#内存管理C#内存管理提供了与java一样的自动内存管理功能,让程序员从繁重的内存
2023-06-17

C/C++内存管理的示例分析

这篇文章主要介绍了C/C++内存管理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。C/C++赋予程序员管理内存的自由,是C/C++语言特色,虽然这引入了复杂度和危险
2023-06-15

C++中内存管理的示例分析

这篇文章将为大家详细讲解有关C++中内存管理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。概述内存管理的原理庞大而复杂,然而这些都被操作系统进行了封装,并对外预留了API,这些api被c++调用
2023-06-25

Linux内存管理之内存回收的示例分析

这篇文章主要介绍了Linux内存管理之内存回收的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1.1 内存回收的目标不是所有的物理内存都可以参与回收的,比如要是把内核
2023-06-16

java内存管理与内存溢出异常的示例分析

这篇文章主要介绍了java内存管理与内存溢出异常的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。说到内存管理,笔者这里想先比较一下java与C、C++之间的区别:在C
2023-05-31

FreeRTOS动态内存分配管理示例分析

本篇内容主要讲解“FreeRTOS动态内存分配管理示例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“FreeRTOS动态内存分配管理示例分析”吧!动态内存管理FreeRTOS提供5种动态内存
2023-06-29

Python异常处理举例分析

这篇文章主要讲解了“Python异常处理举例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python异常处理举例分析”吧!异常处理在项目开发中,异常处理是不可或缺的。异常处理帮助人们d
2023-06-02

iOS内存管理引用计数示例分析

这篇文章主要为大家介绍了iOS内存管理引用计数示例分析详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-06

C++技术中的内存管理:内存泄漏的典型案例分析

c++++ 中常见的内存泄漏类型包括栈泄漏、堆泄漏和全局泄漏。本文通过一个实战案例分析了堆泄漏。该示例中,一个动态分配的指针在函数返回时丢失了作用域,但分配的内存未释放,导致内存泄漏。可以使用智能指针、手动内存释放或内存检测工具来防止内存泄
C++技术中的内存管理:内存泄漏的典型案例分析
2024-05-08

C语言中动态内存管理的示例分析

这篇文章主要介绍了C语言中动态内存管理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。什么是动态内存分配我们都知道在C语言中,定义变量的时候,系统就会为这个变量分配内
2023-06-25

C/C++中指针与内存管理的示例分析

这篇文章主要介绍了C/C++中指针与内存管理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。指针和内存管理始终是C/C++比较容易模糊的知识点,但在C/C++编程中又
2023-06-29

Java应用程序中内存泄漏及内存管理的示例分析

这篇文章主要介绍Java应用程序中内存泄漏及内存管理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!btw,一些静态代码扫描工具也能检测出不好的编程习惯带来潜在的内存泄露的风险。Java平台的一个突出的特性是
2023-06-02

C语言中动态内存管理实例分析

今天小编给大家分享一下C语言中动态内存管理实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.动态内存开辟的原因常见的
2023-07-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录