欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

面试必会系列 - 1.7 JVM 内存模型

程序员文章站 2022-05-04 19:45:54
对象内存布局new Object() 对象在内存中占多少字节?16字节8 字节(MarkWord,固定大小)4 字节(开启压缩时的对象指针 ClassPointer,指向你的对象 TT.class)0 字节 Instance data,要看你的对象有多少成员变量4 字节 padding(对齐,要被 8 整除)...

本文已收录至 Github(MD-Notes),若博客中图片模糊或打不开,可以来我的 Github 仓库,包含了完整图文https://github.com/HanquanHq/MD-Notes,涵盖了互联网大厂面试必问的知识点,讲解透彻,长期更新中,欢迎一起学习探讨 ~

更多内容,可以访问:

面试必会系列专栏https://blog.csdn.net/sinat_42483341/category_10300357.html
操作系统系列专栏https://blog.csdn.net/sinat_42483341/category_10519484.html


JVM

查看 JVM 启动默认参数:java -XX:+PrintCommandLineFlags -version

java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=266536512 -XX:MaxHeapSize=4264584192 -XX:+PrintCommandLineFlags 
-XX:+UseCompressedClassPointers -XX:+UseCompressedOops 
-XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_251"
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)

对象内存布局

new Object() 对象在内存中占多少字节?16字节

8 字节(MarkWord,固定大小)

4 字节(开启压缩时的对象指针 ClassPointer,指向你的对象 TT.class)

0 字节 Instance data,要看你的对象有多少成员变量

4 字节 padding(对齐,要被 8 整除)

查看对象的内存布局工具:JOL = Java Object Layout

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
    <dependency>
        <groupId>org.openjdk.jol</groupId>
        <artifactId>jol-core</artifactId>
        <version>0.9</version>
    </dependency>
</dependencies>
public class MyTest {
    public static void main(String[] args) {
        Object o = new Object(); // 不加锁
        o.hashCode();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());

        Object o1 = new Object();
        synchronized (o1) { // 加锁
            System.out.println(ClassLayout.parseInstance(o1).toPrintable());
        }
    }
}

1、不加锁时,对象内存布局如下:

(注意,jdk 11 需要指定 -XX:-UseBiasedLocking 参数,取消偏向锁) 001 表示没有锁

面试必会系列 - 1.7 JVM 内存模型

2、添加 syncronized 之后,如下。下面 00 表示轻量级锁,因为偏向锁未启动,直接升级为了轻量级锁

面试必会系列 - 1.7 JVM 内存模型

3、添加参数,启动偏向锁:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0

面试必会系列 - 1.7 JVM 内存模型

对象头包括什么?JDK 1.8 的实现如下

对象头包括: MarkWord,classpointer,Instance data,padding

**MarkWord 包括:**锁信息、HashCode、GC信息

在 hotspot 源码 jdk8u: markOop.hpp 中,详细的说明了 object header 的布局。理解即可,无需背过。

The markOop describes the header of an object.
Bit-format of an object header (most significant first, big endian layout below):

32 bits:
--------
hash:25 ------------>| age:4    biased_lock:1 lock:2 (normal object)
JavaThread*:23 epoch:2 age:4    biased_lock:1 lock:2 (biased object)
size:32 ------------------------------------------>| (CMS free block)
PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)

64 bits:
--------
unused:25 hash:31 -->| unused:1   age:4    biased_lock:1 lock:2 (normal object)
JavaThread*:54 epoch:2 unused:1   age:4    biased_lock:1 lock:2 (biased object)
PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
size:64 ----------------------------------------------------->| (CMS free block)

unused:25 hash:31 -->| cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && normal object)
JavaThread*:54 epoch:2 cms_free:1 age:4    biased_lock:1 lock:2 (COOPs && biased object)
narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->|(COOPs&&CMSpromotedobject)
unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)

总结 JDK8 中对象的 object header 布局如下(64 位,也就是 8 字节):

要看加的是什么锁的话,先看 markword 的最低两位,是 01 / 00 / 10 / 11

64位 markword 构成

面试必会系列 - 1.7 JVM 内存模型

为什么 GC 年龄默认为 15?因为分代年龄只有 4 bit,可以表示最大的数就是 15

对象分配过程?

见《Java垃圾回收机制.md》- 对象分配过程

