快速入门JVM

Posted 奥利奥的笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速入门JVM相关的知识,希望对你有一定的参考价值。

    

    本文将介绍运行时数据区的java栈(java stack)和堆(heap)。介绍栈管运行,堆管存储。介绍栈、堆、方法区的交互。简单介绍GC的发生。

快速入门JVM(二)

    在数据结构中,队列和栈是必须要掌握的。队列最显著的特点是先进先出(FIFO)。栈的特点是先进后出(FILO)。


栈(Stack)

    栈也叫栈内存,主管java程序运行,是在线程创建时创建,他的生命周期跟随线程的生命周期,线程结束占栈内存就释放。栈内存是线程私有。对于栈来说不存在垃圾回收问题。在栈内存中会分配8种基本类型的变量、对象的引用变量和实例方法。

    栈的大小和具体的JVM实现有关。


栈帧

    栈中的数据都是以栈帧的格式存在,栈帧是一个内存区块,是一个有关方法和运行数据的数据集。每一次调用函数,都会在调用栈上维护一个的独立的栈帧。一个栈帧一般包括:

    本地变量:包括输入参数、输出参数以及方法内的变量;

    栈操作:入栈、出栈操作;

    每一个方法从调用直至执行完毕的过程,对应着一个栈帧在虚拟机中入栈到出栈的过程。


栈帧的应用过程

    执行下列代码:

public class testStackFrame {  public void method2(){  System.out.println("栈帧2建立。。");  System.out.println("栈帧2入栈。。");  System.out.println("方法2执行。。");  System.out.println("方法2执行完成。。");  System.out.println("栈帧2出栈。。");  }

@Test public void method1() { System.out.println("栈帧1建立。。"); System.out.println("栈帧1入栈。。"); method2(); System.out.println("方法1继续执行。。"); System.out.println("方法1执行完成。。"); System.out.println("栈帧2出栈。。"); } }

    输出如下:

快速入门JVM(二)

    对应到栈的操作如下图所示,在一个栈中有两个栈帧,具体过程是:

  1. 栈帧1是被最先调用的方法,先入栈;

  2. 方法1调用了方法2,栈帧2入栈处于栈顶的位置;

  3. 方法2执行完毕,栈帧2弹出;

  4. 方法1等调用的方法2执行完毕之后,继续执行完成,栈帧1弹出;

  5. 线程结束,栈释放。


快速入门JVM(二)


StackOverFlowError

    执行下列代码:    

public class TestStackOverFlowError {  public void method2(){  System.out.println("栈帧2建立。。");  System.out.println("栈帧2入栈。。");  System.out.println("方法2执行。。");  method2();  System.out.println("方法2执行完成。。");  System.out.println("栈帧2出栈。。");  }

@Test public void method1() { System.out.println("栈帧1建立。。"); System.out.println("栈帧1入栈。。"); method2(); System.out.println("方法1继续执行。。"); System.out.println("方法1执行完成。。"); System.out.println("栈帧2出栈。。"); } }

    会报一个如下图的错误,原因是method2不断调用method2,不断建立栈帧,超出了java栈的空间大小,发生error。

快速入门JVM(二)


栈、堆、方法区的交互关系

快速入门JVM(二)

JVM具体执行流程

看如下代码:

public class TestJVM {   public static void main(String[] args) {  Chinese chinese1 = new Chinese("奥利奥", 24, 6000.0, '男');  Chinese chinese2 = new Chinese();  System.out.println(chinese1.getAge());  } }

       系统启动一个java虚拟机进程,这个进程首先加载TestJVM.class文件,读取这个文件中的二进制数据,然后把TestJVM这个类的类信息放到运行时数据区的方法区。即类的加载。

        接着,JVM定位到TestJVM中的main(),开始执行他的指令。main()的第一句是

Chinese chinese1 = new Chinese("奥利奥", 24, 6000.0, '男');

就是让JVM创建一个Chinese实例,使用chinese1来引用这个实例。

