• 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

  • 相关阅读:
    一.js高级(4)-函数调用-this指向-其他参数
    一.js高级(3)-原型及其继承
    一.js高级(2) -构造函数-原型对象
    curl ,post,get (原创)
    PDOHelper (原创)
    php 写日志函数(原创)
    一致性hash 算法 (转)
    md5 c# unicode 互换(原创)
    php auto_load mvc 接口框架(原创)
    php获取uniqid
  • 原文地址:https://www.cnblogs.com/llkmst/p/4826349.html
Copyright © 2020-2023  润新知