JVM面试题

内容分类详情
Java高频面试题汇总入口
JVMJVM面试题
并发并发面试题
SpringSpring面试题
分布式分布式面试题
SpringBootSpringBoot面试题
SpringCloudSpringCloud面试题
DubboDubbo面试题
MySQLMySQL面试题
MybatisMybatis面试题
RedisRedis面试题
RocketMQRocketMQ面试题
算法算法面试题
遇到的问题遇到的问题
面试官的其他问题面试官的其他问题
GitGit面试题

简述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内存模型

在这里插入图片描述

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

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

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

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

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

JVM堆内存模型

在这里插入图片描述

虚拟机栈内存溢出

  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内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为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的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。

在这里插入图片描述

volatile关键字

volatile其含义是 ‘易变的’

在这里插入图片描述

类加载器

类加载器的作用

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

在这里插入图片描述

你知道的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文件的

在这里插入图片描述

双亲委派

在这里插入图片描述

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

在这里插入图片描述

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

gzh-程序员灿灿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值