vlambda博客
学习文章列表

JVM面试前准备(翻阅牛客网面精,问题汇总+见解)

一 内存图

1.1 内存模型有哪些?有什么区别?

JVM分为:运行时方法区、类加载器、本地接口、执行引擎。类加载器:根据全类名将class文件加载到运行时方法区的方法区。执行引擎:执行classes指令。运行时方法区:执行指令内存。本地接口:与其他编程语言交互接口。

运行时方法区分为:虚拟机栈、堆、方法区、本地方法栈。虚拟机栈:用于加载方法,局部变量、请求参数。(单线程有效) 堆:用于存储对象。多个线程有效。本地方法栈:执行本地方法的区域。方法区:类信息、常量、静态变量、即时编译的代码(多线程有效)。程序计数器:很小内存,字节码执行标识器。(当前线程有效)

1.2 堆内存模型在1.7和1.8之间变化?为什么这样变?

1.7:年轻代、老年代、永久代。1.8:年轻代、老年代、元空间(物理内存)。因为JRockit没有永久代,HotSpot jvm为了融合JRockit而移除永久代。

1.3 为什么要分代?新生代还要分Eden、From、To区域呢?

根据对象的寿命将对象放在不同的区。对于不同的区采用不同的垃圾回收算法。寿命短的回收频率高点,寿命长的回收频率低点。提高效率。

因为年轻代对象寿命很短,进行GC时候,只有少部分对象存活,将存活的对象复制到TO和老年代。能提高效率和减少内存碎片化。

1.4  JVM什么情况会触发垃圾回收(young gc MinorGC Full GC)?

young gc 触发频率:Eden区被耗尽会被触发。mixed gc 当老年代大小占用堆阙值,就会触发mixed gc。回收全部年轻代和部分老年代。Full GC: System.gc()方法被调用、老年代空间不足、方法区空间不足。

1.5 为什么新生代使用标记复制,老年代使用标记压缩法?

年轻代为什么要用标记复制算法:因为年轻代存放对象生命周期较短,垃圾对象多,要复制有用的对象少。执行效率高。

老年代使用标记压缩法:因为老年代的存活对象比较多,移动的元素相对少,还解决了内存碎片化问题。

1.6 内存泄漏和内存溢出是什么?

内存泄漏:对象申请内存,用完后不释放。内存溢出:创建对象,没有足够内存创建对象。

1.7 详细描述下年轻代进行回收的过程是什么?

在开始GC时候,对象只存在Eden区和from区。紧接着GC时候,eden区对象复制到to区,from区对象根据年龄复制到to和老年代,当from和eden完全为null。to和from交换角色。如果在复制过程中时候to满了,全部对象到老年代。

1.8 对象一定在堆里?常量和变量在哪?

对象创建存在堆中,但是静态对象、常量对象也会存在方法区中。常量存在方法区中。局部变量存储在栈中,成员变量存储在堆中。

1.9 哪些对象可以作为gc roots?

  1. 静态对象。
  2. 常量引用对象。
  3. 栈帧中引用的对象。
  4. 被同步锁持有对象。
  5. 基本数据类型对应对象、异常对象。

1.10 Eden和Survivor的比例分配等?

在年轻代中Eden占有八份,Survivor的From占有一份,to占有一份。

1.11 JVM的空间分配担保策略描述。

JVM采用分代算法,将堆划分为老年代和年轻代。不同的内存采用不用的回收算法。空间担保指的是老年代进行空间分配和担保。

在发生Minor GC前,先检查老年代最大连续可用空间是否大于年轻代所有的对象。如果大于则证明此次Minor GC安全。如果小于且开启了空间分配等保,则查看历次变为老年代对象的平均值,如果大于平均值则尝试进行一次Minor GC,如果小于或者没有设置空间等保进行有一次full gc。

1.12 JVM常用调优参数有哪些?

-Xms初始堆大小 -Xmx最大堆大小 -XX:NewRatio=n 设置年轻代和老年代比值。-XX:NewSize=n:设置年轻代大小 -XX:+UseSerialGC:设置收集器

1.13 JVM 运行时数据区 方法递归调用存在什么问题?

如果递归调用比较深参数比较多,有可能导致栈溢出。

1.14 跨代引用怎么解决的?

每个Region初始化都会初始化一个RSet,用来记录其他Region指向该Region中对象引用。

1.15 Java线程模型和JVM线程模型区别?

1.16 标记清除多次后老年代产生内存碎片,引起 full gc,接下来可能发生什么问题?

可能对碎片化内存进行压缩。如果设置设置压缩内存次数低,可能导致full gc频率过高。

1.17 高吞吐量该jvm如何调优?

