JVM内存结构组成

背景:一谈到JVM一直是很多人觉得头疼的知识点,那么针对JVM这个痛点,我总结了一些,网上很多谈到由浅入深JVM,其实丑话说在前,一篇文章或者几篇文章是不够深入JVM的,但至少知其然。

PS:至于知其所以然,依旧还是推荐《深入理解JVM》这本书,虽说它很多还是基于JDK1.7去演示的,但万变不离其宗。且目前已有更新第三版,完全不用担心过时。周老师还是很强滴~~

一、JVM内存结构组成

首先我们来看一张图。由图我们可得知,JVM组成主要包含 堆、栈、元区间(方法区)、本地方法栈、PC寄存器等。

且JVM内存中包含 栈、本地方法栈、PC寄存器。且在1.8之前是包含方法区的。1.8之后揪出来放在了内存。

 

 

1.1、堆

堆:只要new一个对象,就会存放在堆里。比如定义一个数组,堆数据所有线程都会共享。在Java虚拟机启动就建立堆,最主要的内存工作区域,几乎所有的对象实例都存放到Java堆中。我们可以认为堆中的变量是持久存在的,而栈的变量是临时态的。至于老年代新生代垃圾回收后期记载。

比如创建一个数组array:

 

public int[] array = new int[] {1, 2, 3};

那么该对象array就会存放在堆内存中,被所有线程共享。由此也会引发一个很头疼的问题,当类A与类B同时操作该数组时,会出现冲突,造成数据错乱,那么就产生线程安全问题。让人头疼。当然解决的方法也有很多种,JDK中syn锁,lock锁等都可以处理,这里跳过。

 

1.2、栈

栈:栈她由一个个栈帧组成,那么栈帧是什么?其实就是一个一个的方法体,比如方法say,就是一个栈帧。

public static void say(String text){
    String remark = "Hello world";
    System.out.println(remark + text);
}

栈帧(方法体):那么通过方法体来形象的理解栈帧。每个栈帧都包含 局部变量表、操作数栈、动态链接、返回链接。

  • 局部变量表:用于方法参数和方法内部定义的局部变量。通过索引访问。Say方法中的text参数与变量remark属于局部变量
  • 操作数栈:又称操作栈,通过标准弹栈压栈进行访问。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。看下面的示例,它演示了虚拟机是如何把两个int类型的局部变量相加,再把结果保存到第三个局部变量的。
begin  
iload_0    // push the int in local variable 0 onto the stack  
iload_1    // push the int in local variable 1 onto the stack  
iadd       // pop two ints, add them, push result  
istore_2   // pop int, store into local variable 2  
end  

 在这个字节码序列里,前两个指令iload_0和iload_1将存储在局部变量中索引为0(100)和1(98)的整数压入操作数栈中,其后iadd指令从操作数栈中弹出那两个整数相加(100+98),再将结果压入操作数栈。第四条指令istore_2则从操作数栈中弹出结果(198),并把它存储到局部变量区索引为2的位置。下图详细表述了这个过程中局部变量和操作数栈的状态变化,图中没有使用的局部变量区和操作数栈区域以空白表示。


看完以上一堆代码我们再来简单复述下,它对应的Java代码实际上就是

public static int add(){
    int i0 = 100;
    int i1 = 98;
    // int i2 = i0 + i1; return i2;
    //198
    return i0 + i1;
}

是不是很简单?

 

  • 动态链接:动态链接的概念,就相当于你在say方法中,调用了add方法,来段代码
public static void print(){
    System.out.println(add());
}
  • 返回链接:即指方法运行后,返回某处。以print方法为栗子,add方法返回链接就指向print,而print是无返回void方法,那么程序会直接运行完毕。若是在main方法中运行print就会返回到main方法,并继续走完程序。

1.3、本地方法栈

本地方法栈,最大不同为本地方法栈用于本地方法调用。Java虚拟机允许Java直接调用本地方法(通过使用C语言写)。

 

