JVM基础-Hotspot VM相关知识学习

这里写目录标题

    • jdk
    • JVM虚拟机
      • 类的生命周期
      • 类加载的时机
      • 类的双亲委派机制
      • 类的验证
    • java对象
          • Mark Word
          • Klass Pointer
          • 实例数据
          • 对齐数据
    • 字符串常量池
    • 垃圾收集器
      • 1.Serial收集器(串行收集器)
      • cms垃圾算法
      • G1垃圾收集器
          • 与CMS收集器相比, G1收集器的优势:
          • G1收集器的实现原理:

JVM参考文章:

JVM之 方法区、永久代(PermGen space)、元空间(Metaspace)三者的区别

jdk

JRE是java运行时环境而JDK是java开发工具包,JDK包含JRE,但是JRE可以独立安装。

  • JDK:java development kit (java开发工具),JDK 是用于开发 Java 程序的最小环境。

  • JRE:java runtime environment (java运行时环境),是提供给 Java 程序运行的最小环境。

JRE包含了java虚拟机、java基础类库。是使用java语言编写的程序运行所需要的软件环境,是提供给想运行java程序的用户使用的。JDK是程序员使用java语言编写java程序所需的开发工具包,是提供给程序员使用的。运行java程序只需安装JRE。如果需要编写java程序,需要安装JDK。

JVM基础-Hotspot VM相关知识学习_第1张图片

JVM虚拟机

JVM基础-Hotspot VM相关知识学习_第2张图片

  • 内存模型中线程独享的有:线程栈(虚拟机栈)、本地方法栈、程序计数器
  • 内存模型中线程共享的有:堆、方法区(1.7以前名为永久区,1.8后为元空间)
  • 常量池中的对象在1.6以前位于用永久区,1.7以后位于
  • 线程栈的最小单元叫栈帧,组成部分有局部变量表、操作数栈、动态链接、方法出口

JVM基础-Hotspot VM相关知识学习_第3张图片

  • 在方法区里面有一个运行常量池,包含变量和方法,class文件所有的变量和引用作为符号引用,保存在class文件的常量池中。

  • 描述一个方法调用了其他的方法的时候就是通过常量池中指向方法的符号引用来表示。那么动态连接的作用就是将这些符号引用转化成调用方法的直接引用

  • 所以动态链接:将对方法的符号引用 链接成 对调用方法的直接引用

  • 动态链接(虚方法)发生在运行时,静态链接发生在编译时(private\static\final)

  • 重载是静态绑定,重写是动态绑定

JVM知识梳理之二_JVM的常量池

JVM基础-Hotspot VM相关知识学习_第4张图片

类的生命周期

JVM基础-Hotspot VM相关知识学习_第5张图片

类加载的时机

下面5种情况下会导致类初始化,因此必须在发生这5种情况之前对类进行加载。

  • 当虚拟机启动时加载主类。
  • 使用 java . lang . reflect 包的方法对类进行反射调用时,如果类还没有初始化,则需要进行初始化。
  • new 一个类的对象,调用类的静态成员(除了由 final 修饰的常量外)和静态方法,无论是解析执行还是编译执行的情况下,都会在处理 new 、 getstatic 、 putstatic 或 invokestatic 字节码指令时对类进行初始化。在第9章中会介绍使用 new 字节码指令创建对象的过程,其中就会有触发类装载的逻辑判断。
  • 当初始化一个类时,如果其父类没有被初始化,则先初始化其父类。后续在介绍函数 InstanceKlass :: initialize _ impl ()时会看到这个判断逻辑。
  • 使用JDK7的动态语言支持时,如果一个 java . lang . invoke . MethodHandle 对象最后的解析结果是 REF _ getStatic 、 REF _ putStatic 和 REF _ invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先进行初始化。

可以通过调用 ClassLoader 类的 loadClass ()方法装载类,还可以调用 java . lang . Class .
forName ()方法通过反射的方式完成装载类。 loadClass ()方法只是将 Class 文件装载到 HotSpot VM 中,而 forName ()方法会完成类的装载、链接和初始化过程。