JVM一看,要建立实例了,直接去方法区(方法区存放了已加载类的结构信息)找,这时候一定找不到,因为还没加载Chinese,既然没加载,那就加载吧,于是把Chinese的类信息存放到方法区。

        JVM继续执行下一条指令,在堆区继续创建另一个类Chinese的实例,然后执行打印操作。当JVM执行chinese1.getAge() ,JVM根据局部变量chinese1持有的引用,定位到堆中类Chinese的实例,再根据类Chinese的实例持有的引用,定位到方法区类Chinese的结构信息,从而获取到getAge()成员方法的字节码,接着执行该成员方法包含的指令。


堆(Heap)

    一个JVM实例只有一个堆内存,所有线程共享。堆内存大小可以调节。类加载器读取了类文件之后,需要把类、方法、常量、变量放到堆内存中,保存所有引用类型的真实信息,方便执行器执行。

    堆内存分为如图三部分:

快速入门JVM(二)


新生区

        新生区是类的诞生、成长、消亡的区域。一个类在这路产生、应用,最后被垃圾回收器收集,结束生命。

        新生区又分为两部分:伊甸园(Eden)区和幸存者(Survivor)区。所有类都是在伊甸园区被new出来;幸存者区又分幸存0区(S0或者from区)和幸存1区(S1或者to区)。

        新生区占堆内存1/3。其中伊甸园区占8/10,S0和S1各占1/10。


Minor GC、Full GC和OOM

    当伊甸园区的空间被用完时,程序又需要创建对象,这时候就会触发JVM的垃圾回收器对伊甸园区进行垃圾回收。这里的垃圾回收指的是轻量级的垃圾回收(Minor GC)。会将伊甸园区中不被其他对象所引用的对象进行销毁。然后将剩余对象移动到幸存0区。

    若S0也满了,再对该区进行垃圾回收,然后将剩余移动到S1。

    若S1也满了,就移动到养老区。

    若养老区也满了,会进行对养老区的垃圾回收,这里的垃圾回收是Full GC。

    若多次Full GC之后,依然无法进行对象的创建保存,就会产生OOM异常。


Java heap space

    如果出现java.lang.OutOfMemoryError: Java heap space异常,说明java虚拟机堆内存不够,原因有二:

    一是java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整;

    二是代码中创建了大量大对象,并且长时间不能被垃圾收集器回收(即存在被引用)。


Minor GC的过程

    Minor GC的过程就是复制->清空->互换的过程。

    首先,当伊甸园区满的时候触发第一次GC,把还活着的对象拷贝到from区;

    当伊甸园区再一次触发GC时,会扫描伊甸园区和from区,对这两个区域进行垃圾回收,经过这次回收还活着的对象,会直接复制到to区,并把这些对象年龄加1。如果这时候有对象的年龄达到老年的标准,则复制到老年区;

然后清空伊甸园区和from区中的对象,即复制之后清空;

    最后,to区和from区互换,原to区称为下一次GC时的from区,即谁空谁是to区;

    部分对象会在from和to之间复制来复制去。如此交换15次之后,最终如果还存活,就存入到老年区。

    15次由JVM参数MaxTenuringThreshold决定,该参数默认为15。

    简单讲就是,将eden,from复制到to;清空eden,from;from 和to互换。


永久代

    永久代对应的是是方法区。在上一个文章里说过,方法区是一种规范,在jdk7 之前的实现是永久代,在jdk8之后实现是元空间。

    实际讲,方法区和堆一样,是所有线程共享的内存区域。虽然JVM将方法区描述为堆的一个逻辑部分,但他却还有一个别名叫Non-heap(非堆),目的就是和堆分开。

    永久存储区是一个常驻内存区域,用于存放jdk自身所携带的类和接口的元数据,存储的是运行环境必须的类信息,该部分区域的数据是不会被垃圾回收器回收的,只有关闭JVM才会释放此区域占用的内存。

20191124



以上是关于快速入门JVM的主要内容,如果未能解决你的问题,请参考以下文章

JVM快速入门(图解超级详细通俗易懂)

快速入门JVM

JAVA快速入门总结

B站狂神说Java笔记-JVM快速入门篇

java 简单的代码片段,展示如何将javaagent附加到运行JVM进程

从JVM的角度看JAVA代码--代码优化