Java Visual Machine

本文详细阐述了Java虚拟机中的类加载子系统,包括类加载的三个阶段、类加载器的分类、双亲委派机制,以及运行时数据区(如PCRegister、JVMStacks、Heap)的结构和管理。此外,文章还讲解了垃圾收集的原理、策略和评估指标,以及常见的垃圾回收器。
摘要由CSDN通过智能技术生成

一. 类加载子系统

类加载子系统负责加载class文件,ClassLoader只负责类的加载,加载类的信息放在方法区

1. 加载类的三个阶段

①加载阶段

在加载阶段在内存中生成代表这个类的Java.lang.class对象,作为方法区各种数据的访问入口

②链接阶段

③初始化阶段

2. 类加载器的分类

Hostpot虚拟机实现的类加载器可分为三层,分别是 :

  • Bootstrap Class Loader(引导类加载器)
  • Extension Class Loader(扩展类加载器)
  • System Class Loader / App Class loader(系统类加载器)

java核心的类库使用引导类加载器 用户自定义的类默认使用系统类加载器

引导类加载器由c/c++实现,没有继承java.lang.ClassLoader,没有父类加载器

Bootstrap Class Loader 只加载报名为java java sun 等开头的类

3. 双亲委派机制

双亲委派机制的优点:

  • 避免类的重复加载
  • 保护程序安全,防止核心API被随意篡改

在jvm中表示两个Class对象是否为同一个类的两个必要条件:

  • 类的完整类名必须一致

  • 加载这个类的ClassLoader实例对象必须为同一对象

4. 类的主动使用和被动使用

二. 运行时数据区

1. Program Counter Register

  • PC Register用来存储指向对应线程的下一条指令地址,由执行引擎读取下一条指令

  • 在JVM规范中,每个线程都有自己的PC Counter Register,是线程私有的,生命周期与线程生命周期一致

  • 是程序控制流的指示器,分支、循环、异常处理、线程恢复都依赖PC Counter Register

  • 是java虚拟机规范中没有规定任何OutOfMemoryError情况的区域

  • JVM中的PC Counter Register 是对物理PC寄存器的一种抽象模拟

2. JVM Stacks

每个线程创建时,都会创建一个java虚拟机栈,内部保存着一个个栈帧,一个栈帧对应一次方法调用,生命周期与线程一致

①虚拟机栈的大小

设置虚拟机栈的大小 -Xss

② 栈内部细节

  • 虚拟机栈的最小单位是栈帧,方法执行时的数据在栈帧中保存
  • 每个栈帧中存储着

        ⑴ 局部变量表

局部变量表中的基本单位是Slot(槽),里面存放着编译器可知的8种基本数据类型,引用类 型,returnAddress类型的变量.在局部变量表中,32位以内的类型只占用一个slot(包括returnAddress类型),64位数据类型占用两个slot,byte、short、char存储前也会被转换为int类型,boolean也会被转换为int类型,0表示false,非0表示true,如果当前栈帧是由构造方法或者实例方法创建的,那么该对象的引用会存放在index为0的slot处,栈帧中局部变量表的槽位可以复用,比如变量超过它的作用域时

        ⑵操作数栈

        ⑶动态链接

        ⑷ 方法返回地址

                方法返回地址中存放着调用该方法的PC Register 的值,

        一个方法有两种结束方式:正常执行完成返回 出现异常非正常退出

        正常执行返回时:调用该方法的PC  Register的地址值作为返回地址

        通过异常退出时:返回地址需要通过异常表来确定

        ⑸ 一些附加信息

3. Native Method Stacks

4. Heap

①堆结构

② 堆空间大小设置

-Xms 用于表示堆区的起始内存,等价与 -XX:InitialHeapSize 默认为电脑内存 / 64

-Xmx 用于表示堆区的最大内存,等价与 -XX:MaxHeapSize 默认为电脑内存 / 4

-XX:NewRatio 配置老年代相对新生代在堆结构中的占比 默认为2

-XX:SurvivorRatio 配置新生代中Eden区与Survivor区的比例

-XX:+PrintFlagsInitial 查看所有参数的默认值

-XX:+PrintFlagsFinal 查看所有参数的最终值

-XX:MaxTenuringThreshold 设置新生代垃圾最大年龄

-XX:+PrintGCDetails 输出详细的GC处理日志

-XX:+PrintGC  -verbose:gc 打印gc简要信息

-XX:HandlePromotionFailure 是否设置空间分配担保

③对象在堆区的分配过程

④ TLAB

⑤逃逸分析

5. Method Area

方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据

方法区逻辑上属于堆的一部分,但是为了与 堆 进行区分,通常又叫 非堆。

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

  • 方法区的基本概念

方法区是虚拟机规范的一部分而非实现

在JDK1.7中 方法区的实现是永久代(PermGen)

而在JDK1.8中 方法区的实现是元数据区(Metaspace)

6. GC

        ①垃圾收集分类: 部分收集和整堆收集

        ②Minor GC的分配担保策略

