【Java开发岗面试】八股文—Java虚拟机(JVM)

声明:

  1. 背景:本人为24届双非硕校招生,已经完整经历了一次秋招,拿到了三个offer。
  2. 本专题旨在分享自己的一些Java开发岗面试经验(主要是校招),包括我自己总结的八股文、算法、项目介绍、HR面和面试技巧等等,如有建议,可以友好指出,感谢,我也会不断完善。
  3. 想了解我个人情况的,可以关注我的B站账号:东瓜Lee

Java程序的运行环境(Java二进制字节码的运行环境

好处:

  1. 一次编写, 到处运行
  2. 自动内存管理,垃圾回收机制(GC)



面试题:

JVM组成:

  1. JVM有什么作用?

    JVM 其实就是一个虚拟机,它在计算机上模拟了一台真正的计算机,而且是与平台无关的,这意味着 Java 代码可以在任何支持JVM的平台上运行,而不需要考虑平台的具体细节。JVM 是 Java 的核心,它为 Java 语言提供了强大的功能和平台无关性。

    JVM 的主要作用包括:

    1. Java 代码的编译和解释执行:Java代码首先被编译成字节码,然后由JVM解释执行字节码。JVM 使用即时编译技术(Just-In-Time Compilation,JIT)将频繁执行的代码编译成本地机器代码,从而提高执行效率。
    2. 内存管理:JVM 提供了垃圾回收机制,自动管理内存分配和释放,避免了内存泄漏和空指针异常等问题。
    3. 安全管理:JVM 可以对 Java 代码进行安全性检查,防止恶意代码对系统造成损害。
    4. 多线程支持:JVM 可以处理多个线程的并发执行,保证线程安全。
    5. 跨平台兼容性:JVM 实现了一种平台无关的执行环境,使 Java 代码可以在不同平台上运行。

  1. 什么是程序计数器

    1. 线程私有的(没有线程安全问题),内部保存的是字节码的行号,存储 正在执行的字节码指令地址。
    2. 把Java编译后的class文件,反编译成汇编语言代码后就能看到,程序的执行是按照命令的行号来的,程序计数器记录的就是这个行号(比如线程1执行到了第10行,然后cpu切换到了其他线程,其他线程执行完后继续来执行线程1,就会从程序计数器中得到第10行继续执行)。

  1. 什么是Java虚拟机栈(Java方法栈)

    1. 栈的特点就是先进后出 后进后出的数据结构
    2. 每个线程运行时所需要的内存,称为虚拟机栈(多个线程运行,就会创建多个虚拟机栈,线程安全的)
    3. 每个栈由多个栈帧(frame) 组成,对应着每次方法调用时所占用的内存(但是每个线程只能有一一个活动栈帧,对应着当前正在执行的那个方法)

  1. 什么是本地方法栈?

    本地方法就是native method,它底层并不是由java实现的,一般是和操作系统底层比较相关的方法。

    本地方法栈就是用来支持本地方法的调用逻辑的

    本地方法栈和虚拟机栈一样 都是用于支持当前线程的方法调用的,所以都是线程安全的。


  1. 垃圾回收是否涉及栈内存?

    1. 垃圾回收主要指就是堆内存,对象存储在堆内存,垃圾回收器回收的主要是对象
    2. 对于栈而言,当栈帧弹栈以后(当前方法执行完毕),内存就会释放,所以不需要被GC回收

  1. 栈内存分配越大越好吗?

    1. 未必,默认的栈内存通常为1024k(1m)
    2. 栈帧过大会导致线程数变少,例如,机器总内存为512m,目前能活动的线程数则为512个,如果把栈内存改为2048k,那么能活动的栈帧就会减半

  1. 什么情况下会导致栈内存溢出?

    1. 栈帧过多(方法调用过多)导致栈内存溢出,典型问题:递归调用
    2. 栈帧过大(每个方法分配的空间太大)导致栈内存溢出
    3. StackOverflowError

  1. 方法内的局部变量是否线程安全?

  1. 如果方法内局部变量没有逃离方法的作用范围(不作为形参也不作为返回值),它是线程安全的
  2. 如果是局部变量引用了对象,并逃离方法的作用范围(比如作为形参和返回值),则需要考虑线程安全问题

  1. 你能给我详细的介绍下Java的堆吗?

    1. 是所有线程共享的内存空间(有线程安全问题),可以被垃圾回收器回收

    2. 存储对象实例和数组

    3. 当堆内存不够时,就会抛出OutOfMemoryError异常

    4. 堆是由年轻代和老年代组成的(比例为1:2)

      年轻代又被分为了三个区域:Eden、survivor(from+to)

    5. 不同于数据结构中的堆(最大堆,最小堆)


  1. 能不能介绍一下方法区

    1. 方法区(Method Area)是各个线程共享的内存区域(具有线程安全问题)
    2. 方法区是一个抽象的概念,永久代(before)和元空间(now)是实现
    3. 主要存储类的信息、运行时常量池
      1. 运行时常量池可以看作是一张表, 虚拟机指令根据这张常量表找到要执行的信息
    4. 虚拟机启动的时候创建方法区,关闭虚拟机时释放方法区
    5. 如果方法区域中的内存无法满足分配请求,则会抛出OutOfMemoryError: Metaspace

  1. 堆和栈的区别是什么

    1. jvm上:

      1. 堆内存是用来存储Java对象和数组的,堆内存会使用GC来执行垃圾回收,所有线程共有的(有线程安全问题)
      2. 栈内存一般会用来存储局部变量和方法调用,栈内存不会使用GC,不同的线程对应有不同的栈(没有线程安全问题)
      3. 两者出现内存溢出,都会抛异常,一个是OutOfMemoryError,一个是StackOverFlowError
      4. 栈的空间大小远远小于堆的空间
    2. 数据结构上:

      1. 栈是一种线性表,符合先进后出,后进先出的特点
      2. 堆是一种树形结构,是一种特殊的完全二叉树
        1. 如果所有的节点都>=它的父节点,那么这个堆就是最小堆
        2. 如果所有的节点都<=它的父节点,那么这个堆就是最大堆

  1. 你听过直接内存吗

    1. 直接内存:并不属于JVM中的内存结构,不由JVM进行管理。是虚拟机的系统内存,常见于NIO操作时,用于数据缓冲区,它分配回收成本较高,但读写性能高
    2. 普通IO也叫做BIO,NIO的效率更高(比如作文件拷贝的时候)

  1. Java从源代码文件.java 到代码执行的过程

    1. **编译:**编译器将.java源代码文件编译成.class字节码文件(编译过程会对源代码做语法分析、语义分析、注解处理等等)
    2. **加载:**类加载器会将.class字节码文件加载到JVM中(加载过程又可以分为:装载、连接、初始化)
    3. **解释:**JVM可以把字节码转换为操作系统可以识别的指令
    4. **执行:**操作系统就可以调用CPU来执行指令了

  1. 什么是类加载器,类加载器有哪些?

    JVM只会运行二进制文件,类加载器的作用就是将.class字节码文件加载到JVM中,从而解释执行字节码文件以启动Java程序。

    1. 启动类加载器(BootStrap ClassLoader):加载JAVA HOME/jre/lib目录下的库(底层是c++实现的)
    2. 扩展类加载器(ExtClassLoader):加载JAVA HOME/jre/lib/ext目录中的类
    3. 应用类加载器(AppClassLoader):加载classPath下的类
    4. 自定义类加载器(CustomizeClassLoader):继承自ClassLoader,实现自定义类加载规则

  1. 什么是双亲委派模型?

    加载某一个类,先委托上一级的加载器进行加载,如果上级加载器也有上级,则会继续向上委托,如果上级已经加载了这个类了,就直接返回给下级子加载器,如果该类委托的上级没有被加载,子加载器才会加载该类。

复制代码
比如一个Student类,首先应用类加载器 向上委托到 扩展类加载器,扩展类加载器 继续向上委托到 启动类加载器,发现没有加载过这个类,那么子加载器(应用类加载器)就会加载Student类。

如果是String类,应用类加载器 一直向上委托到 启动类加载器,启动类加载器中已经加载过String类了,那么就可以直接返回给子加载器(应用类加载器)。

  1. JVM为什么采用双亲委派机制?

    1. 通过双亲委派机制可以避免某一个类被重复加载,当父类已经加载后则无需重复加载,以保证类加载的唯一性。
    2. 为了安全,保证类库API不会被修改(比如String类已经被加载了,又去定义一个String类就会报错)。

  1. 说一下类装载的执行过程?【待学习】

    1. 加载:查找和导入.class文件
    2. 验证:保证加载类的准确性
    3. 准备:为类变量分配内存并设置类变量的初始值
    4. 解析:把类中的符号引用转换为直接引用
    5. 初始化:对类的静态变量、静态代码块执行初始化操作
    6. 使用:JVM开始从入口方法执行用户的程序代码
    7. 卸载:当用户程序代码执行完毕后,JVM便开始消耗创建的class对象

  1. 对象什么时候可以被垃圾回收器GC回收?

    简单的来说就是对象没有被引用了,也就是被定义为了垃圾对象,就可以被垃圾回收器回收了。

    那怎么定义对象是否为垃圾对象呢,一般会有两种方式:引用计数器法、可达性分析算法

    1. 引用计数法:每个对象都有一个引用计数器,记录了它被引用的次数,如果为0,就被定义为垃圾对象。

      虽然用起来简单,但是具有一个致命的问题,就是循环引用的问题,比如一个对象A有个属性引用了对象B,对象B有个属性引用了对象A,那么即使他们俩没有被其它的变量引用了,他们对应的引用计数器的值还是都为1,那实际上这两个对象都不需要用了,但是垃圾回收器也回收不了他们,就会引发内存泄露的问题。所以Java不是采用这种机制。

    2. 可达性分析算法:会有个GC Roots对象作为根节点,沿着这个节点向下走,如果可以被扫描到的对象,就是可达对象,也就是不能被回收的,不可达的对象就说明是垃圾对象,要被回收。

      可以作为GC Roots的对象一般有:

      1. 虚拟机栈中引用的对象
      2. 方法区中类静态属性引用的对象
      3. 方法区中常量引用的对象

  1. JVM垃圾回收算法有哪些?

    1. 标记-清除算法:分为标记和清除两个阶段,首先使用可达性分析算法标记所有存活的对象,标记完成后 统一回收所有没有被标记的对象(也就是垃圾对象)。

      缺点:标记清除之后会产生大量不连续的内存碎片,如果内存不连续了,则有可能不能存储一些占用内存大的对象或者数组。

    2. 标记-整理算法:对标记-清除算法的改进,也是先标记出所有存活的对象,然后把所有没有被标记的对象进行清除,但是它清除之后呢,会将目前存活的对象往一端移动,也就是进行整理,这样就使得内存是连续的,没有内存碎片了。

      但是因为多了个整理的过程,所以垃圾回收的效率会受到影响。

    3. 复制算法:把内存分为大小相等的两块区域,一片区域B不存放对象,另一片区域A使用可达性分析算法标记出存活的对象,统一放置到区域B,然后再把区域A中的剩余对象(也就是要被回收的对象)给全部回收了。

      优点:效率比较高,而且也没有内存碎片

      缺点:实际可使用的内存空间缩小为原来的一半,内存利用率较低。


  1. 说一下JVM中的分代回收?

    1. 堆的区域划分
      1. 堆被分为了两份:新生代和老年代(1: 2)
      2. 对于年轻代,内部又被分为了三个区域,Eden区,幸存者区survivor(分成from和to)(8:1:1)
    2. 对象回收分代回收的策略
      1. 新创建的对象,都会先分配到Eden区
      2. 当Eden区内存不足时,使用可达性分析算法标记出Eden区和from区存活的对象,将存活的对象采用复制算法复制到to区中,复制完毕后,将Eden区和from区的内存释放
      3. 经过一段时间后,Eden区又出现内存不足,标记Eden区和to区的存活对象,将其复制到from区
      4. 当from区对象经过了很多次垃圾回收(默认15次),直接放到老年代

  1. 说一下JVM有哪些垃圾回收器?

    1. 串行垃圾回收器

      是指使用单线程来进行垃圾回收,也就是垃圾回收时,只有一个线程在工作,适合堆内存较小的,比如说PC电脑。

      而且一个线程在进行垃圾回收的时候,其他所有线程都要暂停,等待垃圾回收的完成,

      具体来说包含Serial和Serial Old串行垃圾回收器:

      1. Serial作用于新生代,采用复制算法
      2. Serial Old作用于老年代,采用标记-整理算法
    2. 并行垃圾回收器

      JDK8默认采用的就是并行的垃圾回收器,也就是说有多个线程同时进行垃圾回收,而且其他的线程也都要暂停,等待垃圾回收的完成,才能继续执行。

      具体来说包含Parallel New和Parallel Old并行垃圾回收器:

      1. Parallel New作用于新生代,采用复制算法
      2. Parallel Old作用于老年代,采用标记-整理算法
    3. 并发(CMS)垃圾回收器

      CMS是一个并发的、使用标记-清除算法的垃圾回收器,该回收器主要是针对老年代垃圾回收的,是一款以获取最短回收停顿时间为目标的收集器,停顿时间短,用户体验就好。其最大特点是在进行垃圾回收时,应用仍然能正常运行。

    4. G1垃圾回收器


  1. 强引用、软引用、弱引用、虚引用是什么?有什么区别?

    引用主要是用来引用对象的,被分为四种类型,区别主要就是在对对象进行垃圾回收的时候,会有不同

    1. 强引用:最普通的引用方式,表示一个对象处于有用且必须的状态 ,如果一个对象具有强引用,则GC并不会回收它。即便堆中内存不足了,宁可出现OOM,也不会对其引用的对象进行回收。
    java 复制代码
    User user = new User();
    1. **软引用:**如果一个对象时被软引用的,那么在垃圾回收的时候,如果内存空间还足够,这个对象就不会被回收,如果再次垃圾回收的时候,发现内存空间不足了,才会被回收软引用的对象。
    java 复制代码
    User user = new User();
    SoftReference softReference = new SoftReference(user);
    1. 弱引用:表示一个对象处于可能有用且非必须的状态 。在GC线程扫描内存区域时,一旦发现弱引用,就会回收到弱引用相关联的对象 。对于弱引用的回收,无关内存区域是否足够,一旦发现则会被回收。
    java 复制代码
    User user = new User();
    WeakReference weakReference = new WeakReference(user);

​ 4. 虚引用:配合引用队列使用


  1. JVM调优的参数可以在哪里设置

    1. 使用SSM做开发的时候,war包部署在tomcat服务器中,可以在tomcat的文件中设置

      修改TOMCAT_HOME/bin/catalina.sh文件

    2. 使用SpringBoot做开发的时候,是使用的jar包,在启动命令中设置

      Java -Xms512 -Xmx1024m -jar


  1. 用的JVM调优的参数都有哪些?

    JVM调优主要就是调整新生代、老年代的堆内存空间大小,虚拟机栈空间,元空间大小,还可以调整垃圾回收器的类型。

    新生代中的对象什么时候进入老年代?

    在对象被创建的时候,一般就是放在新生代,如果有这么几种情况就会放到老年代:

    1. 达到晋升的年龄:新生代对象在经历GC的时候,如果没有被回收,就会年龄+1,如果年龄到达阈值,就会进入老年代,默认情况下阈值为15,当然也可以通过JVM调优参数来设置。
    2. 如果创建的对象很大,也可能会直接进入老年代,具体的大小也可以通过JVM调优参数来设置。

  1. 说一下JVM调优的工具?

    1. jdk自带的命令工具:

      jps:进程状态信息
      jstack:查看Java进程内线程的堆栈信息

      jmap:查看堆转信息

      jhat:堆转储快照分析工具

      jstat:JVM统计监测工具

    2. 可视化工具
      jconsole:用于对jvm的内存,线程,类的监控

      VisualVM:能够监控线程,内存情况


  1. Java内存泄露的排查思路?

    (在实际开发中,遇到内存溢出的情况,怎么解决?借助什么工具?解决流程?

    内存泄漏有几种情况:

    1. 虚拟机栈:StackOverFlowError,一般就是递归造成的,方法调用过多,栈帧过多,虚拟机栈空间有限,一般为1024k,就容易爆栈,可以去看下递归的边界条件有没有设置好
    2. 方法区:OutOfMemoryError:Metaspace,一般是动态加载的类太多了,可以从这个方面去排查
    3. :OutOfMemoryError:java heap space,内存泄漏通常是指的堆内存 ,一般是出现了一些大对象没有被回收的情况
      1. 通过jmap或设置m参数 来获取堆内存的快照dump文件
      2. 通过VisualVM可视化工具工具 去分析dump文件,VisualVM可以加载离线的dump文件
      3. 通过查看堆信息的情况,可以大概定位内存溢出是哪行代码出了问题
      4. 找到对应的代码,通过阅读上下文的情况,进行修复即可

  1. CPU飙高排查方案与思路?

    1. 在linux中可以使用top命令查看cpu的使用情况,可以看到是哪一个进程占用cpu较高
    2. 使用ps命令查看这个进程中的线程信息
    3. 使用jstack命令查看进程中哪些线程出现了问题,最终定位问题(可能的情况比如说,一个线程里面开了死循环)。

【后续继续补充,敬请期待】

相关推荐
蚂蚁雅嘿5 小时前
maven标签日常总结
java·maven
洋不写bug5 小时前
二叉树(二) 常见基础操作解析|结点数、树高、查找结点、判断完全二叉树
java·开发语言·数据结构·完全二叉树·二叉树结点数·树高·查找结点
菠萝加点糖5 小时前
Maven 坐标系统说明
java·maven
zuozong_5 小时前
内存的管理
jvm
hm宋5 小时前
Canal 升级 jar 包操作文档(以升级 FastJSON 为例)
java·canal·jar
YonyouHRSaaS5 小时前
AI视频面试系统定义、功能作用、品牌推荐、选择攻略
人工智能·面试·职场和发展·ai面试·视频面试·ai视频面试
后台模板学习5 小时前
学习的心态高频面试题
java·数据库·学习
用户3126874877206 小时前
线程池到底怎么管理线程的?从 ThreadPoolExecutor 到拒绝策略全链路拆解
java
cpolar技术支持6 小时前
Kafka Streams 窗口统计怎么验收:本地跑订单流聚合,用 cpolar 给同事看只读结果页
java·docker·kafka·cpolar·kafka streams
爱读源码的大都督6 小时前
DeepSeek面试官问:生产RAG系统回答不准确,该如何定位和优化?这样回答,能让面试官当场给你Offer!
java·后端·python