JVM面试题

内容分类 详情
Java高频面试题 汇总入口
JVM JVM面试题
并发 并发面试题
Spring Spring面试题
分布式 分布式面试题
SpringBoot SpringBoot面试题
SpringCloud SpringCloud面试题
Dubbo Dubbo面试题
MySQL MySQL面试题
Mybatis Mybatis面试题
Redis Redis面试题
RocketMQ RocketMQ面试题
算法 算法面试题
遇到的问题 遇到的问题
面试官的其他问题 面试官的其他问题
Git Git面试题

文章目录

    • 简述JDK,JRE,JVM的关系
    • 讲解JVM内存模型
    • JVM堆内存模型
    • 虚拟机栈内存溢出
    • JVM常见的错误
      • java.lang.OutOfMemoryError: Java heap space
      • java.lang.OutOfMemoryError: PermGen space
      • java.lang.StackOverflowError
      • Fatal: Stack size too small
      • java.lang.OutOfMemoryError: unable to create new native thread
      • java.lang.OutOfMemoryError: Requested array size exceeds VM limit
      • 在GC花费了大量时间,却仅回收了少量内存时,也会报出OutOfMemoryError
    • minorGC 和 Full GC、Major GC区别
    • JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor
      • 共享内存区划分
      • 一些参数的配置
      • 为什么要分为Eden和Survivor?为什么要设置两个Survivor区?
    • JVM中一次完整的GC流程是怎样的
    • 你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms和G1,包括原理,流程,优缺点
      • 垃圾收集器
      • CMS收集器和G1收集器的区别
    • JVM和JMM
    • volatile关键字
    • 类加载器
      • 类加载器的作用
    • 你知道的JVM相关的参数
      • 堆栈配置相关
    • JVM如何加载class文件的
    • 双亲委派

简述JDK,JRE,JVM的关系

  • JDK: Java Development Kit,Java开发工具包,是一个编写Java应用程序的开发环境。
    JDK是整个Java的核心,包括了JRE(Java运行环境)与一些Java开发工具(例如:jconsole、javac、java、javadoc、native2ascii、jar等)。JDK=JRE+Java开发工具(编译器、调试器等),javac命令调用JDk中的编译器,将.java文件编译成二进制流的.class文件。

  • JVM(Java Virtual Machine),即Java虚拟机,运行在操作系统之上,存在于内存中,与内存打交道,与硬件没有直接交互,是Java语言实现跨平台的核心。JVM主要负责运行Java编译器编译后的字节码文件(*.class文件)。JVM在执行字节码时,把字节码解释成具体平台上的机器码执行。JVM自己无法执行,必须要联合JRE中的Java基础&核心类库才能使用。

  • JRE(Java Runtime Environment),即Java运行环境,支持Java程序运行的标准环境,包括了JVM(Java虚拟机)的标准实现以及Java基础&核心类库。JRE=JVM+Java基础&核心类库。

JRE=JVM+Java基础&核心类库。

JDK=JRE+Java开发工具(编译器、调试器等)。

讲解JVM内存模型

JVM面试题_第1张图片

程序计数器:当前线程所执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址,线程私有。

Java虚拟栈:存放基本数据类型、对象的引用、方法出口等,线程私有。

Native方法栈:和虚拟栈相似,只不过它服务于Native方法,线程私有。

Java堆:java内存最大的一块,所有对象实例、数组都存放在java堆,GC回收的地方,线程共享。

方法区:存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据等。回收目标主要是常量池的回收和类型的卸载,各线程共享

JVM堆内存模型

JVM面试题_第2张图片

