【面试准备】JVM

修订记录时间
首次发布2023.06

一、JVM组成

1.1 整体架构

在这里插入图片描述
在这里插入图片描述

1.2 什么是程序计数器?

程序计时器是每个线程私有的,每个线程一个,保存的是字节码的行号。用来记录当前执行的字节码指令的地址。当这个线程获取到CPU时间片时从这个地址开始。

1.3 Java堆

  • 堆是线程共享的区域,主要用来保存对象实例、数组等,内存不够时就会抛出OutOfMemoryError(OOM)异常。
  • JDK 1.8中堆里分为年轻代+老年代
    • 年轻代分为Eden区和两个大小相同的Survivor区(S0、S1)。
    • 老年代主要保存生命周期长的对象。
  • JDK 1.7和JDK1.8中堆的区别
    • 1.7版本的堆中有一个永久代,用于存储类信息、静态变量、常量、编译后的代码。
    • 1.8版本的堆中移除了永久代,把数据存储到了本地内存的元空间中,防止内存溢出OOM。因为动态类加载的越来越多,可能会使堆的大小不可控,所以挪到本地内存的元空间中来节省堆的空间。

1.4 栈

1. 虚拟机栈

  • 虚拟机栈是线程私有的,是每个线程运行时所需要的内存。一个线程一个栈,一个方法一个栈帧,先进后出。每个线程只有一个活动栈帧,对应当前执行的方法。
  • 栈帧中存储的是参数、局部变量、返回地址等。
  • 垃圾回收涉及栈内存吗?
    • 不涉及。垃圾回收是回收堆。当一个栈帧弹出时,它对应的内存就会被释放。
  • 栈内存分配越大越好吗?
    • 不是。一个栈分配的越大,线程数就会减少。默认一个栈是1M。
  • 方法内的局部变量是线程安全的吗?
    • 可能不是。方法内的局部变量没有超出方法的作用范围就是线程安全的。
    • 入参传入对象、返回对象都可能造成线程安全问题。
  • 什么时候栈会溢出?StackOverflowError
    • 栈帧过多,递归调用。
    • 栈帧过大。
  • 堆和栈的区别
    • 存储内容:堆存实例对象和数组,栈存局部变量和方法调用。
    • 线程共享:堆是线程共享的,栈是线程私有的。
    • 垃圾回收:堆会进行垃圾回收,栈会在栈帧弹出时释放对应的内存。

2. 本地方法栈

  • 线程私有的。和虚拟机栈类似,但虚拟机栈为Java方法服务,本地方法栈为Native方法服务。
  • 在Hotspot JVM中,直接将本地方法栈和虚拟机栈合二为一。

1.5 本地内存

1. 直接内存

  • 是虚拟机的系统内存,不属于JVM的内存结构,也不受JVM管理。
  • 常用于NIO操作,比如读写数据时的数据缓冲区。分配成本高,但读写效率高。

2. 方法区/元空间

  • 保存类信息(Class、Classloader)、运行时常量池(要执行的类名、方法名、字面量等)。
  • 虚拟机启动时创建,关闭时释放。
  • 方法区内存无法满足时,会抛出OutOfMemoryError: Metaspace。
  • JDK 1.7和JDK1.8中堆的区别
    • 1.7版本在堆的永久代中。堆中有一个永久代,用于存储类信息、静态变量、常量、编译后的代码。
    • 1.8版本在本地内存的元空间中。堆中移除了永久代,把数据存储到了本地内存的元空间中,防止内存溢出OOM。因为动态类加载的越来越多,可能会使堆的大小不可控,所以挪到本地内存的元空间中来节省堆的空间。
  • 运行时常量池
    • 常量池可以看作一张表,虚拟机指令根据这张表找到执行的类名、方法名、参数类型、字面量等。
    • 当类被加载时,常量池信息会放入运行时常量池,并把里面的符号地址改为真实地址。

二、类加载器

2.1 什么是类加载器?

JVM只能执行二进制文件,类加载器是将字节码文件加载进JVM的,从而使Java程序得以执行。

