JVM和Linux进程的内存模型
.class –> JVM —> Linux
JVM 本身就是用原生代码(C& C++)实现的,也就是说一个JVM进程其实就是一个C语言开发的程序在运行,JVM上可以解释class字节码文件,在JVM层,内存主要划分为方法区(指令,Class对象,static成员变量),heap堆区(new出来的对象和常量池),JVM stack ,虚拟机栈(函数运行需要分配栈空间,以及函数的局部变量都存储在栈上),程序计数器。
当JVM进程运行在Linux系统上,Linux实际上给JVM进程分配的内存空间区域划分,text段(放指令),rodata(常量的),data(初始化),bss(未初始化)(数据段),heap堆(malloc或者new),stack(函数运行需要分配栈空间,以及函数的局部变量都存储在栈上);
相当于把JVM方法区的指令最终映射到Linux进程上的.text段上,把数据映射到Linux进程的.data .bss 数据段上,jvm heap映射到Linux进程的heap,jvm stack 映射到Linux进程的stack上;
不管是JVM还是Linux内存模型,进程里面的线程只有栈是不一样的,其他的是线程共享的。
Linux系统的地址映射
程序在还没有运行之前,实际上程序的指令和数据的地址都已经分配好了,但是这个指令或者数据的地址实际的物理地址吗??
不是,程序的指令和数据所分配的地址,绝对不是物理地址,他们都是虚拟地址(IBM)!!!
虚拟地址 -----》 虚拟空间上的地址;
32位Linux系统虚拟地址空间 2^32 4G大小。
[外链图片转存失败(img-oQr3t2EL-1568911473735)(G:\20190727-Linux\虚拟地址空间.png)]
每一个进程都有一个虚拟地址空间,地址范围相同。
存在,能看见,它是物理的;存在,看不见,透明的。
Linux系统会给每一个进程都提供了一份它自己的页目录和页表内容。
每一个页目录对应一个页表项;
Linux上地址映射的过程???
-
代码运行之前,编译生成的指令和数据,其地址已经分配好了,(编译的时候,函数和数据的地址是一定要分配的,否则汇编指令没办法生成,无法形成函数的调用关系和数据的运算关系),但是程序没有运行之前,又不知道将来会在哪一类物理内存上运行,所以此时给指令或者数据分配的地址,都是------虚拟地址(虚拟地址空间上的地址 )------Linux系统会给每一个进程都分配一个虚拟地址空间,各个进程多能适用的虚拟地址范围是一样的。
-
当执行当前进程,CPU开始执行当前程序的指令,或者做全局数据的初始化,或者再执行指令的过程中,要访问数据,都得去内存上取指令或者数据!!
- 那么不可能直接发送指令和数据的虚拟地址来定位他们在物理内存上的位置,此时要做虚拟地址 -----物理地址的地址映射;
-
由MMU(内存管理单元)和Linux系统共同完成地址映射的。
-
Linux系统负责提供该进程地址映射所需的页目录和页表内容;
-
MMU负责具体的映射计算过程;
-
MMU会先把虚拟地址分成10位(pdindex 页目录)、10位(ptindex 页表)、12位(offset 偏移量)三份,第一个10位表示页目录的下标,第二个10位表示页表的下标,12位表示在物理页面上的偏移量;
-
PD[pdindex] --->PT地址
PT[ptindex] --->一个物理页面的起始地址
一个物理页面的起始地址 + offset = 指令或数据所在的位置
程序启动,任何的物理内存都没有分配过,进程只有一个页目录,进行地址映射的时候,会不断产生”缺页异常“,转入缺页异常程序处理,发现导致地址映射失败的原因:页目录项中没有分配页表,还算是页表项中没有分配物理页面。。。。;然后进行相应资源的分配,重启地址映射过程,最终映射成功!!!