只会写代码的程序员就像一名司机会踩油门、打方向盘、能将车子从A开到B。而实际开发中的内存溢出、内存泄漏、以及性能优化问题就像驾驶途中出现了爆胎或者交通堵塞问题如果不对其机理有所了解则会陷入茫然无措的境地。所以今天就和霞姐一起学习一下物理内存和虚拟内存的相关知识加深一下内力吧一、物理内存1物理内存是什么通俗来说物理内存就是主板上实际安装的、可由内存控制器直接寻址的内存条。物理内存真实存在并且打开机箱就能看到。在linux系统上用户可以使用free -h命令或者dmidecode -t memory查看物理内存的详细信息。2物理内存的局限性物理内存出现的很早。20世纪50年代的电子计算机存储系统已经采取了磁芯磁鼓两级结构。主存为磁芯今天的RAM辅存是磁鼓今天的Disk。CPU只能对主存寻址。只有物理内存的时候程序员很苦。程序员需要设计出合理的方案将程序划分成多个块并调度这些块被称为段或页在两级存储之间的传输被称为覆盖或交换。以矩阵乘法为例如果在不考虑内存容量的情况下只需要数行代码即可实现。但如果内存无法同时容纳三个矩阵的情况下程序员就需要确定矩阵哪些行和列需要被装载进内存制定将其调入主存的策略并在程序中插入相应的代码实现这不仅带来代码量的膨胀也非常耗时。因此50年代首批操作系统的设计者就梦想着通过自动化管理所有存储操作来减轻程序员的负担。二、虚拟内存1动机和历史让程序受物理内存数量的限制是非常不方便的。不仅要考虑物理内存容量限制、还会带来内存碎片化、多程序之间做不到安全隔离从而导致的崩溃等问题。计算机的世界里有一句名言那就是剑桥大学教授David Wheeler说的“All problems in computer science can be solved by another level of indirection.计算机科学领域的任何问题都可以通过增加一个间接中间层来解决。”虚拟内存就是用来解决前述问题的关键技术。1959年曼彻斯特大学的阿特拉斯团队成功研制出首个可运行的虚拟内存原型。该原型包括三项关键技术(a) 能自动将处理器生成的每个地址转换为其当前对应的内存位置的硬件设备(b) 由地址转换器触发的中断机制可将缺失的数据页调入主内存的请求分页技术(c)能识别并将利用率最低的页送回辅助内存的替换算法。60年代后虚拟内在商业操作系统中得到广泛应用IBM 360/67、CDC 7600、Burroughs 6500、RCA Spectra/70 以及 GE 645 等机型均配备了虚拟内存。70 年代中期IBM 370、DEC VMS、DEC TENEX 和 Unix 等系统也相继采用了虚拟内存技术。2虚拟内存是什么虚拟内存是对主存的抽象它提供了三个重要的能力(1)对主存的高速利用将主存看成是一个存储在磁盘上的地址空间的高速缓存。在主存中只保存活动区域并在主存和磁盘之间来回传送数据比如在玩大型游戏时游戏文件是装载磁盘上的。不是所有的游戏都会加载到主存中只有当前运行所需部分才会被加载到主存中。有一些代码比如错误处理只有很少的机会被运行到如果常驻主存将是对空间的浪费。(2)简化内存管理为每个进程提供了一致、私有的地址空间它定义了一个连续的虚拟地址空间每个进程都认为自己在独享主存。这降低了程序员编程的难度。(3)增强了安全隔离保护每个进程的地址空间不被其它进程破坏通过虚拟内存机制用户能以和主存接近的速度和磁盘相近的价格获得“主存磁盘”大小的内存容量。3虚拟寻址(1)CPU通过生成一个虚拟地址来访问主存(2)CPU内的MMU内存管理单元利用存放在主存中的、由OS管理的查询表页表来将虚拟地址动态翻译成物理地址。(3)MMU根据物理地址访问主存(4)主存返回物理地址存储的数据给CPU4Linux的虚拟内存系统Linux为每个进程维护了一个如下图所示的虚拟地址空间。(1)内核虚拟内存包括内核中的代码和数据结构。某些区域被映射到所有进程共享的物理页面比如所有进程共享的内核的代码和全局数据结构。(2)进程虚拟内存这个大家都很熟悉了在我前一篇文章中也有类似的图。Linux将虚拟内存组织成一些区域area也叫段segment的集合。代码段、数据段、堆、共享库段、用户栈都是不同的区域。虚拟地址空间是可以有间隙的。当MMU试图翻译某个虚拟地址时触发缺页后会进入到内核的缺页处理程序。缺页程序会判断虚拟地址是否合法、对它进行的内存访问是否合法如果不合法则触发相应的异常合法的话则进行换出换入等正常缺页处理。三、对程序员的启示学习了物理内存和内存相关的知识后霞姐列举了三点对程序员的启示大家也可以发散思维想想看哦~1.利用局部性原理写出locality好的代码尽量让数据访问集中在一块连续的内存区域这样可以提高缓存命中率减少缺页异常。2.警惕内存泄漏因为虚拟内存的存在内存泄漏可能不会立即导致程序崩但会不断消耗物理内存久而久之堆里就会充满垃圾这对不会终止的守护进程类的程序来说更加严重。3.理解malloc/new的成本申请虚拟内存是“廉价”的但第一次写入时触发的缺页异常和分配物理内存是有成本的。在性能敏感的代码中可以考虑使用内存池来预分配内存。