JVM 运行时内存结构

tt1

 

1.JVM内存模型

      JVM运行时内存=共享内存区+线程内存区

tt2

1).共享内存区

      共享内存区=持久带+堆

      持久带=方法区+其他

      堆=Old Space+Young Space

      Young Space=Eden+S0+S1

tt3

(1)持久带

      JVM用持久带(Permanent Space)实现方法区,主要存放所有已加载的类信息,方法信息,常量池等等。可通过-XX:PermSize和-XX:MaxPermSize来指定持久带初始化值和最大值。Permanent Space并不等同于方法区,只不过是Hotspot JVM用Permanent Space来实现方法区而已,有些虚拟机没有Permanent Space而用其他机制来实现方法区。

(2)堆

      堆,主要用来存放类的对象实例信息。

      堆分为Old Space(又名,Tenured Generation)和Young Space。

      Old Space主要存放应用程序中生命周期长的存活对象;

      Eden(伊甸园)主要存放新生的对象;

      S0和S1是两个大小相同的内存区域,主要存放每次垃圾回收后Eden存活的对象,作为对象从Eden过渡到Old Space的缓冲地带(S是指英文单词Survivor Space)。

堆之所以要划分区间,是为了方便对象创建和垃圾回收,后面垃圾回收部分会解释。

2).线程内存区

      线程内存区=单个线程内存+单个线程内存+.......

      单个线程内存=PC Regster+JVM栈+本地方法栈

       JVM栈=栈帧+栈帧+.....

      栈帧=局域变量区+操作数区+帧数据区

tt4

 

      在Java中,一个线程会对应一个JVM栈(JVM Stack),JVM栈里记录了线程的运行状态。JVM栈以栈帧为单位组成,一个栈帧代表一个方法调用。栈帧由三部分组成:局部变量区、操作数栈、帧数据区。

 

(1)局部变量区

      局部变量区,可以理解为一个以数组形式进行管理的内存区,从0开始计数,每个局部变量的空间是32位的,即4字节。基本类型byte、char、short、boolean、int、float及对象引用等占一个局部变量空间,类型为short、byte和char的值在存入数组前要被转换成int值;long、double占两个局部变量空间,在访问long和double类型的局部变量时,只需要取第一个变量空间的索引即可。

例如:

public static int runClassMethod(int i,long l,float f,double d,Object o,byte b) {   
    return 0;   
}   
            
public int runInstanceMethod(char c,double d,short s,boolean b) {   
    return 0;   
}

对应的局域变量区是:

1

 

      runInstanceMethod的局部变量区第一项是个reference(引用),它指定的就是对象本身的引用,也就是我们常用的this,但是在runClassMethod方法中,没这个引用,那是因为runClassMethod是个静态方法。

(2)操作数栈

      操作数栈和局部变量区一样,也被组织成一个以字长为单位的数组。但和前者不同的是,它不是通过索引来访问的,而是通过入栈和出栈来访问的。操作数栈是临时数据的存储区域。

例如:

int a= 100;
int b =5;
int c = a+b;

对应的操作数栈变化为:

2

      从图中可以得出:操作数栈其实就是个临时数据存储区域,它是通过入栈和出栈来进行操作的。

      PS:JVM实现里,有一种基于栈的指令集(Hotspot,oracle JVM),还有一种基于寄存器的指令集(DalvikVM,安卓 JVM),两者有什么区别的呢?

      基于栈的指令集有接入简单、硬件无关性、代码紧凑、栈上分配无需考虑物理的空间分配等优势,但是由于相同的操作需要更多的出入栈操作,因此消耗的内存更大。 而基于寄存器的指令集最大的好处就是指令少,速度快,但是操作相对繁琐。

示例:

package com.demo3;
 
public class Test {
 
    public static void foo() {
        int a = 1;
        int b = 2;
        int c = (a + b) * 5;
    }
 
    public static void main(String[] args) {
        foo();
    }
}

 

基于栈的Hotspot的执行过程如下:

111gif

 

基于寄存器的DalvikVM执行过程如下所示:

222

上述两种方式最终通过JVM执行引擎,CPU接收到的汇编指令是:

 

333

(3)帧数据区

      帧数据区存放了指向常量池的指针地址,当某些指令需要获得常量池的数据时,通过帧数据区中的指针地址来访问常量池的数据。此外,帧数据区还存放方法正常返回和异常终止需要的一些数据。

 

转自:http://my.oschina.net/sunchp/blog/369707

posted @ 2015-09-21 15:35  L.L.K  阅读(364)  评论(0编辑  收藏  举报