类的双亲委派机制

各个类加载器之间并不是继承关系,而是表示工作过程,具体说就是,对于一个加载类的具体请求,首先要委派给自己的父类加载器去加载,只有父类加载器无法完成加载请求时子类加载器才会尝试加载,这就叫"双亲委派"。
JVM基础-Hotspot VM相关知识学习_第6张图片

在这里插入图片描述

类的验证

类在连接过程中会涉及验证。 HotSpot VM 会遵守 Java 虚拟机的规范,对 Class 文件中包含的信息进行合法性验证,以保证 HotSpot VM 的安全。从整体上看,大致进行如下4方面的验证。

  • 文件格式验证:包括魔数和版本号等;
  • 元数据验证:对程序进行语义分析,如是否有父类,是否继承了不被继承的类,是否实现了父类或者接口中所有要求实现的方法;
  • 字节码验证:指令级别的语义验证,如跳转指令不会跳转到方法体以外的代码上;.
  • 符号引用验证:符号引用转化为直接引用的时候,可以看作对类自身以外的信息进行匹配验证,如通过全限定名是否能找到对应的类等。

JVM基础-Hotspot VM相关知识学习_第7张图片
JVM基础-Hotspot VM相关知识学习_第8张图片
JVM基础-Hotspot VM相关知识学习_第9张图片
全网最硬核 JVM TLAB(Thread Local Allocate Buffer) 分析

java对象

JVM基础-Hotspot VM相关知识学习_第10张图片
JVM基础-Hotspot VM相关知识学习_第11张图片

Mark Word

32位Mark Word:
JVM基础-Hotspot VM相关知识学习_第12张图片

64位Mark Word:
JVM基础-Hotspot VM相关知识学习_第13张图片

  • 锁标志位(lock):区分锁状态,11时表示对象待GC回收状态, 只有最后2位锁标识(11)有效。
  • biased_lock:是否偏向锁,由于正常锁和偏向锁的锁标识都是 01,没办法区分,这里引入一位的偏向锁标识位。
  • 分代年龄(age):表示对象被GC的次数,当该次数到达阈值(最大为15,默认为15,CMS垃圾收集器默认为6)的时候,对象就会转移到老年代。
  • 对象的hashcode(hash):运行期间调用System.identityHashCode()来计算,延迟计算,并把结果赋值到这里。当对象加锁后,计算的结果31位不够表示,在偏向锁,轻量锁,重量锁,hashcode会被转移到Monitor中。
  • 偏向锁的线程ID(JavaThread):偏向模式的时候,当某个线程持有对象的时候,对象这里就会被置为该线程的ID。 在后面的操作中,就无需再进行尝试获取锁的动作。
  • epoch:偏向锁在CAS锁操作过程中,偏向性标识,表示对象更偏向哪个锁。
  • ptr_to_lock_record:轻量级锁状态下,指向栈中锁记录的指针。当锁获取是无竞争的时,JVM使用原子操作而不是OS互斥。这种技术称为轻量级锁定。在轻量级锁定的情况下,JVM通过CAS操作在对象的标题字中设置指向锁记录的指针。
  • ptr_to_heavyweight_monitor:重量级锁状态下,指向对象监视器Monitor的指针。如果两个不同的线程同时在同一个对象上竞争,则必须将轻量级锁定升级到Monitor以管理等待的线程。在重量级锁定的情况下,JVM在对象的ptr_to_heavyweight_monitor设置指向Monitor的指针。
Klass Pointer

即类型指针,是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

实例数据

如果对象有属性字段,则这里会有数据信息。如果对象无属性字段,则这里就不会有数据。根据字段类型的不同占不同的字节,例如boolean类型占1个字节,int类型占4个字节等等;

对齐数据

对象可以有对齐数据也可以没有。默认情况下,Java虚拟机堆中对象的起始地址需要对齐至8的倍数。如果一个对象用不到8N个字节则需要对其填充,以此来补齐对象头和实例数据占用内存之后剩余的空间大小。如果对象头和实例数据已经占满了JVM所分配的内存空间,那么就不用再进行对齐填充了。
所有的对象分配的字节总SIZE需要是8的倍数,如果前面的对象头和实例数据占用的总SIZE不满足要求,则通过对齐数据来填满。