虚拟机栈内存溢出

  1. 虚拟机栈是线程私有的,主要存放基本数据类型,对应的引用地址,方法的出口信息。
  2. 每个方法的加载执行,都会对应该方法的入栈和出栈。
  3. java虚拟机根据服务器内存的大小和设定的参数,为java虚拟机栈进行分配空间。
  4. 线程申请的栈深度大于虚拟机允许的栈深度,就会报出StackOverFlowError的错误。一般在递归逻辑中会出现该错误。每次执行递归方法就对应栈帧压入虚拟机栈,栈帧总和大于-Xss设置的值时,就会报该错误。
  5. 在创建每个线程的时,虚拟机会分配栈空间给该线程使用,如果没有可用的空间分配给该线程就会报出OutOfMemoryError的错误。(线程启动过多)
  6. -Xss参数设置虚拟机栈大小

JVM常见的错误

java.lang.OutOfMemoryError: Java heap space

原因:Heap内存溢出,意味着Young和Old generation的内存不够。
解决:调整java启动参数 -Xms -Xmx 来增加Heap内存。
ms表示初始堆内存大小,mx表示最大堆内存大小

java.lang.OutOfMemoryError: PermGen space

原因:主要是反射产生大量的类不断加载,导致永久代被占满。
解决:调整-XX:PermSize= -XX:MaxPermSize= 两个参数来增大PermGen内存。一般情况下,这两个参数不要手动设置,只要设置-Xmx足够大即可,JVM会自行选择合适的PermGen大小。

java.lang.StackOverflowError

当前线程申请的栈深度大域虚拟机允许的栈深度,线程栈满了,从循环或者递归那里找问题。
原因:当前线程申请的栈深度大域虚拟机允许的栈深度
解决:优化程序设计,减少方法调用层次;调整-Xss参数增加线程栈大小。

Fatal: Stack size too small

原因:虚拟机栈太小
解决:涉及到的参数:-Xss2m -> 调大

java.lang.OutOfMemoryError: unable to create new native thread

原因:栈内存不足以创建新的线程
解决:

  1. 通过-Xss启动参数减少单个线程栈大小
  2. 通过-Xms -Xmx 两参数减少Heap大小,将内存让给Stack(前提是保证Heap空间够用)

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

原因:这个错误比较少见(试着new一个长度1亿的数组看看),同样是由于Heap空间不足。如果需要new一个如此之大的数组,程序逻辑多半是不合理的。
解决:修改程序逻辑吧。或者也可以通过-Xmx来增大堆内存。

在GC花费了大量时间,却仅回收了少量内存时,也会报出OutOfMemoryError

原因:当使用-XX:+UseParallelGC或-XX:+UseConcMarkSweepGC收集器时,在上述情况下会报错,在HotSpot GC Turning文档上有说明:
The parallel(concurrent) collector will throw an OutOfMemoryError if too much time is being spent in garbage collection: if more than 98% of the total time is spent in garbage collection and less than 2% of the heap is recovered, an OutOfMemoryError will be thrown.
解决:
一是需要进行GC turning,二是需要优化程序逻辑。

minorGC 和 Full GC、Major GC区别

新生代 GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快。

老年代 GC(Major GC):指发生在老年代的 GC,出现了 Major GC 经常会伴随至少一次的 Minor GC(并非绝对),Major GC 的速度一般会比 Minor GC 的慢 10 倍以上。

新生代和老年代 GC (Full GC)
JVM面试题_第3张图片

JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor

共享内存区划分

共享内存区 = 持久带 + 堆
持久带 = 方法区 + 其他
Java堆 = 老年代 + 新生代
新生代 = Eden + S0 + S1

一些参数的配置

默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ,可以通过参数 –XX:NewRatio 配置。
默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定)
Survivor区中的对象被复制次数为15(对应虚拟机参数 -XX:+MaxTenuringThreshold)

为什么要分为Eden和Survivor?为什么要设置两个Survivor区?

  • 如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC.老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多,所以需要分为Eden和Survivor。

  • Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。

  • 设置两个Survivor区最大的好处就是解决了碎片化,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)

