0
点赞
收藏
分享

微信扫一扫

秋招后端开发面试题 - JVM运行时数据区


目录


运行时数据区

前言

已经找到工作了,分享秋招时的笔记。祝大家都能顺利找到自己心仪的工作。


面试题

JVM 内存区域 / 运行时数据区?

image-20230315161815980

JVM 运行时数据区包括程序计数器、虚拟机栈、本地方法栈、堆、方法区
程序计数器、虚拟机栈、本地方法栈是线程私有的,堆和方法区是线程共享的

程序计数器:

  • 程序计数器是当前线程所执行的字节码的行号指示器
  • 是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError/OOM 情况的区域

虚拟机栈:

  • 描述 Java 方法执行的线程内存模型
  • 每个方法被执行时会创建一个栈帧,用于存储局部变量表、操作数栈等信息
  • 虚拟机栈是线程私有的,生命周期与线程相同
  • 异常情况:如果栈深度超过虚拟机允许的深度,会抛出 StackOverflowError 异常;如果栈容量扩展失败,会抛出 OutOfMemoryError 异常

本地方法栈:

  • 与虚拟机栈类似,但为虚拟机使用的本地 Native 方法服务

堆:

  • 堆是线程共享的,存放对象实例,几乎所有对象实例都在堆上分配
  • 通过参数 -Xmx 和 -Xms 设置堆的大小
  • 在 Java 堆中可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)以提升对象分配效率
  • 细分为新生代(Eden 区、Survivor 区 S0 和 S1)和老年代
  • 如果 Java 堆没有足够内存进行实例分配且无法再扩展,会抛出 OutOfMemoryError 异常

方法区:

  • 用于存储已加载的类型信息、常量、静态变量等数据
  • 和 Java 堆一样,是线程共享的
  • 在 JDK8 之前,方法区的实现为永久代,JDK8 之后为元空间

说一下 JDK1.6、1.7、1.8 内存区域的变化?

在 JDK 1.6、1.7 和 1.8 版本中,内存区域的变化主要体现在方法区的实现方式上

  • JDK1.6: 方法区的实现是永久代
  • JDK1.7: 讲字符串常量池和静态变量从永久代中移到堆
  • JDK1.8 去除永久代的概念,使用元空间;在直接内存划分区域作为元空间,运行时常量池、类常量池都移动到元空间

为什么使用元空间替代永久代作为方法区的实现?

  • 永久代是固定大小的,无法动态调整;元空间使用本地内存作为存储区域,可以根据系统的物理内存动态调整大小

Java 堆的内存分区了解吗?

  • 按照垃圾收集,将 Java 堆划分为新生代和老年代
  • 新生代存放存活时间短的对象,每次回收后存活的少量对象,逐步升到老年代
  • 新生代分为三个区域:Eden、S1、S2,比例是 8:1:1

运行时常量池?

  • 运行时常量池是方法区的一部分
  • 常量池表:用于存放编译器生成的字面量和符号引用,在类加载后存放到方法区的运行时常量池中
  • 具有动态性,不要求常量一定只有编译期才能产生
  • 常量池在无法申请到内存时会抛出 OutOfMemoryError 错误

字符串常量池了解吗?

  • 字符串常量区是 JVM 为了提高性能和较少内存消耗针对字符串类专门开辟的一块区域
  • 为了避免字符串的重复创建
// 在堆中创建字符串对象”ab“
// 将字符串对象”ab“的引用保存在字符串常量池中
String aa = "ab";
// 直接返回字符串常量池中字符串对象”ab“的引用
String bb = "ab";
System.out.println(aa==bb);// true

为什么将字符串常量池移动到堆中?

  • 因为方法区的 GC 回收效率太低,只有在整堆收集 (Full GC) 时才会执行 GC
  • Java 程序中通常有大量的字符串等待回收,将字符串常量池放在堆里,能够更加高效的回收字符串内存
举报

相关推荐

0 条评论