为什么要对齐数据?
字段内存对齐的其中一个原因,是让字段只出现在同一CPU的缓存行中。如果字段不是对齐的,那么就有可能出现跨缓存行的字段。也就是说,该字段的读取可能需要替换两个缓存行,而该字段的存储也会同时污染两个缓存行。这两种情况对程序的执行效率而言都是不利的。其实对其填充的最终目的是为了计算机高效寻址。


默认情况下JVM开启了指针压缩,对象A大小情况

  • 【Mark word】为8字节
  • 【Klass Pointer】被压缩为4字节
  • 实例数据:int类型id大小为4字节、String类型name大小为4字节、byte类型b大小为1字节,byte类型对象内对齐3字节、Object类型o大小为4字节
  • 对齐填充:综上数据大小为28字节,数据对齐添加4字节,对象A的实际大小为32字节

new Object() 生成的对象大小为16个字节


手动关闭JVM指针压缩,对象A大小情况

  • 【Mark word】为8字节
  • 【Klass Pointer】为8字节
  • 实例数据:int类型id大小为4字节、String类型name大小为8字节、byte类型b大小为1字节,byte类型对象内对齐3字节、Object类型o大小为8字节
  • 对齐填充:综上数据大小为40字节,数据对齐不用补齐,对象A的实际大小为40字节

Java对象中实例数据发生的间隙填充alignment/padding gap条件是什么?

字符串常量池

JVM基础-Hotspot VM相关知识学习_第14张图片

JVM基础-Hotspot VM相关知识学习_第15张图片

JVM基础-Hotspot VM相关知识学习_第16张图片

JVM基础-Hotspot VM相关知识学习_第17张图片

JVM基础-Hotspot VM相关知识学习_第18张图片

垃圾收集器

JVM基础-Hotspot VM相关知识学习_第19张图片
JVM基础-Hotspot VM相关知识学习_第20张图片

图中的连线表示可以使用连线两端的两种收集器分别收集年轻代和老年代的内存空间,而G1收集器既以收集年轻代的内存空间,也可以收集老年代的内存空间。

1.Serial收集器(串行收集器)

Serial 收集器是一个单线程的收集器,采用"复制"算法。单线程的意义一方面指它
只会使用一个 CPU 或一条收集线程去完成圾收集工作,另一方面指在进行垃圾收集时必须暂停其他的工作线程,直到收集结束。
Serial 收集器的工作过程如图所示。
JVM基础-Hotspot VM相关知识学习_第21张图片

Serial 收集器是一个单线程的收集器,采用"复制"算法。"单线程"并不是说只使
用一个 CPU 或一条收集线程去完成垃圾收集工作,而是指在进行垃圾收集时,必须暂停其他的工作线程,直到收集结束。本章将详细介绍 Serial 垃圾回收的具体实现过程。

cms垃圾算法

JVM基础-Hotspot VM相关知识学习_第22张图片

G1垃圾收集器

JVM基础-Hotspot VM相关知识学习_第23张图片

JVM基础-Hotspot VM相关知识学习_第24张图片

与CMS收集器相比, G1收集器的优势:
  • 基于标记-整理算法, 不会产生大量的内存碎片;
  • 可以更加精确地控制停顿时间, 在不牺牲吞吐量前提下, 实现低停顿垃圾回收.
G1收集器的实现原理:
  • G1收集器能够避免全区域的垃圾收集, 它把堆内存划分为大小固定的几个独立区域, 并跟踪这些区域的垃圾收集进度, 同时在后台维护一个优先级列表, 每次根据所允许的收集时间, 优先回收垃圾最多的区域

JVM基础-Hotspot VM相关知识学习_第25张图片

关于垃圾收集器G1与ZGC

JVM基础-Hotspot VM相关知识学习_第26张图片

  • 默认情况下年轻代所占内存为堆空间的1/3,其中Eden区大小占年轻代空间的80%,s0和S1各占10%; 永久代所占内存为堆空间的2/3

