JVM学习(二)深入了解运行时数据区(方法区、堆、虚拟机栈)

上一次学习中我们已经知道了运行时数据区包含了:方法区、堆、虚拟机栈、本地方法栈、程序计数器这五个部分,那么我们到底要学习什么呢?
我认为本地方法栈和程序计数器是没有必要学习的,或者说已经超出了java领域,那么我们就把重点放在其他三个领域,方法区和堆是存储数据的虚拟机栈则是对应执行方法的时候过程,想到这我就很激动,我们每次写完一个方法到底是在虚拟机中如何运行的?

每个java方法中需要包含什么内容

  • 官网给出的信息是这样讲的:java虚拟机栈压的其实是每一个的栈帧,而栈帧里面包含了:局部变量表(Local Variables)、操作数栈(Operand Stack)、指向运行时常量池的引用(A reference to the run-time constant pool)、方法返回地址(Return Address) 对应代码来看:
public static int sum(int num1, int num2) {
        num1 = 3; 
        int result = num1 + num2; 
        return result;
    }

    public static void subtraction() {
    }

    public static void main(String[] args) {
        sum(1, 2);
        subtraction();
    }

1、局部变量表:方法中定义的局部变量以及方法的参数存放在这张表中 局部变量表中的变量不可直接使用,如需要使用的话,必须通过相关指令将其加载至操作数栈中作为操作数使用。 上面代码:num1,num2,result就是局部变量表
2、操作数栈:以压栈和出栈的方式存储操作数的 。简单理解就是为变量赋值
3、动态链接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态 连接(Dynamic Linking)。即:在运行中的时候才能确定是什么类型、什么地址
4、方法返回地址: 当一个方法开始执行后,只有两种方式可以退出,一种是遇到方法返回的字节码指令;一种是遇见异常,并且 这个异常没有在方法体内得到处理。即:当main方法执行了sum方法执行完了,那么我刚才在main方法中执行哪个位置了,我要知道才能继续执行。

  • 通过字节码指令理解

1、javac Test.java //将Tset.java 转为Test.class

2、javap -c Test >Test.txt //将Test.class 转为字节码并输出到Test.class中

转换完之后的片段代码如下:

 public static int sum(int, int);
    Code:
       0: iconst_3
       1: istore_0
       2: iload_0
       3: iload_1
       4: iadd
       5: istore_2
       6: iload_2
       7: ireturn

  public static void subtraction();
    Code:
       0: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: iconst_2
       2: invokestatic  #10                 // Method sum:(II)I
       5: pop
       6: invokestatic  #11                 // Method subtraction:()V
       9: return

这时候我们又看不懂了,但是肯定有人能看的懂,官方肯定是最权威的,但是本人英文水平确实有点差,所以找了个翻译后的,地址如下:https://www.cnblogs.com/longjee/p/8675771.html

对照之后意思如下

public static int sum(int, int);
    Code:
       0: iconst_3   //将int类型常量3压入[操作数栈]
       1: istore_0   //将int类型值存入[局部变量0]
       2: iload_0    //从[局部变量0]中装载int类型值入栈
       3: iload_1    //从[局部变量1]中装载int类型值入栈
       4: iadd       //将栈顶元素弹出栈,执行int类型的加法,结果入栈
       5: istore_2   //将栈顶int类型值保存到局部变量2中
       6: iload_2    //从局部变量2中装载int类型值入栈
       7: ireturn    //返回int类型值

此时我相信已经能够理解这个方法的执行过程了,那么方法区和堆应该从哪里入手呢,或者与虚拟机栈存在着什么样的关联呢:

1、方法运行时候,有没有虚拟机栈中的变量指向堆呢?

Object obj=new Object(); //显然存在栈指向堆

2、有没有方法区指向堆的情况呢?

private static Object obj=new Object();  //显然存在方法区指向堆

3、有没有堆指向方法区呢?

在代码的书写中,我确实没有看到有对指向方法区的,但是我们想一下,我new 了一个 Object方在堆里面去了,此时,根据之前的知识可以知道的是:方法区中包含类信息,堆中会有对象,那怎么知道对象是哪个类创建的呢 ?或者我认为每一个对象在创建的时候就应该有所记录,那么每创建一个对象都会记录我想大概率是JVM的设计者已经帮我们完成了这一步的记录工作。所以引发出来了另一个经典的面试题:java对象内存模型

java对象内存模型

在这里插入图片描述
由此可见堆指向方法区的这个过程是在对象头中的Klass Pointer(Class对象指针)中完成的。
此时此刻,我们至少是能看到方法区、堆、虚拟机栈这三者进行关联起来了,那么我们可以知道的是:
1、虚拟机栈中的执行是随着线程的变化而变化的
2、方法区和堆则是在虚拟机启动就会创建,并且一直存在,直至虚拟机停止
3、随着不断的创建对象,堆内存一定是越来越大,达到一定程度就会进行垃圾回收

那么此刻堆内存的设计就尤为重要了,jvm的设计者是如何设计堆内存的分配的呢?

方法区、堆在内存中如何分配

在这里插入图片描述
解释一下:
堆主要分为两大区域:1、老年代,2、新生代

  • 新生代中的数据:比如创建一个A 对象,此时会分配在Ende区,然后一直创建,当创建到S对象的时候,我的Ende区满了,已经装不下了,那么此刻就会启动垃圾回收(GC),当GC之后可能会有一些对象还没被使用完,需要保留,那么此时我就会将保留下来(非垃圾)的对象及S0区域的对象全部复制到S1中,这时候我的S0就是空的了,等进行GC的时候,就把保留下的对象及S1中的所有对象复制到S0中,如此反复,也就是说S1,S0这两块区域始终有一个是空的,其Ende区与S1,S1的比例是:8:1:1
  • 老年代数据:当新生代的数据经过了多次GC还存活的,就会放到Old区了或者相对超过了某个阈值的对象也会在Old区。

最后,说明一下垃圾回收的名词,对应的都是什么:
1、Minor GC:新生代
2、Major GC:老年代
3、Full GC:新生代+老年代

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值