天津百度做网站多少钱,微信营销方案,江西合创建设工程有限公司 网站,长春市建设信息网站面试题005-Java-JVM(上) 目录 面试题005-Java-JVM(上)题目自测题目答案1. JVM由哪几部分组成#xff1f;2. 运行时数据区中包含哪些区域#xff1f;3. 栈和堆中分别存放什么数据#xff1f;4. 为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) #xff1f;5. 堆空间的…面试题005-Java-JVM(上) 目录 面试题005-Java-JVM(上)题目自测题目答案1. JVM由哪几部分组成2. 运行时数据区中包含哪些区域3. 栈和堆中分别存放什么数据4. 为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 5. 堆空间的基本结构了解吗什么情况下对象会进入老年代6. 大对象放在哪个内存区域7. Java对象的创建过程 参考资料 题目自测 1. JVM由哪几部分组成 2. 运行时数据区中包含哪些区域 3. 栈和堆中分别存放什么数据 4. 为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 5. 堆空间的基本结构了解吗什么情况下对象会进入老年代 6. 大对象放在哪个内存区域 7. Java对象的创建过程
题目答案
1. JVM由哪几部分组成
答JVM是一个可以执行字节码(.class)文件的虚拟计算机同时提供了内存管理垃圾回收等机制。它包含了以下几个主要部分。
类加载子系统负责将字节码文件(.class)加载到JVM。运行时数据区是JVM在执行期间使用的内存区域。执行引擎负责解释或编译字节码为机器码供处理器执行。本地库接口提供了一组调用操作系统或其他语言编写的本地库的API。
2. 运行时数据区中包含哪些区域
答运行时数据区是JVM在执行Java程序时为其分配的内存区域。
程序计数器是一块较小的内存空间是当前线程正在执行的那条字节码指令的地址。如果线程正在执行本地方法这这个计数器的值是未定义的。Java虚拟机栈每个线程在创建的时候都会创建一个虚拟机栈用于存储线程的局部变量表、操作数帧、动态链接、方法出口信息等。Java虚拟机栈中包含多个栈帧每个方法被调用到执行完成的过程都对应着虚拟机中一个栈帧的入栈到出栈的过程。本地方法栈是JVM运行Native方法准备的空间它与Java虚拟机栈实现的功能类似它是描述本地方法运行过程的内存模型。堆用于存放几乎所有的对象实例和数组是垃圾回收器主要工作的区域。方法区用于存储被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等。JDK1.8之前被实现为永久代。JDK1.8开始永久代被原空间取代。元空间使用的是本地内存而非堆内存。
3. 栈和堆中分别存放什么数据
答栈(Java虚拟机栈)中存放的数据
局部变量表主要用于存储方法参数方法内的局部变量数据类型包括基本数据类型和对象的引用。操作数栈用于临时存储操作指令和方法执行过程中中间结果。动态链接指向方法所属类的常量池的引用用于解析方法中的符号引用。方法返回地址存储方法调用后执行的下一条指令地址。 堆中存放的数据对象实例在程序中通过new关键字创建的对象实例包括对象的属性和方法。数组所有类型的数组包括基本类型数组和对象数组。
4. 为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace)
答将永久代替换为元空间主要是为了解决永久代的一些固有问题和限制提高JVM的性能和灵活性。
提高内存管理的灵活性和效率永久代的内存大小是在JVM启动时就已经设定好的不能动态调整。元空间使用的是本地内存而不是Java堆内存它的大小可以根据需要动态调整。解决类卸载和垃圾回收问题永久代的GC行为较为复杂且不可预测并且回收效率偏低。提供更好的性能和稳定性使用元空间使得JVM内存管理更加统一和一致因为元空间和其他内存区域一样都是使用本地内存进行管理。这样可以简化内存管理策略提升整体性能和稳定性。简化JVM的内存管理
5. 堆空间的基本结构了解吗什么情况下对象会进入老年代
答堆空间的基本结构主要由新生代、老年代和永久代组成。JDK8以后永久代被元空间取代使用本地内存来存储。
新生代新生代进步一细分为Eden区和两个幸存者区(Survivor 0 和 Surivivor 1) Eden区新创建的对象首先在Eden区分配内存。幸存者区(S0, S1)用于存放在新生代垃圾回收时存活下来的对象。每次Minor GC后存活的对象会在这两个区来回复制。 老年代经过多次Minor GC后仍然存活的对象。老年代进行垃圾回收(Major GC 或 Full GC)的频率较低。永久代/元空间用于存储类的元数据包括类的定义、常量、静态变量、即时编译后的代码等。
对象进行老年代的情况
年龄阈值达到每个对象在新生代分配内存时都有一个年龄每次Minor GC后年龄都会加1。当年龄达到一定阈值(默认是15)后对象会被提升到老年代。大对象如果对象太大超过了JVM设定的阈值对象会直接在老年代分配空间。Survivor区空间不足如果在进行Minor GC时幸存者区没有足够的空间来容纳所有存活的对象这些对象会被动态对象年龄判定如果Survivor空间中相同年龄的所有对象大小超过了Survivor空间的一半那么年龄大于或等于该年龄的对象可以直接进入老年代。
// 动态年龄计算代码
uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {//survivor_capacity是survivor空间的大小
size_t desired_survivor_size (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);//TargetSurvivorRatio 为50
size_t total 0;
uint age 1;
while (age table_size) {
total sizes[age];//sizes数组是每个年龄段对象大小
if (total desired_survivor_size) break;
age;
}
uint result age MaxTenuringThreshold ? age : MaxTenuringThreshold;...
}6. 大对象放在哪个内存区域
答大对象(非常大的数组和字符串)通常会直接分配在老年代内存区域。这是为了避免新生代进行频繁的垃圾回收时大对象频繁地在Eden区和Survivor区之间复制从而提高垃圾收集效率。 配置大对象直接进入老年代的阈值
# 将大于1MB的对象直接分配在老年代
java -XX:PretenureSizeThreshold1m -jar your-application.jar7. Java对象的创建过程
答
类加载检查 如果类没有被加载、连接和初始化JVM会先进行类加载。这包括以下步骤 加载通过类加载器读取类文件并将类的字节码加载到内存中。连接包括验证、准备和解析三个阶段。验证类文件的正确性准备类的静态变量并分配内存解析符号引用为直接引用。初始化执行类的静态初始化块和静态变量的初始化。 内存分配 JVM在堆中为新对象分配内存。分配的内存大小由对象的结构决定包括对象头和实例数据。JVM有两种主要的内存分配方式 指针碰撞Bump-the-pointer如果堆内存是规整的分配指针只需向空闲内存区域移动指定大小的距离。空闲列表Free List如果堆内存是非规整的JVM需要维护一个空闲列表分配内存时从空闲列表中找到合适的块。 初始化零值 JVM会将对象的所有实例变量初始化为默认值。例如数值类型变量会被初始化为0布尔类型变量初始化为false引用类型变量初始化为null。 设置对象头 在对象的内存空间中设置对象头信息这包括对象的哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID等。 构造方法初始化 调用对象的构造函数来完成对象的初始化工作。这包括执行实例变量的显式初始化操作以及构造方法体中的代码。具体步骤如下 执行类的实例初始化块。按照继承层次从上到下执行父类的构造方法。初始化实例变量为显式指定的值。执行类的构造方法的主体部分。
/*** 创建对象的示例代码*/
public class MyClass {private int value;public MyClass(int value) {this.value value;}public static void main(String[] args) {MyClass obj new MyClass(10);}
}上面代码示例的执行过程如下 类加载JVM检查 MyClass 是否已加载。如果未加载则加载 MyClass 类。内存分配在堆中为 MyClass 的新实例分配内存。内存初始化将分配的内存初始化为默认值。设置对象头在对象头中设置元数据。构造方法初始化执行 MyClass 的构造方法初始化实例变量 value 为 10。
参考资料
JavaGuide牛客网-Java面试宝典Java虚拟机底层原理总结ChatGPT