JVM相关面试题

【面试题一】谈谈JVM内存模型

JVM内存区域的划分,之所以划分是为了JVM更好的进行内存管理。就好比一间卧室,这块放床,这块放个电脑桌,每块地方各自有各自的功能,床用来睡觉,电脑桌用来办公打游戏。而JVM划分的内存区域也是各自有各自的用处。首先当JVM启动时,会申请一块很大的空间,然后把这块空间分成几块较小的区域,如下图。

  1. 本地方法栈:是用来存储JVM内部的native方法之间调用的关系的。本地方法栈是JVM内部C++写的代码。
  2. 虚拟机栈:是给Java代码使用的栈,存储的是Java方法之间调用的关系。可以认为包含了多个元素,每个元素是一个方法,一个元素又可以称为"栈帧",一个栈帧里会包含**"方法的入口地址、方法的返回地址、方法参数和局部变量"**,并且一个线程拥有一个栈,栈是线程私有的。
  3. 程序计数器:描述的是一个线程当前执行到了哪条指令,每个线程有一份。
  4. 堆:是JVM里最大的一块空间,存储的是NEW出的对象,类的成员变量。一个进程有一份,多个线程共享一份堆。
  5. 元数据区:在Java8之前是称为"方法区"的,在Java8之后才成为"元数据区",存储的是类对象、静态成员变量和常量池。一个进程有一份,多个线程共享一份。

注意一下哈,此处总结为:栈存储的是局部变量,堆储存的是普通成员变量,元数据区存储的是静态成员变量。

【面试题二】谈谈JVM类的加载过程

类加载就是找到.class文件,然后把文件内容从硬盘里读取到内存中。大概流程如下:

  1. 加载:就是找到.class文件,打开文件,然后把文件内容从硬盘读到内存中,最后完成加载得到类对象。
  2. 验证:即验证.class文件的格式是否正确,.class文件是一个二进制的文件。在官方的虚拟机规范文档里有描述这个二进制文件长啥样。
  3. 准备:给类对象分配一个内存空间,也就是在元数据区里占个位置,此时会把静态成员变量初始为0值。
  4. 解析:初始化字符串常量,把符号引用转为直接引用。
  5. 初始化:此时才会真正的给类对象的内进行初始化,也就是进行成员的初始化,执行代码块、静态代码块和加载父类。

解释一下符号引用和直接引用是什么:字符串常量得有一块内存空间进行存放,然后使用一个引用来指向这个内存空间的起始地址,但是在类加载前,字符串常量是存放在.class文件里的,此时并未分配空间,也就是这个引用其实指向的是一个类似于"偏移量"或者是"占位符"的一个东西,这个就是"符号引用"。而当类加载后,字符串常量才会被真正的分配一个内存空间,此时这个引用才会指向这个内存空间的起始地址,这个就称为"直接引用",这个过程就是"符号引用"转为"直接引用"。

【面试题三】一个类什么时候会被加载

只有当一个类被真正使用到了才会被加载,并不是当Java程序一启动就把所有类进行加载。比如进行构造类的实例,调用类的静态方法或者静态属性都会执行类加载。当使用子类时会先加载父类,并且一个类一旦被加载后,不会被重复加载。

【面试题四】谈谈双亲委派模型

​ 双亲委派模型描述的是如何找到.class文件。JVM提供了三个类加载器,BootStrapClassLoard负责加载标准库中的类,ExtensionClassLoard负载加载JVM扩展库中的类。(扩展库就是Java规范之外,由实现JVM的厂家提供的额外功能的库)ApplicationClassLoard负责加载第三方库的类和程序猿自定义的类。这三个类存在"父子关系",即每个类里有个parent属性,这个属性里存储的是自己的"父·类加载器"。Application的父类加载器是Extension,Extension的父·类加载器是Bootstrap。

​ 当进行类加载时,会先Application开始,不过Application会把执行加载的任务交给自己的父类,只有当自己的父类加载完成后或者没有父类,自己才会真正的执行加载任务,于是此时任务交给了Extension,而Extension又把任务交给了BootStrap,最后BootStrap由于没有父类加载器,于是BootStrap开始执行类加载,去标准库里找到相关的类进行加载,如果没有找到剩余的类,就会把任务交给自己的子类加载,此时Extension会执行加载任务,会去扩展库中找到相关的类进行加载,最后把任务交给Application加载,去第三方库和自定义的类里找到并且加载,如果剩余的类没有找到,此时会报一个"类找不到"这样的异常。

​ **为什么会有这样的顺序?**之所以有这样的顺序是由于JVM的实现是按照类似于递归的方式实现的,于是就导致了从上到下又从下到上的顺序,其次也是为了保证BootStrap先加载,Application最后加载,这样可以避免程序猿自定义的一些类,引起了JVM内部bug。比如自定义写了个java.lang.String,按照上述流程就是先加载的是标准库里的String类,就不会导致JVM内部已经实现的代码出现bug。类加载器也可以自定义,并且可以存放在三个类加载器的任意位置。