将垃圾回收器设置为G1。高吞吐量尽量避免full gc,可以将eden区设置大点,减少young gc次数,使对象岁数增长慢点,尽可能将对象在young gc解决掉。从而不进入老年代。

1.18 哪些地方会OOM,元空间如何OOM?

集合中有对象的引用,使用未清空,GC不能进行回收。代码循环中产生过多重复对象。一次性从数据库取出数据过大。堆内存值小。

一次加载过多的class文件会导致OOM(内存溢出)。

1.19 永久代和元空间会发生垃圾回收?

当永久代或者元空间满了会出现Full GC。元空间默认是物理机内存的1/64。最大内存是物理机内存的1/4或者1G.

二 回收算法

2.1 jvm垃圾回收算法有哪些?之间区别?

标记清除算法、标记压缩算法、标记复制算法、分代算法

标记清除:需要遍历两遍对象,效率低,而且产生碎片化。标记压缩算法:也是需要遍历两遍对象,而且需要对对象移动。解决内存碎片化,但是效率也比较低。标记复制算法:清理的对象多的时候效率高,且无碎片化。Survivor分两个区,每次只能使用一个。分代算法:根据回收对象特点选择,年轻代复制算法、老年代标记清除和标记压缩。

2.2 调用System.gc()是否会立即回收,为什么?

System.gc()只是告诉虚拟机要进行回收,什么时候回收是由虚拟机说的算。垃圾回收机制是内存快不够用了才回收。

2.3 有没有出现栈溢出?

栈内存溢出,一般栈内存局部变量过多,导致内存溢出。常见:出现递归方法,参数过多,递归过深、递归没有出口。

三 回收器

3.1 垃圾回收器有哪些?分别运用了什么垃圾回收算法?

串行收集器、并行收集器、cms收集器、G1收集器、ZGC收集器

CMS垃圾回收器:针对老年代进行垃圾回收,采用标记清除算法。G1垃圾回收器:取消物理上的老年代和年轻代的划分。而是将堆划分若干个区域,每个区域包含逻辑上的年轻代和老年代。采用年轻代收集、混合收集、full gc。

3.2 CMS垃圾回收器怎么解决标记清除导致的碎片问题?

设置多少次Full GC触发一次压缩,默认值为0,代表每次进入Full GC都会触发压缩。

四 类加载器

4.1 什么是类加载机制?在哪个区域进行?

虚拟机将类的class文件加载到内存中,并对数据校验、准备、解析、初始化,最终形成虚拟机直接使用的Java类型,叫做类加载机制。

类加载过程放到虚拟机外部执行。

4.2 什么是双亲委派机制?有什么用?

类加载器有:启动加载器、扩展加载器、应用加载器。如果一个类收到加载请求,先不进行加载先给父类进行加载。依次递进,最上层加载器没有父类加载器。如果父类加载器反馈无法加载这个类,子类才去进行加载。

保证Java程序的稳定运作。如果一个类书写为java.lang.Object 。系统中出现两个不用的 Object 类,Java体系中最基本的行为无法保证。

4.3 JVM字节码文件对象的结构?

版本信息、生成时间;类中涉及常量池;类的构造器;main方法信息;

4.4 一个对象new具体过程,如果发生OOM,JVM会做什么?

虚拟机接到new指令时,先检查常量池是否加载对应的类。如果没有,先加载对象的类。类加载之后接下来时分配内存。若堆内存是绝对工整的,使用“指针碰撞”分配内存,如果不规整就从“空闲列表”分配。划内存时候还需要考虑一个问题并发,两种方式:CAS同步处理或本地线程分配缓冲。然后内存初始化操作,执行init方法。

内存分配两种方式保证线程安全两种方式:

4.5 类加载器加载文件过程?怎么扫描的方法和属性,放在了哪?

整个生命周期经历7段,加载、验证、准备、解析、初始化、使用、卸载。

4.6 JVM的编译优化常见什么?

为了使代码运行效率更高,虚拟机的编译器做着优化工作。常见如下:方法内联 逃逸分析 公共子表达式消除 数组边界检查消除 。

4.7 什么是逃逸分析?

目前Java虚拟机中比较前沿的优化技术,它并不是直接优化代码的手段,而是为其 他优化措施提供依据的分析技术。

分析对象动态作用域,当一个对象在方法里面被定义后,它可能被外部方法所引用,例如作为调用参数传递 到其他方法中,这种称为方法逃逸;甚至还有可能被外部线程访问到,譬如赋值给可以在其他线程中访问的实例变量,这种称为线程逃逸;从不逃逸、方法逃逸到线程逃逸,称为对象由低到高的不同逃逸程度。