余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

译|为什么要理解内存管理(内存的翻译)

xiyangw 2022-12-04 10:07 14 浏览 0 评论

这是一篇译文,原文地址:https://www.welcometothejungle.com/en/articles/btc-memory-handling

内存,以及编程语言如何管理内存,是一个让开发者们头疼不已的问题。我们所写的程序时刻不停地分配着内存,但我们却很难搞清楚,这一切到底是怎么发生的。


什么是内存

存储空间,正如它一开始所定义的,是我们存储特定信息,以备之后使用的地方,这种存储可能是永久的(直到我们手动删除),也可能是临时的(直到电脑自动删除)。实际上,我们和电脑之间的每一次交互,都涉及信息的存储。比如说,打开一个浏览器时,它的执行步骤就从永久存储(硬盘)加载到临时存储(内存RAM)中。

主存储,或者说 RAM,是电脑使用的内部存储空间,有别于 USB 、硬盘之类的外部存储设备。电脑可以与内存直接交互,所有程序也必须加载到内存中才能执行。有时,整个程序都会被加载到内存中,也有时,只有程序的一部分(一个进程)被加载到内存中——这个机制被叫做动态加载。如果这部分程序依赖于另一个程序,那么,还会有一个动态链接机制建立起这个程序与主程序之间的关系。

内存管理影响到电脑中的每一个程序,极为关键,因此,现代操作系统都有一套复杂的机制来完成这项工作。通过各个层次(硬件层、操作系统层、应用软件层)的协调与控制,确保内存使用合理高效。

本文聚焦于操作系统与应用软件中内存管理。在系统层,内存管理主要涉及特定存储块(可以被理解为地址与空间)的分配;在应用层,内存管理主要涉及向系统发送内存空间请求,以及确保程序定义的对象与数据结构有足够的存储空间(内存的分配、重新分配以及释放)。

当一个程序申请一段内存时,一个“分配者”会负责将内存分配给它,并在不再需要的时候释放出来,以供重新分配。这个过程可以手动控制,也可以自动完成,主要取决于编程语言的特性以及程序员自己的选择。

手动内存管理可以理解为程序员通过自己的代码分配或释放内存。比较著名的,是 C 语言使用的动态内存分配技术。不过,得力于 ObjectiveC 和 Swift 的大力推广,现在流行的大多数编程语言都通过垃圾回收器或自动引用计数(ARC)实现了自动内存管理。


内存管理的陷阱

错误的内存操作会破坏内存区块的分配与释放过程,导致很严重的后果。从更高层面看,内存区块总是会恢复正常的,一个简单的错误似乎并没有那么严重,但系统中总是同时运行着成百上千个进程,不可能都卡在那里,等着某个内存区块恢复正常。

于是,这些错误会用光程序运行所需的必要内存空间,或者更糟糕的是,如果区块被错误地释放或分配,区块中存储的敏感信息,比如密码、密钥或者其它隐私信息,会被攻击者所窃取。

以下是错误的内存操作产生的常见后果:

算术或整数溢出(Arithmetic or integer overflows)

由于错误的算术计算,原来分配的内存区块无法存储最后的结果。比如说,一个程序可能定义了一个占用 8 位内存的值,只能存储 -128 到 +127 之间的数字,假设程序先将这个数字赋值为 127,之后又加了 1,就会导致一个预期外的结果,因为 8 位内存空间无法存储 128 这个值。

这个 Bug 由 Brumley, Chiueh 和 Johnson 在 2012 年定义,具体描述是,“一个变量的值超出了机器存储这个值所用字节的表示范围”。产生这个 Bug 的原因很多,比如向上溢出、向下溢出、数据截取、符号错误等,主要是由于错误定义的语句或整数操作,而程序员要定位问题往往很困难。不同语言处理这个问题的方式也不一样——例如,Smalltalk 与 Scheme 会自动升级变量类型,而其它一些语言则把问题留给程序员自己。

内存泄露(Memory Leak)

