初识JVM(二)

1. 虚拟机栈

虚拟机栈识线程私有的,每个线程运行的时候都会创建一个对应的虚拟机栈。虚拟机栈中存储的是栈帧,每一个方法的调用都表示一个栈帧压栈的过程。那么,栈帧中都存储了什么呢?

1.1 栈帧的结构

  • 局部变量表

局部变量表中存储了方法中定义的局部变量以及方法的参数。
局部变量表中的变量不可直接使用,如需要使用的话,必须通过相关指令将其加载至操作数栈中作为操作数使用。

  • 操作数栈

以压栈和出栈的方式存储操作数

  • 动态链接

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态 连接(Dynamic Linking)

  • 方法返回地址

当一个方法开始执行后,只有两种方式可以退出,一种是遇到方法返回的字节码指令;一种是遇见异常,并且 这个异常没有在方法体内得到处理

1.2 虚拟机栈存储的数据图示:

栈帧.png

2.栈、堆、方法区的关系

2.1 栈指向堆

例如某个方法中有Object obj = new Object();
obj存储在局部变量表中,new Object()会分配一个对象在堆中。obj为这个对象的引用。

2.2 方法区指向堆

我们知道方法区用来存放静态变量,常量等数据。下面这种情况是方法区指向堆的典型的例子。

private static Object obj = new Object();

2.3 堆指向方法区

方法区中存放了类信息,堆中存放被创建的对象。那么对象怎么知道是被哪个类创建出来的呢?
举例:

堆中有两个Person对象,但是一个是A路径下的Person类创建的,另一个是B路径下的Person类创建的,这两个Person对象怎么知道自己是被哪个class创建的呢?

每个对象在被创建出来的时候都会有一个对象头,对象头会指向对象对应的类元数据的内存地址。

2.4 Java对象内存布局

对象内存布局.png

3. 堆

3.1 堆介绍

堆区分为两大块,一个是Old区,一个是Young区。
Young区分为两大块,一个是Survivor区(S0+S1),一块是Eden区。 Eden:S0:S1=8:1:1
S0和S1一样大,也可以叫From和To。

3.2 Yong区

Yong区包含Eden区和两块Survivor区。

一般情况下新建的对象被分配在Eden区,当Eden区空间不足的时候,触发Minor GC进行垃圾清理,垃圾回收之后会使存活的对象年龄加1。

同一时刻Survivor区只有一个是被使用的。在进行垃圾回收的时候,eden区存活的对象会被复制到to区,from区中存活的对象有两个去处:from区中对象年龄达到阈值,对象被移动到Old区。from区中的对象未达到阈值,移动到to区。清理之后,之后to区有存活的对象,这时交换from和to区,保证to区是空的。Minor GC一直重复着这个过程。

值得一提的是,当to区的大小不足以存放存活的对象,并且对象年龄没有到达阈值怎么办?此时会触发担保机制,从Old区借一些空间给survivor区用于存放对象。

3.3 Old区

从上面的分析可以看出,一般Old区都是年龄比较大的对象,或者相对超过了某个阈值的对象。在Old区也会有GC的操作,Old区的GC我们称作为Major GC。

3.4 对象创建过程中的内存分配

从上面的分析中可以看出,对象的创建需要分配空间,空间不够就要触发GC。下图展示类给一个对象分配内存的各种情况。


对象创建的坎坷经历.png

4. 一些问题

4.1 为什么要有survivor区

如果没有Survivor,Eden区每进行一次Minor GC,并且没有年龄限制的话,存活的对象就会被送到老年代。
这样一来,老年代很快被填满,触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做触发了Full GC)。老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。
执行时间长有什么坏处?频发的Full GC消耗的时间很长,会影响大型程序的执行和响应速度。
假如增加老年代空间,更多存活对象才能填满老年代。虽然降低Full GC频率,但是随着老年代空间加大,一旦发生Full GC,执行所需要的时间更长。
假如减少老年代空间,虽然Full GC所需时间减少,但是老年代很快被存活对象填满,Full GC频率增加。
所以Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。

4.2 为什么要有两个survivor区

Yong区使用复制算法进行垃圾回收。如果只有一个survivor区的话,每次eden区满类之后触发GC,就会把存活的对象放到survivor区,此时survivor区中也需要进行垃圾回收,那survivor区中存活的对象放在哪里呢?此时再设置一个survivor区,并且保证两个survivor区在同一时刻只有一个被使用,这样即提高了垃圾回收的效率,也解决了空间碎片的问题。

4.3 如何理解full GC、Major GC和Minor GC

Minor GC:新生代
Major GC:老年代
Full GC:新生代+老年代

5. 验证一下

打来jdk提供的jvisualvm工具,安装了一下插件后即可监控本地的java进程。可以印证虚拟机中堆空间分为Old区,Eden区和两个S区。


垃圾回收.png

你可能感兴趣的:(初识JVM(二))