2.2 有哪些类加载器?

  1. 启动类加载器 BootStrapClassLoader:用于加载JAVA_HOME/jre/lib目录下的核心类,由C++编写实现。
  2. 扩展类加载器 ExtClassLoader:用于加载JAVA_HOME/jre/lib/ext目录下的类。
  3. 应用类加载器 AppClassLoader:用于加载CLASSPATH下的类,也就是开发者自己写的类。
  4. 自定义类加载器 CustomizeClassLoader:用于实现自定义类的加载规则。

2.3 什么是双亲委派模型?

在这里插入图片描述
加载一个类时,会先委托它上一级的类加载器进行加载,如果上一级的类加载器还有上级,就继续向上委托。如果上级的类加载器无法加载这个类,子加载器就会尝试加载这个类。如果上级的类加载器可以加载这个类,那么会直接返回这个类。

直接调用findClass方法可以跳过双亲委派机制,重写loadClass()方法可以打破双亲委派机制。

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 一个自定义的类加载器(之前的部分隐藏)
        MyClassLoader myClassLoader1 = new MyClassLoader("d:\\");

        // find方法调用,加载全限定名类
        Class<?> clazz1 = myClassLoader1.findClass("com.ali.Hello");
        
		System.out.println(clazz1.hashCode()); //out: 26508395
        System.out.println(clazz1.getClassLoader()); // out: 使用的类加载器 MyClassLoader@481248
}

2.4 JVM为什么要采用双亲委派模型?

  • 可以避免某一个类被重复加载,如果父加载器已经加载,子加载器无需重复加载,保证唯一性。
  • 避免核心API被修改,保证了安全。比如用户自己实现了String类,也无法使用。

2.5 类装载的执行过程

在这里插入图片描述
TODO 详细使用

三、垃圾回收

3.1 对象什么时候可以被垃圾器回收?

当一个对象没有被引用的时候就是垃圾,如果定位了垃圾,就有可能被垃圾器回收。

怎么判断有没有被引用(怎么定位垃圾)?

  • 引用计数法:如果循环引用,可能造成内存泄漏。现不使用。
  • 可达性分析法:扫描堆中的对象,看是否能沿着GC Root对象为引用链起点找到该对象,找不到则表示可回收。
    • 哪些对象可以作为GC Root?
      • 虚拟机栈中引用的对象
        在这里插入图片描述
      • 方法区中类静态属性引用的对象
        在这里插入图片描述
      • 方法区中常量引用的对象
        在这里插入图片描述
      • 本地方法栈中JNI(Native方法)引用的对象

3.2 JVM垃圾回收算法有哪些?

  • 标记清除法:分为标记和清除两个阶段,先用可达性分析法标记出哪些对象可达,再清除剩余的。
    • 优:效率高。
    • 缺:内存不连续。
  • 标记整理法:在标记清除算法的基础上,将存活对象都向内存的一段移动,然后清理垃圾。一般老年代使用标记整理法。
    • 优:清理后内存连续。
    • 缺:需要移动对象,效率较低。
  • 复制算法:将原有的内存空间一分为二,每次只用其中的一块。垃圾回收时,将存活的对象复制到另外一块区域,再将原来的内存区域清空,并交换两个区域的角色。一般新生代使用复制算法
    • 优:效率高,内存连续。
    • 缺:内存使用率低,每次仅使用一半。

3.3 JVM的分代回收

JVM堆内分为新生代和老年代,大小比例为1:2。新生代中又分为eden区和幸存者区(包含from区和to区),比例为8:1:1。

  • 所有新创建的对象都会到eden区。
  • 当eden区内存不足时,会进行垃圾回收,将eden和from区的存活对象移动到to区,from和to的角色交换。
  • 当eden区再次不足时,会将eden区和to区的存活对象移动到from区。
  • 当幸存区对象熬过最多15次后,会晋升到老年代。
  • 如果幸存区内存不足或大对象会提前晋升。

MinorGC、MixedGC和FullGC

  • MinorGC/YoungGC新生代进行垃圾回收,暂停时间短。
  • MixedGC新生代+部分老年代垃圾回收,G1收集器特有。
  • FullGC新生代+老年代完整垃圾回收,暂停时间长,应该尽力避免。

