一丶背景
项目出现内存报警,当我们导出大文件excel的时候。以下是相关参数与猜想。
jvm参数:-Xms8192m
excel文件大小:一个二十多万行的excel数据。
报警条件:物理内存高于80%会触发报警
相关猜想:按理说堆内存我们一来就分配好8个g的物理内存,导出文件触发的堆内存增长应该不会引起物理内存的增长。
二丶结论
此次报警,属于正常能预期到的现象,我们线上程序并未受到影响。我们jvm参数配置Xms8192m,实际上分配的是虚拟地址空间,并不是实际的物理内存,导出excel的时候堆空间大量增长,那么对应的虚拟地址就会去大量的命中实际的物理内存,所以就算我们堆空间的内存降下去后,实际命中的物理内存也分配给了对应的堆内存,导致物理内存永久下降。
三丶虚拟地址空间
我们的进程其实是运行在虚拟地址空间里,cpu的寻物理内存址过程其实是对虚拟地址的地址翻译后再找到实际的物理地址。
为什么我们使用虚拟地址呢?
-
让每个进程拥有了相同的、独立内存空间,相互之间不会干扰
- 如果没有虚拟地址,每个进程直接对物理内存进行操作,势必会存在各个进程相互影响而无法正常进行。
-
方便各个进程之间内存共享
- 可以映射到不同的物理内存。其实不同进程的虚拟地址也可以映射到相同的物理内存以实现内存共享。
- 比如每个操作系统的进程,都会需要跟内核程序打交道。有了内存共享,多个进程间就可以共用内核程序,而不需要为每一个进程在物理内存里加载一份内核程序。
-
不连续的物理空间可以映射成连续的虚拟地址空间
- 可以将碎片化的物理内存映射到连续的虚拟地址。
-
进程分配的内存空间只有在实际使用时,才会触发缺页异常来分配实际物理空间,从而最大程度减少了内存空间的浪费。
- 在我们使用虚拟地址空间时,如果没有对应的物理内存,就会出现我们常见的缓存不命中的情况。专业术语叫缺页异常。这时内核的缺页异常处理程序,将会帮助我们分配物理内存,如果物理内存不足,它将会选择一个物理内存页作为牺牲,写回磁盘上,这也就是我们所说的交换分区(linux叫做交换区,window上叫做虚拟内存)。
四丶jvm与虚拟地址空间
JVM进程,本质上就是一个用c++写的普通进程,起开始申请的地址其实是一个虚拟地址空间,并不是实际的物理内存,必须在分配的虚拟地址空间实际使用才会触发分配实际的物理内存。
五丶相关佐证
从下图可以看出我们生产环境配置的物理内存的使用是远远小于我们分配的jvm参数配置Xms8192m,说明并不是随着程序的启动就分配了对应的物理内存。
六丶总结
我们生产环境监控的物理内存是监控包含了一部分的堆内存的大小,但是我们堆内存设置为-Xms8192m -Xmx8192m -Xmn2048m。实际上这个监控意义不大,因为这个年老代内存的增长是随着程序的运行时必定增长的。
七丶扩展-缺页
看完上述后相信有人肯定还有一些疑问,缺页到命中物理内存是怎么一个过程,那么首先我们的需要了解一些概念,虚拟内存,虚拟页,物理页,物理内存,页表,DRAM。
虚拟内存:
- 虚拟内存被组织为一个由存放在磁盘上的N个连续的字节大小的单元组成的数组,本质时磁盘上的对象,而每个自己对应的地址,我们就理解为虚拟地址。
虚拟页:
- 就是虚拟内存分割为固定大小的块,我们就叫做虚拟页,由上可以得知。虚拟页实际上时在磁盘上的
物理页:
- 物理内存分割为固定大小的块,叫做物理页
DRAM:
- 虚拟内存系统的缓存,在主存中缓存虚拟页,说通俗点,就是物理内存对应的虚拟系统(自己理解)
页表:
页表就是一个页表条目的数组,功能是将虚拟页映射到物理页,我们每次读取虚拟地址时,实际上读取的就是页表。页表可以形象的理解为虚拟页和物理页的一个管理,DRAM缓存内存时,如果缓存了物理地址,那么虚拟地址就能直接映射到物理地址,如果页表上没有缓存物理页,那么就是缺页异常,需要进行一次页面调度
页面调度过程与缺页异常 :
当我们去用虚拟内存地址时,我们DRAM没有缓存到物理页时,会触发一个缺页异常,内核会选择一个空闲的物理页作为牺牲页,并且将虚拟页磁盘的上复制到对应的物理页,那么这个虚拟页就缓存到了对应的物理页,虚拟地址就能直接映射物理地址了,完成了一次页面调度
八丶参考书籍
《深入理解计算机系统-第三版》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。