前言:📫 作者简介:小明java问道之路,专注于研究计算机底层,就职于金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的设计和架构📫
🏆 Java领域优质创作者、阿里云专家博主、华为云享专家🏆
🔥 如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主哦
本文导读
现代操作系统提供一种对主存(Main memory(DRAM))的抽象,叫虚拟内存,虚拟内存是硬件异常、硬件地址翻译、主存、磁盘文件和内核的交互。虚拟内存为每个进程提供一个大的、一致的和私有的地址空间。
虚拟内存提供了三个重要的能力:一、虚拟内存将主存看成是一个存储在磁盘上的地址空间的高速存储,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,虚拟内存高效使用主存;二、虚拟内存为每个进程提供了一致的地址空间,从而简化了内存管理;三、虚拟内存保护了每个进程的地址空间不被其他进程破坏。
为什么要学习了解虚拟内存?一、虚拟内存是核心,遍布计算机系统所有层面(硬件异常、汇编器、链接器、加载器、共享对象、文件和进程);二、虚拟内存强大,可以创建和销毁内存片、将内存片映射到磁盘文件的某个部分,以及与其他进程共享的内存;三、虚拟内存是危险的,每次应用程序引用一个变量、间接引用一个指针,或者调用动态分配内存的程序是,就会和虚拟内存发生交互。
本章需要了解虚拟内存如何工作的,应用程序如何使用和管理虚拟内存。
重点解读:
一、物理地址和虚拟内存
计算机系统中主存被组织为一个由 M 个连续字节大小的单元组成数组。每字节都有一个唯一的物理地址(Physical Address PA)。虚拟地址是虚拟的,CPU 通过 MMU(内存管理单元——利用主存中的查询表动态翻译虚拟地址 )硬件将虚拟地址->物理地址,流程如图:图现代处理器使用的一种虚拟寻址的寻址形式。
二、地址空间
主存中的每个字节都有一个选自虚拟地址空间的虚拟地址和一个选自物理地址空间的物理地址。虚拟内存作为缓存的工具,虚拟内存被组织为一个由存放在磁盘上的 N 个连续字节大小的单元组成数组。
三、Linux 虚拟内存系统
liunx 为每个进程维护一个单独的虚拟地址空间,如下图虚拟内存,包括代码、数据、堆、共享库以及栈。这部分虚拟内存位于用户栈上。
内核虚拟内存包含内核中的代码和数据结构。内和虚拟内存的某些区域(内核的代码和全局数据结构)被映射到所有进程共享的物理页面。Linux 将一组连续的虚拟页面(大小等于系统中 DRAM 总量)映射到响应的一组物理页面。例如当需要访问页表或在一些设备上执行内存映射的 IO 操作,这些设备被映射到特定的物理内存位置。内核虚拟内存其他区域(页表,内核在进程上下文执行代码时用的栈,记录虚拟地址空间的数据结构)包含每个进程都不相同的数据。
四、Linux 虚拟内存区域
内核为系统中的每个进程维护一个单独的任务结构(源代码中的 task_sturct)。任务结构中的元素包含或者指向内核运行该进程的所需要的所有信息(例如,PID、指向用户栈的指针、可执行目标文件的名字以及程序计数器)
五、内存映射
Linux 通过将一个虚拟内存区域与一个磁盘上的对象关联起来,以初始化这个虚拟内存区域的内容,这个过程叫做内存映射。
虚拟内存区域可以映射到两种类型的对象中的一种:
Linux 文件相同中的普通文件:一个区域可以映射到一个普通磁盘文件的连续部分,例如一个可执行目标文件。这些虚拟页面并没有加入物理内存,知道 CPU 第一次引用。匿名文件:一个区域也可以映射到一个匿名文件,匿名文件是由内核创建的,包含的都是二进制零。
六、写时复制(copy-on-write)
私有对象使用一种叫做写时复制(copy-on-write)的技术巧妙地映射到内存中。一个私有对象开始生命周期的方式和共享对象的一样,在物理内存中只保留私有对象的一份副本。
对于每个映射私有对象的进程,相应私有区域的页表条目都被标记为只读,并且区域结构被标记为私有的写时复制。如果有一个进程试图修改私有区域的某个页面,那这个写操作会触发一个保护故障。,进行如下图的操作:
七、动态内存(dynamic memory)分配
动态内存分配器维护着一个进程的虚拟内存区域,称为堆(heap)。紧接未初始化的数据区域后面开始向上(高地址增长)。对于每一个进程,内核维护者一个变量 brk(break),它指向堆的顶部。
分配器有两种基本的风格。两种风格都要求应用显式分配块。不同之处在于那个实体负责释放已分配块。显式分配器(explicit allocator), C malloc 函数与 free 函数;隐式分配器(implicit allocator), 要求分配器检测一个已分配块何时不被程序使用时释放块。隐式分配器也叫做垃圾收集器(garbage collector),而自动释放未使用的已分配块的过程叫做垃圾收集,例如 Java。
八、垃圾收集
垃圾收集器(garbage collector)是种动态内存分配器,它自动释放程序不再需要的已分配块。垃圾收集器将内存视为一张有向可达图。
Java 的垃圾收集器,对于创建指针比较有严格的规定,能够维护可达图的精准表达。
九、Mark & Sweep 垃圾收集器
Mark & Sweep 垃圾收集器由标记(mark )阶段和清除(sweep)阶段组成,标记阶段标记出根节点的所有可达的和已分配的后继,而后面清除阶段释放每个未被标记的已分配的块。块头部中空闲的低位中的一位通常用来表示这个块是否被标记。
C 使用 Mark & Sweep 垃圾收集器来处理的时候是必须保守的,其根本原因是因为 C 不会使用类型信息来标记内存位置。
小结
1、虚拟内存是对主存的抽象;2、支持虚拟内存的处理器通过使用虚拟寻址的间接形式来引用主存;3、处理器产生一个虚拟地址,在被发送到主存之前,这个地址被翻译成一个物理地址;4、虚拟地址空间到物理地址空间的地址翻译要求软硬件结合,硬件通过使用页表来翻译虚拟地址,而页表的内容由操作系统提供的;5、虚拟内存提供了三个重要功能:(1)在主存中自动缓存最近使用的存放磁盘上的虚拟地址空间的内容;(2)简化内存管理,进而简化链接。在进程间共享数据、进程的内存分配以及程序加载;(3)简化内存被保护;6、地址翻译的过程必须和系统中所有硬件缓存的操作继承在一起。大多数页表条目位于 L1 高速缓存中;7、内存映射为共享数据、创建新的进程以及加载程序提供了一种机制;8、动态内存分配器是一个感觉像系统级程序的应用级程序,直接操作内存。分配器有两种类型,显示分配器要求应用显示释放内存块,隐式分配器(垃圾回收器)自动释放任何未使用和不可达块;