上图说到Native修饰的方法就是本地方法,那么有哪些呢?举个最通俗的例子。String类源码中的intern方法。

public native String intern();

那么该方法就是个本地方法,至于使用与作用,在下文方法区详细讲解。

 

1.4、元区间(方法区)

方法区主要存放类的信息、常量信息、常量池信息、包括字符串字面量和数字常量等。

那么举个栗子来了解一下方法区

public static void main(String[] args) {
    
    String a = "abc";
    String b = "abc";
    String c = new String("abc");
    String d = "a";
    System.out.println(a == b);//true
    System.out.println(a == c);//false
    System.out.println(a == c.intern());//true
    System.out.println(a == d);//false
}

下面画图演示下ABC三者的关系图

首先根据a、b、c属于方法内局部变量,那么就是存放在栈中。其次是方法区字符串常量池中的"abc",是由方法内部创建而来。

至于堆中也会有个"abc",那是由于 c是通过new String()创建的。

细心的朋友会发现,代码中,a==c为false,a==c.intern()为什么会为true呢?

 

原因:调用了intern方法的字符串会将堆的值放入到常量池中,原理,被native关键字修饰。其实就是将JVM内存中的数据放入了本地内存中。相当于Hashset赋值,堆里面的数据就不会有了。

通过c.intern后的引用图状态。

 

千万注意!!! JDK1.6及之前是false,JDK1.7及以后为true。面试如果有指定JDK的坑的intern相关笔试题,就不要弄错了!

 

1.5、PC寄存器

PC(Program Couneter)寄存器也是每个线程私有的空间, Java虚拟机会为每个线程创建PC寄存器,在任意时刻,一个Java线程总是在执行一个方法,这个方法称为当前方法,如果当前方法不是本地方法,PC寄存器总会执行当前正在被执行的指令,如果是本地方法,则PC寄存器值为Underfined。

寄存器存放当前执行环境指针、程序技术器、操作栈指针、计算的变量指针等信息。

通俗点说,PC寄存器即为程序执行位置。

 

 

二、汇编

在上文中的栈帧中提到了汇编的操作,相信大家应该也是迫不及待的想知道原理,也想自己动手操作呢!那么教程

比如现在要汇编Test类

public class Test {

    /*
            汇编代码执行
         0: bipush        100   将一个8位带符号整数压入栈(这里是压入100)
         2: istore_1            将int类型值存入局部变量(其他类型有其他的规则)
         3: bipush        99    同100压入
         5: istore_2            将int类型值存入局部变量(其他类型有其他的规则)
         6: iload_1             从局部变量中装载int类型值  (将100装载到操作数栈)
         7: iload_2             从局部变量中装载int类型值  (装99载到操作数栈)
         8: iadd                执行int类型的加法 (99+100)
         9: istore_3            将int类型值存入局部变量(其他类型有其他的规则) 定义给第三个变量 c,存入局部变量
        10: return              方法返回
        */
    public void add(){
        int a = 100;
        int b = 99;
        int c = a + b;
    }

    public static void main(String[] args) {
        Test t = new Test();
        t.add();
    }
}

1.首先我们切到对应目录输入命令,注意是当前Test类在的目录

java -c Test.java

2.我们会发现出现不是处理文件,那么我们打开设置

4是名字,5是备注都可以更改

4:JavapUser
5:执行Javap命令,我要汇编我不管必须要行
6:$JDKPath$\bin\javap.exe
7:-v $FileClass$
8:$OutputPath$

3.编辑完成后,我们运行命令也不会成功的,至少我不会,那么如何做呢?

右键呼出菜单,选择External Tools点击显示的命令即可

三、总结

JVM内存中主要包含栈(由多个栈帧组成,一个栈帧等于一个方法)、本地方法栈(Native修饰的方法,如String源码中的intern)、PC寄存器(程序执行位置),JDK1.7之前包含方法区(存放class对象、静态属性、常量池等等)。

1.8之后方法区移动到内存中,更名元区间,与堆(存放new对象)共处一室。

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值