7. JIT

  • 一个被多次调用的方法,或者一个方法体内部循环次数较多的循环体被称为"热点代码",JVM通过JIT编译器将热点代码编译为本地机器指令。由于这种编译方式发生在方法执行过程中,因此也被称为栈上替换OSR(On Stack Replacement)
  • HotSpot采用基于计数器的热点探测功能对热点代码进行探测
  • HotSpot会为每一个方法建立2个不同类型的计数器:方法调用计数器(Invocation Counter)回边计数器(Back Edge Counter)
  • JIT编译阈值: Client模式下 1500次, Server模式下 1000次
  • 通过-XX:CompileThreshold设定JIT编译
  • 默认情况下,如果一个方法或方法体在一段时间内没有达到被JIT编译的阈值,那么它的方法调用计数器会减少一半,这个过程叫方法热度衰减 ,这段时间叫半衰期
  • 使用-XX:CounterHalfLifeTime 来设置半衰期的时间,单位为s。使用-XX:UseCounterDecay 来关闭热度衰减
  • 默认情况下,HotSpot VM 采用解释器和即时编译器并存的架构,也可以通过命令显示的指定运行时完全采用解释器执行或采用完全即时编译器

        -Xint  完全使用解释器执行程序

        -Xcomp 完全使用即时编译器执行程序, 如果即时编译出现问题,解释器会介入执行

        -Xmixed 采用混合模式共同执行

  • 在HotSpot VM内嵌了两个JIT编译器,分别为Client Compiler(C1编译器) 和 Server Compiler(C2编译器)

        -clinet 指定JVM以client模式运行,并使用C1编译器,C1编译器会对字节码进行简单和可靠的优化,耗时短,编译速度快

        -server 指定JVM以server模式运行,并使用C2编译器,c2编译器耗时较长,优化的代码执行效率更高

8. 对象内存布局

 

三.垃圾收集

1. 垃圾回收的两个阶段

①标记阶段

标记阶段主要来判断对象是否存活,主要涉及的算法有

  • 引用计数算法

  • 可达性分析算法

        可达性分析算法以根对象集合(GC Roots)为起始点,按照从上至下的方式搜索被跟对象集合所连接的目标对象是否可达,使用可达性分析算法后存活对象会被根对象集合直接或间接连接着,搜索所走过的路径称为引用链(Reference Chain),如果目标对象没有任何引用链相连,则不可达,意味着对象已经死亡。

        Java虚拟机中,GC Roots 包括以下对象:

  • 虚拟机栈引用的对象
  • 本地方法栈内JNI引用的对象
  • 方法区类静态属性引用的对象
  • 方法区常量引用的对象
  • 所有被同步锁Synchronized持有的对象
  • Java虚拟机内部的引用

②清除阶段

清除阶段将无用的对象进行清理,涉及的算法有:

  • 标记清除(Mark-Sweep)算法

        将不可达对象地址保存到空闲地址列表,下次分配对象是进行覆盖

        缺点: 效率低,需要暂停用户线程,会产生内存碎片,需要维护一个空闲列

  • 复制算法

       将可用内存划分为两部分,每次只使用一块,垃圾回收时将可达对象复制到未被使用的内存块,清除正在使用的内存块

         优点: 复制后保证内存空间的连续性,不会产生大量内存碎片

        缺点: 需要两倍的内存空间

复制算法的高效性建立在存活对象少、垃圾对象多的前提下

  • 标记压缩算法

2. 分代收集

3. 增量收集

4. 分区收集

5. 对象的finaliztion机制

6. 内存溢出和泄露

  • 内存溢出

  • 内存泄露

7. 垃圾回收器

①分类

垃圾回收器按线程数可分为: 串行垃圾回收器并行垃圾回收器(都是独占式垃圾回收器)

串行垃圾回收器: 单个线程执行垃圾回收操作

并行垃圾回收器: 多个线程执行垃圾回收操作

在CPU处理器或者较小内存空间等硬件平台串行回收器的性能表现可以超过并行回收器和并发回收器

按工作模式分,可以分为 并发式垃圾回收器(回收线程和工作线程并发运行) 和 独占式垃圾回收器(只有回收线程运行)

按碎片处理方式分,可以分为 压缩式垃圾回收器 和 非压缩式垃圾回收器

②评估GC的性能指标

  • 吞吐量: 运行用户代码的时间占总运行时间的比例.

  • 垃圾收集开销: 垃圾收集的补数,垃圾收集的时间占总运行时间的比例.

  • 暂停时间: 执行垃圾收集时,程序的工作线程被暂停的时间.

  • 收集频率: 相对于应用程序的执行,收集操作发生的频率.

  • 内存占用: java堆区所占的内存大小.

  • 快速: 一个对象从诞生到被回收所经历的时间.

③7种经典的收集器与垃圾分代之间的关系

④ 查看默认的垃圾回收器

⑤Serial GC

  • HotSpot中Client模式下默认新生代垃圾收集器
  • 采用复制算法、串行回收、和"Stop-The-World"机制

⑥Serial Old GC

  • HotSpot中Client模式下默认老年代垃圾收集器
  • Serial Old 收集器也采用串行回收和"Stop the world" 机制,不过在内存回收算法中使用标记-压缩算法
  • Serial Old 在Server模式下主要有两个用途

        1. 与新生代Parallel Scavenge配合使用

        2. 作为老年代CMS收集器的后备方案

  •      使用-XX:+UseSerialGC 可以指定年轻代和老年代都使用串行收集器

⑥ParNew GC

⑦Parallel Scavenge GC

G1回收器

面试题

1. 使用PC Counter Register 存储字节码指令地址有什么用呢?

2. PC Counter Register 为什么设为线程私有?

3. 举例栈溢出的情况?

4. 调整栈的大小,就能保证不出现溢出吗?

5. 分配的栈内存越大越好吗?

6. 垃圾回收是否会涉及到虚拟机栈?

7. 方法中定义的局部变量是否线程安全?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值