当前位置 博文首页 > 文章内容

    JAVA JVM运行时数据区详解

    作者:21344 栏目:未分类 时间:2021-09-02 14:44:51

    一、前言

    这是JVM系列文章的第三篇,这篇文章将对整个JVM运行时数据区和GC垃圾回收详细的介绍。这部分也算是JVM的核心内容了。

    二、运行时数据区整体概架构

    在这里插入图片描述

    以下是自己的一句话总结:

    分为线程私有和线程共享的两大类,其中程序计数器、虚拟机栈、本地方法栈是属于线程私有的,堆内存及方法区内存是线程共享的。程序计数器主要是记录字节码指令,CPU上下文切换线程,从一个线程切换到另一个线程,需要知道线程执行到哪一步,所以记录这个指令就是很有必要的,程序计数器无OOM和GC的发生。虚拟机栈里面是一个个栈帧,每一个栈帧对应着每一个方法,栈帧又是由局部变量表、操作数栈、方法返回值地址、动态链接组成。虚拟机栈可能会发生栈溢出异常,即starkoverflow本地方法栈是存放本地方法相关的东西;堆是一块很大的空间,整体分为2大块,新生代和老年代,新生代又分了Eden区、S0区、S1区,垃圾回收主要发生在新生代,每一个区对应不同的垃圾回收算法;方法区保存的是一些常量、类的基本信息等,方法区对应的实现在JDK7中是永久代,在JDK8中是元空间。

    三、程序计数器

    用来储存指向下一条指令的地址,是线程私有的,生命周期和线程的生命周期一致。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    四、虚拟机栈

    虚拟机栈是线程私有的,内部保存一个个栈帧,每一个栈帧对应一个Java方法的调用,生命周期和线程的生命周期保持一致。先来看看栈的特点。

    在这里插入图片描述

    1、栈的特点

    栈是运行时的单位,而堆是存储的单位。栈的特点是先进后出,后进先出。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    可以通过参数-Xss来设置栈空间大小

    2、栈帧的内部结构

    在这里插入图片描述

    3、局部变量表

    是一个数字数组,主要用于存储方法参数和定义在方法内的局部变量,这些数据类型包括各类基本数据类型,对象引用等,所需的容量大小是在编译期确定下来的,在方法运行期间是不会改变局部变量表大小的。

    关于Slot的理解:

    在这里插入图片描述

    静态变量和局部变量的区别:

    在这里插入图片描述

    总结:

    在栈帧中,与性能关系最为密切的就是局部变量表,在方法执行时,虚拟机使用局部变量表完成完成方法的传递,局部变量表中的数据也是可达性分析中的GC Root,如果一个对象在局部变量表中还有引用,那么根绝可达性分析算法,这个变量就不属于垃圾对象,是不会被GC回收的。

    4、操作数栈

    操作数栈是栈中栈,也可称为表达式栈,在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈和出栈。主要用于保存计算过程的中间结果。操作数栈,可以看成是临时寄存器,计算过程中变量的临时保存

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    5、动态链接

    在这里插入图片描述

    在这里插入图片描述

    方法重写的本质

    在这里插入图片描述

    6、方法返回地址

    存放调用该方法的PC寄存器的值

    在这里插入图片描述

    五、本地方法栈

    管理本地native本地方法,是线程私有的,所谓的本地方法,其实就是一些非Java语言写的代码,这部分代码甚至可以和操作系统CPU进行打交道。

    六、堆

    堆是内存管理的核心区域,是线程共享的,属于JVM级别,也就是一个JVM实例就会有一个堆空间,注意的是虽然堆整体上是线程共享的,但是在内部有一小块空间是线程私有的缓存区TLAB。

    几乎所有的对象实例都是在堆中,堆是GC垃圾回收的重点区域。堆整体可以分为新生代和老年代,新生代又分为Eden区和S0和S1区。

    在这里插入图片描述

    新生代和老年代的比例是1:2,Eden区和s0,s1区所占空间比例是8:1:1

    1、设置堆大小的参数

    -Xms:用于表示堆区的起始内存,默认情况下,占物理内存大小的64分之一。

    -Xmx用于表示堆区的最大内存,默认情况下,占物理内存的四分之一。

    通常起始内存和最大内存两个参数设置成一样,目的是为了GC清理完堆区内存后不需要重新分隔
    计算堆区的大小,从而提高性能。
    查看设置的参数:
    方式一:jps(查看进程)  
                jstat -gc 进程id
    方式二:-xx:+printGCDetails
    

    2、对象分配过程

    在这里插入图片描述

    这里s0和s1谁是空的谁就是to,年龄计数器阈值是15,YGC是在Eden区满的时候会触发,s0和s1满的时候不会触发YGC,YGC会将s区以及伊甸园区一起GC

    关于垃圾回收,频繁在新生区收集,很少在养老区收集,几乎不在永久区/元空间收集。

    在这里插入图片描述

    Visualvm是JVM常用调优工具,在JDK的bin下就可以打开
    

    3、堆中的GC

    在这里插入图片描述

    年轻代(Minor GC)触发机制

    在这里插入图片描述

    老年代GC(Major GC/Full GC)触发机制

    Full GC 触发机制

    在这里插入图片描述

    4、内存分配策略

    在这里插入图片描述

    5、什么是TLAB

    在这里插入图片描述

    在这里插入图片描述

    TLAB表明堆不一定是共享的。

    6、堆是分配对象存储的唯一选择吗?

    如果经过逃逸分析,一个对象并没有逃逸出方法的话,那么就有可能被优化成栈上分配。

    逃逸分析手段:

    在这里插入图片描述

    在这里插入图片描述

    注意:JDK6U23版本后,HotSpot默认已经开启逃逸分析。所以我们得出一个结论,开发中能使用局部变量的,就不要使用在方法外定义。JDK7后字符串常量池和静态变量存储在堆中

    七、方法区

    方法区可以看做是一块独立于堆的内存空间,是线程共享的,主要存储类信息、运行时常量池等,也会发生OOM,JDK8前成为永久代,JDK8成为元空间。(元空间和永久代最大的区别是,元空间不再使用JVM内存,而是使用了本地内存技术)

    1、方法区概述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    2、设置方法区内存大小

    在这里插入图片描述

    在这里插入图片描述

    3、如何解决OOM问题?

    在这里插入图片描述

    4、方法区存储什么

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    5、方法区的演进细节

    在这里插入图片描述

    在这里插入图片描述

    6、方法区的GC

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    总结

    本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注IIS7站长之家博文的更多内容!