一、前言
资深数码科技爱好者朋友可能都还记得,在当年的Windows 98/Me时代,隔几个月都要重复一项重要工作,那就是磁盘清理。当年只有机械硬盘,价格很贵空间很小,寸土寸金,所以要经常清理。磁盘清理包括两个方面:一、清理无用的垃圾,二、整理磁盘碎片。
小编对当年Windows Me自带的磁盘碎片整理程序印象深刻,启动之后是满屏不同颜色的方格,开始清理之后方格的颜色会不断地变化,以显示进度。
整个过程可能需要持续大半个小时,在这个过程中尽量不要进行其他的操作,那个时候小编刚有电脑不久,端一杯茶,看着满屏不断滚动、改变颜色的方块,感觉特别轻松、解压。小标现在无法找到到原始图片,大致和上图类似。
——然而,本文分享的主题不是磁盘碎片,而是内存碎片,是的,你没有看错。
二、什么是内存碎片?
内存碎片的概念并不复杂,它和磁盘碎片非常类似,是指由于多种原因所导致的内存空间中产生的不连续的空闲空间,是一部分无法被有效利用,被浪费的内存资源。
看到这里,可能有些朋友感到非常困惑:
内存的读写速度远超过硬盘(包括机械硬盘和固态硬盘),性能和效率都非常高,为什么会出现内存碎片呢?另外,我在Windows任务管理器中,内存资源使用量显示的非常清楚,分别包括“使用中”、“备用”和“可用”,根本不存在小编所说的,“无法被有效利用,被浪费的内存资源”,这是怎么回事?
——其实,内存碎片是客观存在的,只是它无法用以上常规方式查看,表现出来而已。
三、内存碎片的类型
内存碎片主要分为两种类型:外部碎片和内部碎片。
1、外部碎片
外部碎片是指目前还没有被系统分配出去,不属于任何进程,但是由于空间太小,无法分配给其它新进程的内存空闲区域。
这一段话说起来很抽象,其实很好理解。比如说你到超市去买苹果,一个小塑料袋可以装3斤,但是你想买5斤,这种情况下就需要两个塑料袋。
前一个塑料袋虽然装三斤苹果已经满了,但是,严格来说并没有“彻底”装满,苹果和苹果之间有很多空隙,但是这种空隙太小,无法再用来装苹果。
大家可以把内存想象、比做成是塑料袋,苹果是申请内存空间的进程,那么苹果和苹果之间的空隙就相当于是内存碎片,虽然客观 上处于空闲状态,但却无法被使用。
2、内部碎片
内部碎片是指目前已经被系统分配出去,能确定属于哪个进程,但却不能被利用的内存空间。这种情况通常和具体的进程(程序开发者)有关,另外,有时也并不完全是因为开发者疏忽或者有bug所造成的。
比如要开发一个文字编辑器,字数上限为3000,为了保证这个程序能正常运行,前端和后台数据库用来存储这些内容的空间都必须设置为3000。
但有些情况用户可能只写了1000字或者2000字,多余的就被闲置了。在这种情况下,程序开发者并不能因为有人没有写满3000字,就降低分配空间,因为还是有人可能会写到3000字。
小编再拿买苹果举例,一个塑料袋可以装三斤苹果,但你只买了两斤,这个塑料袋完全还可以再装一斤。另外你还要买两斤猪肉,从理论上讲,为了节省空间,你完全可以把其中一斤猪肉再装到前一个塑料袋里面去。
但是猪肉有腥膻味,最好是不和苹果放在一起,所以你需要再拿一个塑料袋,把两斤猪肉单独装进一个塑料袋,这样装苹果的那个塑料袋还有一斤容纳空间就被闲置了。
四、内存碎片的产生常见原因
上文小编列举了一些内存碎片的例子和产生的原因,其主要目的是尽可能地简化,以便于读者理解,实际导致出现内存碎片的原因更加复杂,而且非常多,有以下几种。
1、频繁地内存分配和释放
比如说要用程序制作一个下雪的场景,类似的还有下雨、流星等,统称为粒子系统。首先我们要制作一个下雪效果的样本,一个白色圆点从上到下移动,这个过程可以用程序生成,也可以用某些动画设计软件手动制作。
然后,下一步工作就是在同一个画面中,用for循环代码复制数百个下雪效果的样本,然后再用程序随机地改变他们的坐标(起始位置和终点)、大小,移动速度和透明度等参数,这样一个简单的下雪效果就生成了。
在这个过程中,要生成数百个下雪效果的样本,每一个样本在被程序动态创建的时候都会被分配、占用一部分内存,到达终点后就会被删除,相当于释放内存,然后再创建新的样本,反复循环。
这个程序就会涉及频繁的内存分配和释放,创建的样本越多,产生外部碎片的可能性也就越大,所以不管是什么开发架构,粒子系统通常都比较耗内存。
2. 内存对齐要求
某些系统和硬件要求内存地址对齐,以确保数据在内存中的起始地址按照一定的边界(如2、4、8、16等字节的整数倍)进行对齐,以提升性能和效率。
x86和x64架构在内存对齐要求方面相对宽松,而ARM、RISC-V等架构则较为严格。某些特定的硬件平台(如MIPS、PowerPC以及某些DSP)可能不支持非对齐的内存访问,如果发生非对齐的内存访问,则可能会导致性能下降、硬件异常甚至程序崩溃。
内存对齐可以在一定程度上提高性能,但凡事有利就有弊,这样有可能会导致分配的内存块大小超过实际需求,从而产生内部碎片。
3. 不合理的数据结构设计
设计过大的数据结构或不合理的内存分配策略都可能导致内存碎片的产生。比如新建了10个数据类型为int的数组,实际上却只存储了6个,类似这种情况越多,所产生的内存碎片也就越大。
这个例子对于很多普通数码爱好者朋友来说可能有些抽象,下面小编还拿买苹果举例。比如说你想买三斤苹果,有一种塑料袋正好能装下三斤,结果你拿了一个可以装五斤的塑料袋,那两斤剩下的空间就相当于是内存碎片被闲置、浪费了。
五、内存碎片的负面影响
综上所述,由于各种复杂的内外部原因,内存无法被百分之百地充分利用,产生内存碎片很难完全避免,至于它到底会造成哪些危害,危害程度有多大,要取决于具体的实际情况。
1. 内存浪费
这是最直接的表现和结果。虽然,这部分内存碎片可能在任务处理器中可能被归纳、显示中“使用中”,但实际上它只是被某些进程所“占用”,并未被实际使用,或者无法被分配使用,处于闲置被浪费的状态。
2. 性能下降
数据存储到内存中是手段而不是目的,它最终要交由CPU调用,以执行、完成各种任务。如果内存碎片过多,CPU在调用内存中的数据的时候,需要消耗更多的时间去寻址。
另外,如果有新进程申请分配内存,操作系统在分配内存时可能需要花费更多的时间来寻找合适的内存块,导致程序运行效率下降,分配大块内存可能会变得非常困难,甚至可能导致分配失败。
这些问题结合在一起,最终的表现就是系统和应用程序运行卡顿,甚至出错。
4. 内存泄漏
内存泄露是内存碎片的一种特殊、极端情况。一般来说,数据不会长久地存储在内存中,在任务完成后所占用的内存必须被释放。
内存泄漏是指程序动态申请、分配内存后,由于某种原因,最终未被释放或无法被释放的情况。
比如说,在某一段程序中,某部分代码存在内存未被或者无法被释放的情况,而这部分功能(代码)很重要,会反复执行,那么这种被无效占用、闲置的内存(内存碎片)就会越来越多,这就是严重的内存泄露。
如果内存泄露程度较轻,不会有太大的异常,如果情况较重,会导致程序严重卡顿,如果情况特别严重,就可能导致程序崩溃。如果内存泄漏发生在Windows系统中(存在bug),那么Windows系统也会崩溃,包括不限于蓝屏死机和重启等等。
另外需要强调指出的是,内存泄露并不总是由开发者疏忽所造成的,有一些恶意攻击者会精心设计带有陷阱性质的恶意程序,故意诱导、促使某个应用程序或者操作系统发生内存泄漏,以达到一些非法目的,这是一种常见的攻击手段。
六、总结
综上所述,由于多种复杂原因,内存被百分之百地合理、充分使用的理想状态是不存在的,如何合理、高效地分配、管理、回收释放内存,应因各种异常,一直是Windows操作系统努力的方向,当然其他操作系统也面临类似的问题。
可能有一部分朋友非常感兴趣Windows操作系统如何应因内存碎片,如何尽可能地减少内存碎片的产生和提高内存利用率,这是另外一个话题,小编将在后续的系列文章中介绍,敬请期待。