JVM中一次完整的GC流程是怎样的

  • Java堆 = 老年代 + 新生代

  • 新生代 = Eden + S0 + S1

  • 当 Eden 区的空间满了, Java虚拟机会触发一次 Minor GC,以收集新生代的垃圾,存活下来的对象,则会转移到 Survivor区。

  • 大对象(需要大量连续内存空间的Java对象,如那种很长的字符串)直接进入老年态;

  • 如果对象在Eden出生,并经过第一次Minor GC后仍然存活,并且被Survivor容纳的话,年龄设为1,每熬过一次Minor GC,年龄+1,若年龄超过一定限制(15),则被晋升到老年代。即长期存活的对象进入老年代。

  • 老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GC,Full GC 清理整个内存堆 – 包括年轻代和年老代。

  • Major GC 发生在老年代的GC,清理老年区,经常会伴随至少一次Minor GC,比Minor GC慢10倍以上。

你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms和G1,包括原理,流程,优缺点

垃圾收集器

Serial收集器: 单线程的收集器,收集垃圾时,必须stop the world,使用复制算法。
ParNew收集器: Serial收集器的多线程版本,也需要stop the world,复制算法。
Parallel Scavenge收集器: 新生代收集器,复制算法的收集器,并发的多线程收集器,目标是达到一个可控的吞吐量。如果虚拟机总共运行100分钟,其中垃圾花掉1分钟,吞吐量就是99%。
Serial Old收集器: 是Serial收集器的老年代版本,单线程收集器,使用标记整理算法。
Parallel Old收集器: 是Parallel Scavenge收集器的老年代版本,使用多线程,标记-整理算法。
CMS(Concurrent Mark Sweep) 收集器: 是一种以获得最短回收停顿时间为目标的收集器,标记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除,收集结束会产生大量空间碎片。
G1收集器: 标记整理算法实现,运作流程主要包括以下:初始标记,并发标记,最终标记,筛选标记。不会产生空间碎片,可以精确地控制停顿。

CMS收集器和G1收集器的区别

  • CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;
  • G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用;
  • CMS收集器以最小的停顿时间为目标的收集器;
  • G1收集器可预测垃圾回收的停顿时间
  • CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
  • G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。

JVM和JMM

JVM: Java Virtual Machine
JMM: Java Main Memory

JMM:从抽象的角度来看,定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。

JVM面试题_第4张图片

volatile关键字

volatile其含义是 ‘易变的’

JVM面试题_第5张图片

类加载器

类加载器的作用

类加载器的作用:根据文件的全限定名将JDK编译的class文件,加载到JVM中,转化为class对象。

JVM面试题_第6张图片

你知道的JVM相关的参数

堆栈配置相关

java
-Xmx3550m
-Xms3550m
-Xmn2g
-Xss128k 
-XX:MaxPermSize=16m
-XX:NewRatio=4
-XX:SurvivorRatio=4
-XX:MaxTenuringThreshold=0

-Xmx3550m: 最大堆大小为3550m。

-Xms3550m: 设置初始堆大小为3550m。

-Xmn2g: 设置新生代大小为2g。

-Xss128k: 每个线程的堆栈大小为128k。

-XX:MaxPermSize: 设置持久代大小为16m

-XX:NewRatio=4: 设置新生代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。

-XX:SurvivorRatio=4: 设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6

-XX:MaxTenuringThreshold=0: 设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。

JVM如何加载class文件的

JVM面试题_第7张图片

双亲委派

JVM面试题_第8张图片

双亲委派导致的问题:在双亲委派模型中,由父加载类加载的类

JVM面试题_第9张图片

这条安全异常是由Java类加载的“双亲委派模型”所导致的。在双亲委派模型中,由父加载类加载的类,下层加载器是不能加载的。本例中最高层加载器BootstrapClassLoader加载了classpath路径下所定义的java.包内的类,而java.lang包就不能由BootstrapClassLoader的下层加载器AppClassLoader加载了。这也是java安全机制中对于恶意代码所采取的防护措施。

你可能感兴趣的:(想进厂,java,面试,jvm)