FullGC触发条件

  • 调用了System.gc()
  • 老年代空间不足
    • 大对象、长期存活对象进入老年代。
    • 空间分配担保失败:复制算法的Minor GC需要老年代的内存空间做担保。
  • CMS GC中并发模式失败
    • 有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足)。

3.4 JVM有哪些垃圾回收器

  • 串行垃圾回收器
    • Serial + Serial Old垃圾回收器。Serial作用于新生代,使用复制算法。Serial Old作用于老年代,使用标记整理法。一个线程进行垃圾回收,但应用中的所有线程都会停。
      在这里插入图片描述
  • 并行垃圾回收器
    • Parallel + Parallel Old垃圾回收器。Parallel作用于新生代,使用复制算法。Parallel Old作用于老年代,使用标记整理法。多个线程进行垃圾回收,但应用中所有线程都会停。
      在这里插入图片描述
  • CMS并发垃圾回收器
    • 并发的、使用**标记-清除算法(三色标记算法)**的垃圾回收器。针对老年代。
    • STW停顿时间短,在进行垃圾回收时应用仍然能正常运行。
      • GC Root为黑色,其他节点都为白色。
      • 初始标记:只标记和GC Root直接相连的,直接可达的对象标记为灰色。一个线程标记,其他线程被阻塞。
      • 并发标记:扫描引用链。无子节点的节点,则灰色变为黑色;有子节点,则自己变为黑色,子节点由白色变为灰色。一个线程标记,其他线程运行。
      • 重复标记:扫描整个引用链。直到只有黑白两色。可能GC Root下的引用链有变化。多个线程进行重新标记。
      • 第四阶段一个线程进行清理,其他线程运行。可能存在用户线程在不断产生垃圾,但也只能留到下一次GC进行处理,这些垃圾又称为"浮动垃圾"。
        在这里插入图片描述
  • G1垃圾回收器
    • 作用在新生代和老年代。

3.5 G1回收器

G1垃圾回收器详解
JDK 9及之后默认G1回收器。JDK8默认ParallelGC(Parallel + Serial Old)。
在这里插入图片描述
G1垃圾回收器是将堆划分为若干个区域(Region),每个区域只能是一种角色。Eden区、S区、老年代O区和巨型对象H区(一个对象超过Region大小50%则是巨型对象)。
为了避免整堆扫描来判定垃圾,每个Region有一个RSet(RememberSet),类似于一个反向指针,记录了其它 Region 对当前 Region 的引用情况。这样在查找引用链时只需要看RSet即可,不需要扫描全堆。

过程

  • 年轻代垃圾回收
    • 标记-复制全过程 STW。
    • ‘当年轻代的Region占比超过设定比例后(动态触发,并不是超过就一定触发,默认5%),就会触发年轻代垃圾回收。Eden区复制到Survivor区,Survivor区复制到老年区。
  • 混合垃圾回收
    • 当老年代超过设定比例后(默认45%),将启动混合垃圾回收。会根据设定的最大暂停时间优先回收价值高的区域,也就是存活对象少的区域。
    • 标记阶段
      • 初始标记 STW,但很快
      • 并发标记 非STW,耗时长一些
      • 重新标记 STW
    • 清理阶段
      • 清点出有存活对象的分区和没有存活对象的分区 STW
    • 复制阶段
      • 分配新内存和复制对象的成员变量 STW

参数

G1垃圾回收器

  • -XX:MaxGCPauseMillis:GC最大暂停时间,默认200ms。
  • -XX:G1NewSizePercent:新生代比例下限,默认5%。
  • -XX:G1MaxNewSizePercent:新生代比例上限,默认60%。
    • 最好是 Eden 的空间大一点,因为 Young GC 的频率比Mixed GC高,大的 Eden 空间能够降低 Young GC 的发生次数。但如果Eden 很大,那么留给老年代回收空间就不多了,最后可能会导致 Full GC。
  • -XX:InitiatingHeapOccupancyPercent:触发全局并发标记的老年代使用占比,默认45%,也就是老年代占堆的比例超过45%。
  • -XX:G1MixedGCLiveThresholdPercent:指定被纳入 Cset 中 Region 的存活空间占比阈值,默认 85%。在全局并发标记阶段,如果一个 Region 的存活对象的空间占比低于此值,才有可能被纳入 Cset。

四、JVM实践

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值