4、运行时数据区
4.1、运行时数据区介绍

运行时数据区也就是JVM在运⾏时产生的数据存放的区域,这块区域就是JVM的内存区域,也称为JVM的内存模型——JMM
-  堆空间(线程共享):存放new出来的对象 
-  元空间(线程共享):存放类元信息、类的模版、常量池、静态部分 
-  线程栈(线程独享):⽅法的栈帧 
-  本地⽅法区(线程独享):本地⽅法产⽣的数据 
-  程序计数器(线程独享):配合执⾏引擎来执⾏指令 
4.2、程序在执行时运行时数据区中的内存变化
⾸先,在程序的.class目录内执行如下命令,查看程序具体的汇编指令
javap -c JVMAnalyze
得到结果:
Compiled from "JVMAnalyze.java"
public class com.qf.jvm.JVMAnalyze {
  public com.qf.jvm.JVMAnalyze();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
  public int add();
    Code:
       0: bipush        10
       2: istore_1
       3: bipush        20
       5: istore_2
       6: iload_1
       7: iload_2
       8: iadd
       9: bipush        10
      11: imul
      12: istore_3
      13: iload_3
      14: ireturn
  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/qf/jvm/JVMAnalyze
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method add:()I
      12: istore_2
      13: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      16: iload_2
      17: invokevirtual #6                  // Method java/io/PrintStream.println:(I)V
      20: return
}
通过指令,JMM内存发⽣了如下变化:

-  线程栈:执行⼀个方法就会在线程栈中创建⼀个栈帧。 
-  栈帧包含如下四个内容: - 局部变量表:存放方法中的局部变量
- 操作数栈:用来存放方法中要操作的数据
- 动态链接:存放方法名和方法内容的映射关系,通过方法名找到方法内容
- 方法出口:记录方法执行完后调用此方法的位置。
 
5、对象的创建流程
5.1、对象创建流程

5.2、类加载校验
校验该类是否已被加载。主要是检查常量池中是否存在该类的类元信息。如果没有,则需要进⾏加载。
5.3、分配内存
为对象分配内存。具体的分配策略如下:
-  Bump the Pointer(指针碰撞):如果内存空间的分配是绝对规整的,则JVM记录当前剩余内存的指针,在已用内存分配 
-  Free List(空闲列表):如果内存空间的分配不规整,那么JVM会维护⼀个可用内存空间的列表用于分配。 
对象并发分配存在的问题:
-  Compare And Swap:自旋分配,如果并发分配失败则重试分配之后的地址 
-  Thread Local Allocation Buffer(TLAB):本地线程分配缓冲,JVM为每个线程分配⼀块空间,每个线程在自己的空间中创建对象(jdk8默认使⽤,之前版本需要通过-XX:+UseTLAB开启) 
5.4、设置初值
根据数据类型,为对象空间赋初始化值。
5.5、设置对象头
为对象设置对象头信息,对象头信息包含以下内容:类元信息、对象哈希码、对象年龄、锁状态标志等。
- 对象头中的Mark Work 字段(32位)

- 对象头中的类型指针(Klass Pointer)
类型指针用于指向元空间当前类的类元信息。比如调用类中的方法,通过类型指针找到元空间中的该类,再找到相应的方法。
开启指针压缩后,类型指针只用4个字节存储,否则需要8个字节存储
- 指针压缩
过大的对象地址,会占⽤更大的带宽和增加GC的压力。
对象中指向其他对象所使⽤的指针:8字节被压缩成4字节。 最早的机器是32位,最大支持内存 2的32次方=4G。现在是64位,2的64次⽅可以表示N个T的内存。内存32G即等于2的35次方。如果内存是32G的话,用35位表示内存地址,这样过于浪费。如果把35位的数据,根据算法,压缩成32位的数据(也就是4个字节)。在保存时用4个字节,再使用时使用8个字节。之前用35位保存内存地址,就可以用32位保存。这样8个字节的对象,实际上使用32位来保存,这样64位就能表示2个对象。
如果内存⼤于32G,指针压缩会失效,会强制使用64位来表示对象地址。因此jvm堆内存最好不要大于32G。
Jdk1.6之后默认开启指针压缩,可通过配置jvm参数关闭指针要锁 -XX:-UseCompressedOops
示例代码:
package com.qf.jvm;
import org.openjdk.jol.info.ClassLayout;
import java.lang.String;
/**
 * 对象指针压缩
 * @author Thor
 * @公众号 Java架构栈
 */
public class ObjectLengthAnalyze {
    public static void main(String[] args) {
        ClassLayout classLayout = ClassLayout.parseInstance(new A());
        System.out.println(classLayout.toPrintable());
    }
    static class A{
        int num;
        String name;
    }
}
关闭指针压缩:

开启指针压缩:

5.6、执行init方法
为对象中的属性赋值和执⾏构造方法。
本文章参考B站 千锋教育JVM全套教程(含jvm调优、jvm虚拟机、jvm面试题、jvm源码详解)系统玩转java虚拟机全程干货无废话,仅供个人学习使用,部分内容为本人自己见解,与千锋教育无关。



