【面试题五】谈谈Java的垃圾机制

GC垃圾回收就是把不用的内存给自动释放掉,这样的好处是写代码简单,不容易出错,但是会额外占用系统资源。如果当内存垃圾很多了,此时触发了GC操作,就会占用很多的系统资源,并且GC的有些操作会有一些锁操作,此时就会导致正常执行的业务代码无法执行,这种问题就称为"STW"问题(Stop The World)

GC进行垃圾回收是以"对象"单位回收的。此处可以把对象分为三类:一是正在使用的内存,二是不用了但未被回收的内存,三是未被分配内存的。像一部分在使用一部分未使用的对象是不会被回收的,比如一个对象里有些属性还在使用,而有些属性已经用完了。GC的具体工作流程是:先找到垃圾,然后进行释放。

  • 找到垃圾:使用的是可达性分析策略。JVM存有一份所有对象的名单,可达性分析就是把所有对象看作为一棵树,从根节点开始遍历,能访问到的就标记为"可达",访问不到的标记为"不可达",然后把这些不可达的对象当成垃圾给释放掉,可达性遍历会进行周期性的遍历。因为Java里的对象都是通过引用来指向进行访问内存的,而一个对象里的成员又可以通过引用指向另一个对象,因此就会构成一个类似于树形的结构,可达性分析的根节点/起点称为GCroots,GCroots可以是栈上的局部变量、常量池的对象和静态成员变量

  • 对象的释放/垃圾释放:进行垃圾的释放有以下几种策略

    1. 标记清除:标记清除就是把标记为垃圾的空间直接释放掉,简单高效,不过会导致内存碎片问题,由于Java申请空间都是连续性的,所以当申请一个较大的空间时,可能就会申请失败。

    2. 复制算法:解决了内存碎片的问题, 复制算法就是把一个空间分成两部分,每次只用一半的内存,然后当进行垃圾回收时,把不是垃圾的对象复制到另一半没有使用的内存里,由于对象是按顺序复制过来的,所以不会有内存碎片问题。但是复制算法的成本比较高,当垃圾较少时,有效对象多,此时触发复制算法,复制的成本就比较大。

      进行了复制算法,把左侧对象1(垃圾)进行了删除,此时右侧内存是顺序存放的,所以内存空间是连续的,下次比如对象2变成了垃圾,就会把对象3复制到左侧内存空间,然后直接把对象2进行释放。

    3. 标记整理:标记整理解决了空间利用率低的问题,类似于顺序表删除元素或者搬运元素。比如内存里有1、2、3、4、5、6此时2、4、6为垃圾,就把3、5往前进行搬运,把2、4覆盖掉,最后把6直接释放。

    4. 分代回收:基于前面几种策略,设计出一个复合型的策略,即把垃圾回收分为不同的场景,每个场景使用不同的算法。Java对象的生命周期一般比较长,要么就很短,所以可以根据生命周期的长短,引入一个"年龄"的概念,单位是熬过GC的轮次,即当GC扫描一轮后,当前对象存活下来就把年龄+1,每个刚NEW出来的对象年龄为0。JVM把堆划分出一系列的区域,先把一整个空间划分为两部分,一部分为新生代,另一部分为老年代,在新生代里划分出一个较大的伊甸区,和两个同等大小的幸存区,伊甸区存放刚NEW出来的新对象,当熬过一轮GC后,使用复制算法把对象复制到第一个幸存区,到了幸存区后,当前对象需要继续接受GC周期性的扫描,当第二次熬过GC轮次后,从第一个幸存区复制到第二个幸存区,这样来回两个幸存区复制,当对象的年龄达到15时,就复制到老年代,进入老年代后,GC就会降低扫描的频率,因为当进入老年代说明当前对象生命周期很长,短时间内不会成为垃圾,当老年代里的对象称为垃圾时,会使用标记整理的策略进行垃圾释放。

相关推荐
Feng.Lee2 小时前
性能测试JVM监控有哪些?
jvm
HHhha.2 小时前
JVM深入学习(二)
java·jvm
silver6872 小时前
JVM堆空间
jvm
ThisIsClark3 小时前
【后端面试总结】mysql的join,left join,right join,full join分别是什么意思
mysql·面试·职场和发展
三次拒绝王俊凯6 小时前
java求职学习day15
java·学习·面试
言之。6 小时前
【架构面试】二、消息队列和MySQL和Redis
java·面试·架构
扫地僧00920 小时前
第13章 深入volatile关键字(Java高并发编程详解:多线程与系统设计)
java·开发语言·jvm
m0_748232921 天前
【SQL】掌握SQL查询技巧:数据分组与排序
java·jvm·sql
Pandaconda1 天前
【新人系列】Python 入门(二十九):常用标准库 - 下
开发语言·经验分享·笔记·后端·python·面试·标准库
疯狂小料1 天前
前端性能优化:HMR热更新和预获取加载
前端·面试