JVM面试题之内存区域、类加载篇

引言

Java开发人员在面试中基本都会被问到关于JVM的问题。想要成为高级的开发人员,了解和学习Java运行的原理和JVM是必不可少的,在解决疑难杂症上会很有帮助。本文会介绍在面试和实际工作中需要了解的主要的知识点。


JVM是什么?

JVM(Java虚拟机)。JVM的主要作用是在各种硬件平台上解释并执行Java字节码(.class文件),从而使得Java程序能够在不同的操作系统和架构上无需重新编译即可运行。这是Java“一次编写,到处运行”的核心所在。


1. JVM内存划分

在这里插入图片描述
JVM主要被划分成​ 堆、方法区(元空间)、虚拟机栈、本地方法栈、程序计数器五个部分,主要功能如下:

Heap(堆):
​ 堆用于分配对象的实例以及数组的内存,线程共享,用来存放对象实例,也是垃圾回收(GC)的主要区域。
一般来说堆内又分为新生代和老年代,新生代又分为:Eden区和Surviver1和Surviver2区;

方法区:
​ 方法区也可以称之为永久区,它主要储存的是已经被java虚拟机加载的类信息、常量、静态变量;Jdk1.8以后取消了方法区这个概念,称之为元空间(MetaSpace);

虚拟机栈:
​ 虚拟机栈主要用于存储栈帧 ,线程私有,每一个方法在执行的时候都会创建一个栈帧,栈帧中用来存放(局部变量表、操作数栈 、动态链接 、返回地址

本地方法栈:
​ 本地方法栈和虚拟机栈类似,不同的是虚拟机栈服务的是Java方法,而本地方法栈服务的是Native方法。

PC程序计数器:
​ PC,指的是存放下一条指令的位置的一个指针。它是一块较小的内存空间,且是线程私有的。由于线程的切换,CPU在执行的过程中,需要记住原线程的下一条指令的位置,所以每一个线程都需要有自己的PC。

2. 对象如何在JVM中创建

2.1 内存分配

在这里插入图片描述

  • 对象优先分配在Eden区,如果Eden区没有足够的空间进行分配时,虚拟机执行一次MinorGC。而那些无需回收的存活对象,将会进到Survivor 的 From 区(From 区内存不足时,直接进入 Old 区)。
  • 大对象直接进入老年代(需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。
  • 长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄(Age Count)计数器,如果对象经过了1次Minor
    GC那么对象会进入Survivor区,之后每经过一次Minor GC那么对象的年龄加1,直到达到阀值(默认15次),对象进入老年区。

2.2 创建对象步骤

创建对象主要分为5个步骤类加载检查、分配内存、初始化零值、设置对象头、执行init方法

①类加载检查:
​ 虚拟机遇到 new 指令时,⾸先去检查是否能在常量池中定位到这个类的符号引⽤,并且检查这个符号引⽤代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执⾏相应的类加载过程。

②分配内存:
​ 在类加载检查通过后,接下来虚拟机将为新⽣对象分配内存。

③初始化零值:
​ 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值,这⼀步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使⽤,程序能访问到这些字段的数据类型所对应的零值。

④设置对象头:
​ 初始化零值完成之后,虚拟机要对对象进⾏必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外,根据虚拟机当前运⾏状态的不同,如是否启⽤偏向锁等,对象头会有不同的设置⽅式。

⑤执⾏ init ⽅法:
​ 从虚拟机的视⻆来看,⼀个新的对象已经产⽣了,但从Java 程序的视⻆来看, ⽅法还没有执⾏,所有的字段都还为零。所以⼀般来说(除循环依赖),执⾏ new 指令之后会接着执⾏⽅法,这样⼀个真正可⽤的对象才算产⽣出来。

3. JVM类加载流程

在这里插入图片描述
过程:加载、验证、准备、解析、初始化

加载(Loading):
加载是指读取类的.class文件,将其转化为二进制数据流,再将这些数据转化成方法区中的运行时数据结构。

验证(Verification):
验证阶段确保字节码文件符合JVM规范,防止加载含有错误的类,保障类的正确性和安全性。

准备(Preparation):
准备阶段为类的静态变量分配内存并在方法区中设置默认初始值,但并不执行任何初始化代码。

解析(Resolution):
解析阶段将常量池中的符号引用转换为直接引用,即将类、字段、方法等符号引用解析为具体内存地址。

初始化(Initialization):
初始化是最具活力的一个阶段,它负责执行类构造器 () 方法,用于初始化类变量和其他静态成员,此时类才真正具备对外提供服务的能力。

3.1 双亲委派

JVM的双亲委派模型是指每当一个类加载器需要加载类的时候,它会先委托它的父加载器去尝试加载这个类,只有在父加载器无法加载这个类的情况下,才会由原始的类加载器尝试去加载。这个模型的主要目的是为了保证类的独立性和防止类的重复加载。这里的双亲不能以父母来理解,而是以自下而上的委托父类加载器来理解。

在这里插入图片描述
破坏双亲委派机制:
可以⾃⼰定义⼀个类加载器,重写loadClass方法;loadClass方法主要进行类加载的方法,默认的双亲委派机制就实现在这个方法中。

总结

JVM内存划分及各区域的作用都需要了解,类加载和双亲委派面试常客,也建议学习加了解,学习里面的设计思想。后续会介绍JVM垃圾回收相关的知识。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java码农杂谈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值