面试必会系列 - 1.7 JVM 内存模型

对象怎么定位?

怎么通过 t 找到 new T()

  • 通过句柄池(间接法)
  • 通过直接指针,效率高(HotSpot用的是这种方式),缺点是在GC需要移动对象时reference本身需要被修改

面试必会系列 - 1.7 JVM 内存模型

Java 程序是怎样运行的?

1、首先,通过 Javac 编译器将 .java 转为 JVM 可加载的 .class 字节码文件。

Javac 是由 Java 编写的程序,编译过程可以分为:

① 词法解析,通过空格分割出单词、操作符、控制符等信息,形成 token 信息流,传递给语法解析器。

② 语法解析,把 token 信息流按照 Java 语法规则组装成语法树。

③ 语义分析,检查关键字使用是否合理、类型是否匹配、作用域是否正确等。

④ 字节码生成,将前面各个步骤的信息转换为字节码。

字节码必须通过类加载过程加载到 JVM 后才可以执行,执行有三种模式,解释执行、JIT 编译执行、JIT 编译与解释器混合执行(主流 JVM 默认执行的方式)。混合模式的优势在于解释器在启动时先解释执行,省去编译时间。

2、之后,通过即时编译器 JIT 把字节码文件编译成本地机器码。

Java 程序最初都是通过解释器进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁,就会认定其为"热点代码",虚拟机即时编译器会把它们编译成本地机器码。

3、还可以通过静态的提前编译器 AOT 直接把程序编译成与目标机器指令集相关的二进制代码。

JVM内存模型

class 的生命周期

面试必会系列 - 1.7 JVM 内存模型

  1. 静静躺在磁盘中的 .class 文件
  2. 经过 load,link,initialize,进入 JVM
  3. 经过 JVM 的 run engine,进入 JVM 的运行时数据区
  4. 最终被 GC

Run-time data areas 运行时数据区的组成

我们常说的:栈放方法,堆存对象

堆里面存放的都是一些引用,而栈是我们真正用来执行程序的。可以把每一个方法看做对应一个栈帧。

动态链接:把符号引用(.class文件常量池中的引用)转化为直接引用(指向堆中的对象)

面试必会系列 - 1.7 JVM 内存模型

每个线程有自己独立的 PC,VMS,NMS

线程之间共享 Heap 以及 MethodArea

PC:程序计数器

MethodArea:方法区

类的所有字段和方法字节码,以及一些特殊方法构造函数,接口代码也在这里定义。简单来说,所有定义方法的信息都保存在该区域,静态变量+常量+类信息(构造方法/接口定义)+运行时常量池都存在方法区中

方法区只是对于虚拟机的规范。所有的虚拟机应该有方法区,不同虚拟机方法区的叫法不一样

  • jdk 1.8 之前,HotSpot 使用 PermSpace 永久代 实现方法区
    • 字符串常量位于 Perm Space
    • FGC 不会清理
  • jdk 1.8 及之后:使用 Meta Space 元数据区
    • 字符串常量位于 Heap
    • FGC 会清理

Stacks:栈空间

  • 栈中存放栈帧
    • 局部变量表
    • 操作数栈
    • 动态链接
    • 返回值地址

Heap:堆空间

虚拟机启动时自动分配创建,用于存放对象的实例,几乎所有对象都在堆上分配内存,当对象无法在该空间申请内存时,抛出OOM异常。也是垃圾收集器管理的主要区域。

  • 类实例
  • 为数组分配的空间

DirectMemory:直接内存

  • JVM可以直接访问OS管理的内存,提高效率

    • 零拷贝(不需要拷贝),NIO用到

Run-Time Constant Pool 运行时常量池

线程私有的:是交由 JVM 自动化管理的。我们做的 JVM 调优,是调的堆和方法区。

面试必会系列 - 1.7 JVM 内存模型

面试题:下面输出 i 为多少?

package com.mashibing.jvm.c4_RuntimeDataAreaAndInstructionSet;

public class TestIPulsPlus {
    public static void main(String[] args) {
        int i = 8;
        i = i++;
//        i = ++i;
        System.out.println(i);
    }
}

面试必会系列 - 1.7 JVM 内存模型

本文地址:https://blog.csdn.net/sinat_42483341/article/details/110177874

相关标签: 面试必背系列