了解现代编程语言中的内存管理

哈Ha!我向您介绍Deepu K Sasidharan撰写的文章“使现代编程语言中的内存管理神秘化 ”的翻译。

在本系列文章中,我想消除关于软件(以下称为软件)中的内存管理的神秘面纱,并详细考虑现代编程语言提供的可能性。我希望我的文章能帮助读者了解这些语言的内幕并为自己学习一些新知识。

对内存管理概念的深入研究使您可以编写更高效的软件,因为编码的样式和实践对满足程序需求的内存分配原则有很大影响。

第1部分:内存管理简介


内存管理是一整套机制,可让您控制程序对计算机RAM的访问。这个主题在软件开发中非常重要,同时也给许多程序员带来了困难甚至是黑匣子。

RAM是用来做什么的?


当程序在计算机的操作系统上运行时,它需要访问随机存取存储器(RAM),以便:

  • 上传您自己的字节码以执行;
  • 存储过程中使用的变量和数据结构的值;
  • 加载程序需要完成任务的外部模块。

除了用于加载自己的字节码的空间外,程序在工作时还会使用RAM的两个区域- 堆栈(stack)和(heap)。


堆栈用于静态分配内存。它是按照“ 后进先出”(LIFO的原则组织的您可以将一堆书想象成一堆书-只能与最顶层的书进行交互:阅读或在上面放一本新书。

  • 由于提到的原理,堆栈使您可以快速地对数据执行操作-所有操作都通过“堆栈中的顶层书籍”执行。如果需要保存数据,这本书将添加到最顶部;如果需要读取数据,则从上方开始阅读。
  • , , , — ;
  • — . , , — , . , , , . , , , — , ;
  • ;
  • ; ;
  • ;
  • (stack overflow), . , ;
  • , ;



JavaScript. , .


堆用于动态分配内存,但是,与堆栈不同的是,必须首先使用“目录”找到堆中的数据。可以想象一个堆就是这么大的多层库,按照一定的说明,您可以在其中找到必要的书。

  • 在堆上执行的操作比在堆上执行的要慢一些,因为它们需要额外的步骤来搜索数据;
  • 堆存储动态大小的数据,例如,一个列表,您可以在其中添加任意数量的元素;
  • 所有应用程序线程共有的堆;
  • 由于其动态特性,堆在管理上是不平凡的,并且与内存相关的所有所有问题和错误都将随之产生。编程语言提供了解决这些问题的方法。
  • , — ( , ), , , ;
  • (out of memory), , ;
  • , , , .


?


与硬盘驱动器不同,RAM非常有限(当然,硬盘驱动器也不是无限的)。如果程序在不释放内存的情况下消耗了内存,那么最终,它将吸收所有可用的保留空间并尝试超出内存限制。然后它将完全落入它自己,或更严重的是,它将使操作系统崩溃。因此,非常不希望在软件开发中轻率地进行内存操作。

不同的方法


现代编程语言试图尽可能简化内存的工作,并减轻开发人员的部分负担。尽管一些古老的语言仍然需要手动控制,但大多数仍然提供了更优雅的自动方法。有时,一种语言使用多种方法来一次管理内存,有时,开发人员甚至可以选择哪种选项将更有效地完成其任务(一个很好的例子是C ++)。让我们继续简要介绍各种方法。

手动内存管理


该语言不提供用于自动内存管理的机制。为创建的对象分配和释放内存完全取决于开发人员的良心。这种语言的一个例子-C它提供了许多用于管理内存的方法(mallocrealloccallocfree)-开发人员必须使用它们来分配和释放程序中的内存。这种方法需要很高的准确性和谨慎性。对于初学者来说也特别困难。

垃圾收集器


垃圾收集是对堆进行自动内存管理的过程,该过程包括查找以前为程序需求而占用的未使用的内存部分。这是现代编程语言中最流行的内存管理选项之一。垃圾收集例程通常以预定的时间间隔开始,并且碰巧它的启动与资源消耗过程一致,从而导致应用程序延迟。JVMJava / Scala / Groovy / Kotlin),JavaScriptPythonC#GolangOCamlRuby是使用垃圾收集器的流行语言的示例。

  • 标记和清除垃圾收集器:这是一个分两个阶段运行的算法:首先,它标记内存中被引用的对象,然后将内存从未标记的对象中释放出来。例如,在JVM,C#,Ruby,JavaScript和Golang中使用此方法。JVM中有几种不同的垃圾收集算法可供选择,诸如V8之类的JavaScript引擎除了使用链接计数之外还使用了标记算法。这样的垃圾收集器可以在C和C ++中作为外部库进行连接。

    可视化的标记算法:标记通过链接链接的对象,然后删除不可达的对象
  • : — , . . , , , , PHP, Perl Python. C++;


(RAII)


RAII是OOP中的一种软件习惯用法,其含义是为对象分配的存储区域严格与其寿命相关。内存在构造函数中分配,并在析构函数中释放。这种方法最初是在C ++中实现的,并且还用于AdaRust

自动链接计数(ARC)


这种方法与带有引用计数的垃圾收集非常相似,但是,不是在特定的时间间隔开始计数过程,而是在编译阶段将用于分配和释放内存的指令直接插入到字节码中。当参考计数器达到零时,作为正常程序流的一部分释放内存。

自动引用计数仍然不允许处理循环链接,并且要求开发人员使用特殊关键字来进一步处理此类情况。ARCClang转换器的功能之一,因此存在于Objective-CSwift语言中。另外,自动引用计数可用于Rust。以及具有智能指针的新C ++标准

拥有


当内存中的每个值应仅具有一个所有者变量时, 这是RAII与所有权概念的结合当所有者离开执行区时,立即释放内存。可以说,这近似于在编译阶段对链接进行计数。这种方法在Rust中使用,同时我找不到其他使用类似机制的语言。




本文研究了内存管理领域的基本概念。每种编程语言都使用针对各种任务优化的这些方法和算法的自己的实现。在以下部分中,我们将仔细研究流行语言中的内存管理解决方案。

另请阅读本系列的其他部分:



参考文献





如果您喜欢这篇文章,请加上加号或发表评论。

您可以在TwitterLinkedIn上订阅该文章的作者

插图:
使用pythontutor完成的堆栈可视化
所有权概念图:Link Clark,位于Creative Commons Attribution-Alike License v3.0的Rust团队

对于翻译的证明,特别感谢Alexander MaksimovskyKaterina Shibakova

All Articles