如果一个程序一直向系统申请,但不释放内存——也就是说,告诉系统哪些内存可以重新利用了——就会导致内存泄露,程序最终会用完所有可用内存。另外,如果程序中的某个对象被存储在内存中,但运行中的代码实际上已经没法访问到它了,也会导致同样问题。

段错误(Segmentation faults)

当某个程序访问它没有权限访问的、另作它用的内存空间,或者对某部分内存执行超越权限的操作,比如试图对只读内容进行写操作时,就会导致段错误。段错误可能导致程序挂起、崩溃或退出。

缓冲区溢出(Buffer overflows)

当程序要写入的内容超过了被分配的空间长度,它继续写入到之后的,另作它用,或者没有写权限的内存空间时,就会导致缓冲区溢出。缓冲区溢出也会使程序挂起、崩溃或退出。

删除错误(Double delete)

当程序试图删除一个已经被删除的对象,因而导致堆污染或者段错误时,就叫删除错误。删除错误也可以认为是段错误的一个子集。


手动 VS 自动内存管理

对程序员来说,最常见的内存问题就是如何操作内存的问题——如果说系统可以把内存分配给程序,那么,程序所使用的编程语言是手动还是自动完成内存分配的呢?以及更重要的,这种分配方式会导致什么结果呢?

手动内存管理是指在特定语言中,程序员必须通过自己的代码来管理内存,与之相对地,自动内存管理是指程序员不需要或基本不需要执行什么动作来操作内存。我们这里所说的“操作”和“管理”,是指申请、重新分配内存,或者释放掉我们认为已经成为“垃圾”的内存空间。

直到上世纪 90 年代中期,主流编程语言都支持手动内存管理,即使在今天也依然如此(以关键词 “new” 或 “alloc” 的形式)。不过,这仅仅是因为对象创建,也就是为对象分配内存的过程很容易而已——程序员在创建对象的时候,可以清楚地知道对象的大小、名称以及初始化过程。然而,销毁对象就困难多了,由于销毁过程往往在对象创建很久之后才触发,程序员可能并不知道对象的大小。更麻烦的是,程序员可能也不知道具体在哪个时间点应该销毁对象,很有可能,软件中的某部分代码依然在使用这个对象。

如之前所说,如果不能正确地初始化或销毁对象,就会导致内存错误。编程语言如何处理内存错误取决于它的具体实现:大多情况下,内存错误会导致“未定义行为(undefined behavior)”——也就是说,说不准会发生什么。(注意,在准确的手动内存管理下,一切都是确定的,程序员总是清楚一个对象什么时候被创建或被销毁。)

1959 年,一个内存管理的新概念——垃圾回收——被引入 Lisp 编程语言。垃圾回收是自动内存管理中最著名的一个例子,通过垃圾回收,之后不再使用的对象会被销毁,空间会被释放。这种技术减少了 Bug,提高了内存管理水平。垃圾回收的具体实现采用了多种策略,包括对象追踪、引用计数、时间戳、心跳等。

其它自动内存管理技术包括基于栈的内存管理(stack-based memory allocation)、基于作用域的内存管理(region-based memory management)、自动引用计数(ARC)等。不过,这些技术都存在一些性能问题,也带来了某种不确定性,因为程序员并不能准确地知道对象是在什么时候被销毁的。

当然,手动内存管理与自动内存管理都还被今天的编程语言广泛应用:前者以 C 语言家族为代表,后者以 Lisp、Java 以及其它众多语言为代表。事实上,大多数语言都混合使用这两种技术:如前文所说,通过手动方式分配内存,通过垃圾回收技术释放内存。


结论

如我们所见,电脑帮助人类解决复杂问题的方式,让程序员有一种“宇宙之主”的感觉。我们也注意到,这个宇宙存在着种种规则和限制,其中一个,就是内存总是有限的。不过,正如哈姆雷特所说,作为程序员,我们依然可以“藏身果壳之中,而把自己看作拥有无限疆域的君王。”