STW的原因?
防止少标或多标(多标产生浮动垃圾

JVM基础-Hotspot VM相关知识学习_第27张图片
1.长期存活的对象进入老年代

幸存区复制一次,则年龄增加1。当对象的年龄达到设定的阈值时,将会晋升到老年代。默认情况下,并行 GC 的年龄阈值为15,并发 GC 的年龄阈值为6。由于 age 只有4位,所以最大值为15,这就是﹣ XX : MaxTenuring Threshold 选项最大值为15的原因。

虚拟机给每个对象定义了一个对象年龄计数器。如果对象在 Eden 空间分配并经过第
一次 YGC 后仍然存活,在将对象移动到 To Survivor 空间后对象年龄会设置为1。对象在 Survivor 空间每熬过一次, YGC 年龄就加一岁,当它的年龄增加到一定程度(默认为15岁)时,就会晋升到老年代中。对象晋升老年代的年龄阈值,可以通过﹣ XX : MaxTenuring - Threshold 选项来设置。 ageTable 类中定义 table _ size 数组的大小为16,由于通过﹣ XX : Max - TenuringThreshold 选项可设置的最大年龄为15,所以数组的大小需要设置为16,因为还需要通过 sizes [0]表示一次都未移动的对象,不过实际上不会统计 sizes [0],因为 sizes [0]
的值一直为0。

2.动态对象年龄判定
为了能更好地适应不同程度的内存状况,虚拟机并不总是要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升到老年代。如果在 Survivor 空间中小于等于某个年龄的所有对象空间的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到 MaxTenuringThreshold 中要求的年龄。因此需要通过 sizes 数组统计年轻代中各个年龄对象的总空间。

3.触发 YGC
大多数情况下,对象直接在年轻代中的 Eden 空间进行分配,如果 Eden 区域没有足够的空间,那么就会触发 YGC ( Minor GC ,年轻代垃圾回收), YGC 处理的区域只有年轻代。下面结合年轻代对象的内存分配看一下触发 YGC 的时机:

  • 新对象会先尝试在栈上分配,如果不行则尝试在 TLAB 中分配,否则再看是否满足大对象条件可以在老年代分配,最后才考虑在 Eden 区申请空间。
  • 如果 Eden 区没有合适的空间,则 HotSpot VM 在进行 YGC 之前会判断老年代最大的可用连续空间是否大于新生代的所有对象的总空间,具体判断流程如下:
    1. 如果大于的话,直接执行 YGC 。
    2. 如果小于,则判断是否开启了 HandlePromotionFailure ,如果没有开启则直接执行
      FGC .
    3. 如果开启了 HandlePromotionFailure , HotSpot VM 会判断老年代的最大连续内存空间是否大于历次晋升的平均内存空间(晋级老年代对象的平均内存空间),如果小于则直接执行 FGC ;如果大于,则执行 YGC 。

对于 HandlePromotionFailure ,我们可以这样理解,在发生 YGC之前,虚拟机会先检查老年代的最大的连续内存空间是否大于新生代的所有对象的总空间,如果这个条件成立,则 YGC是安全的。如果不成立,虚拟机会查看 HandlePromotionFailure设置值是否允许判断失败,如果允许,那么会继续检查老年代最大可用的连续内存空间是否大于历次晋级到老年代对象的平均内存空间,如果大于就尝试一次YGC ,如果小于,或者 Handle - PromotionFailure 不愿承担风险就要进行一次 FGC 。

触发 GC 的流程如图所示。
JVM基础-Hotspot VM相关知识学习_第28张图片
JVM随笔 — 安全点(safe point)与 安全区域( safe region)

JVM基础-Hotspot VM相关知识学习_第29张图片

JVM基础-Hotspot VM相关知识学习_第30张图片

JVM基础-Hotspot VM相关知识学习_第31张图片

JVM基础-Hotspot VM相关知识学习_第32张图片

JVM基础-Hotspot VM相关知识学习_第33张图片

JVM基础-Hotspot VM相关知识学习_第34张图片

你可能感兴趣的:(jvm,学习)