相关推荐

Vue的框架(了解)

前端MVC设计模式MVC设计模式,其实就是将前端实现某个业务的所有代码划分为三部分Model:模型,指数据模型,这个数据一般来自于服务器View:视图,指页面标签内容Controller:控制...

Vue.js实战 第五章练习一

练习要求:在原有表格基础上,新增一项是否选中该商品的功能,总价变为只计算选中商品的总价,同时提供一个全选的按钮。实现思路:按照vue数据和dom元素双向绑定的特性,定义allCheckStatus变量...

Vue基础到进阶教程之class和style绑定

关于class和style我们并不陌生,这个在学习css的时候就是家常便饭了,操作元素的class列表和内联样式是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用v-bind处理它们,...

深入Vue 必学高阶组件 HOC「进阶篇」

作者:ssh转发连接:https://mp.weixin.qq.com/s/seKoLSIMtTd1sU4uDrgZCA前言高阶组件这个概念在React中一度非常流行,但是在Vue的社区里讨论...

周末大礼包,23道高质量中级前端面试题。金九银十,建议收藏

这套面试题考察的内容比较常见,涉及到JavaScript、ES6、CSS、Vue、简单算法,浏览器相关知识等。题目列表1.JavaScript的数据类型有哪些2.什么是同源策略3.跨域的方法...

vue3.0-摒弃Object.defineProperty,基于 Proxy 的观察者机制

写在前面:11月16日早上,Vue.js的作者尤大大在VueToronto的主题演讲中预演了Vue.js3.0的一些新特性,其中一个很重要的改变就是Vue3将使用ES6的Proxy作...

程序员都必掌握的前端教程之VUE基础教程(七)

阅读本文约需要10分钟,您可以先关注我们,避免下次无法找到。本篇文章成哥继续带大家来学习前端VUE教程,今天主要讲解VUE的表单处理等知识点。下面我们就一起来学习该块内容吧!01简介在日常开发中,我...

web前端开之网站搭建框架之vue详解

网站搭建框架之vueVue是web前端快速搭建网站的框架之一。它与jQuery有所不同,是以数据驱动web界面(以操作数据改变页面,而jQuery是以操作节点来改变页面),同时,vue还实现了数据的双...

vue3.0尝鲜-基于 Proxy 的观察者机制探索

Vue.js的作者尤大大在VueToronto的主题演讲中预演了Vue.js3.0的一些新特性,其中一个很重要的改变就是Vue3将使用ES6的Proxy作为其观察者机制,取代之前使用...

TypeScript 设计模式之观察者模式

一、模式介绍1.背景介绍在软件系统中经常碰到这类需求:当一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的变化。这是建立一种「对象与对象之间的依赖关系」,一个对象发生改变时将「自动通知其他...

vue面试3

1.单页面应用与多页面应用的去别2.简述一下Sass、Less,且说明区别?他们是动态的样式语言,是CSS预处理器,CSS上的一种抽象层。他们是一种特殊的语法/语言而编译成CSS。变量符不一样,les...

VUE v-bind 数据绑定

动态的绑定一个或多个attribute,也可以是组件的prop。缩写::或者.(当使用.prop修饰符)期望:any(带参数)|Object(不带参数)参数:attrOrP...

vue初学习之自定义选择框实现

v-model简单介绍在使用vue的过程中会经常用到input和textarea这类表单元素,vue对于这些元素的数据绑定和我们以前经常用的jQuery有些区别。vue使用v-model实现这些标签...

Vue实现拖拽穿梭框功能四种方式

一、使用原生js实现拖拽打开视频讲解更加详细Vue实现拖拽穿梭框功能的四种方式_哔哩哔哩_bilibili<html><head><meta...

Vue3.x setup 语法糖实现props双向绑定

1.背景为了封装一下Element-Plus的分页插件,需要实现父子组件之间的传值。2.父组件<scriptsetuplang="ts">letqueryPa...

取消回复欢迎 发表评论: