JVM面试题

1、内存模型以及分区,需要详细到每个区放什么?
答:JVM 分为堆区和栈区,还有方法区,初始化的对象放在堆里面,引用放在栈里面, class 类信息常量池(static 常量和 static 变量)等放在方法区 new:
 方法区:主要是存储类信息,常量池(static 常量和 static 变量),编译后的代码(字 节码)等数据
 堆:初始化的对象,成员变量 (那种非 static 的变量),所有的对象实例和数组都要 在堆上分配
 栈:栈的结构是栈帧组成的,调用一个方法就压入一帧,帧上面存储局部变量表,操 作数栈,方法出口等信息,局部变量表存放的是8大基础类型加上一个应用类型,所 以还是一个指向地址的指针
 本地方法栈:主要为 Native 方法服务
 程序计数器:记录当前线程执行的行号

2、堆里面的分区:Eden,survival (from+ to),老年代,各自的特点?
答:堆里面分为新生代和老生代(java8 取消了永久代,采用了 Metaspace),新生代包 含 Eden+Survivor 区,survivor 区里面分为 from 和 to 区,内存回收时,如果用的是复 制算法,从 from 复制到 to,当经过一次或者多次 GC 之后,存活下来的对象会被移动 到老年区,当 JVM 内存不够用的时候,会触发 Full GC,清理 JVM 老年区 当新生区满了之后会触发 YGC,先把存活的对象放到其中一个 Survice 区,然后进行垃圾清理。因为如果仅仅清理需要删除的对象,这样会导致内存碎 片,因此一般会把 Eden 进行完全的清理,然后整理内存。那么下次 GC 的时候, 就会使用下一个 Survive,这样循环使用。如果有特别大的对象,新生代放不下, 就会使用老年代的担保,直接放到老年代里面。因为 JVM 认为,一般大对象的存 活时间一般比较久远。

3、GC 的两种判定方法?
引用计数法:给对象添加一个引用计数器,当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。(Python 在用,但主流虚拟 机没有使用)
优点:快,方便,实现简单。
缺陷:对象相互引用时(A.instance=B 同时 B.instance=A),很难判断对象是否该回收。

引用链法: 来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GCRoots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为 引用链(ReferenceChain),当一个对象到 GCRoots 没有任何引用链相连时,则证明此对象是不可用的。 作为 GCRoots 的对象包括下面几种:
1.当前虚拟机栈中局部变量表中的引用的对象
2. 当前本地方法栈中局部变量表中的引用的对象
3. 方法区中类静态属性引用的对象
4. 方法区中的常量引用的对象

4、SafePoint 是什么?
答:比如 GC 的时候必须要等到 Java 线程都进入到 safepoint 的时候 VMThread 才能开始 执行 GC,

  1. 循环的末尾 (防止大循环的时候一直不进入 safepoint,而其他线程在等待它进入 safepoint)
  2. 方法返回前
  3. 调用方法的 call 之后
  4. 抛出异常的位置

5、GC 的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用 在什么地方,如果让你优化收集方法,有什么思路?
答:复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使 用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要按顺序分配内存即可, 实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半。 注意:内存移动是必须实打实的移动(复制),不能使用指针玩。
专门研究表明,新生代中的对象 90%是“朝生夕死”的,所以一般来说回收占据 10%的空间够用了,所以并不需要按照 1:1 的比例来划分内存空间,而是 将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor[1]。当回收时,将 Eden 和 Survivor 中还存活着的对象一 次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间。 HotSpot 虚拟机默认 Eden 和 Survivor 的大小比例是 8:1,也就是每次新生代中可用内存空间为整个新生代容量的 90%(80%+10%),只有 10%的内存会被 “浪费”。
标记-清除算法(Mark-Sweep):
过程: 1. 首先标记所有需要回收的对象 2. 统一回收被标记的对象
缺点: 1.效率问题,标记和清除效率都不高 2.标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不 提前触发另一次垃圾收集动作。
标记-整理算法(Mark-Compact):首先标记出所有需要回收的对象,在标记完成后,后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端 边界以外的内存。
思路:先标记,标记完毕之后再清除,效率不高,会产生碎片
复制算法:分为 8:1 的 Eden 区和 survivor 区,就是上面谈到的 YGC
标记整理:标记完毕之后,让所有存活的对象向一端移动

6、GC 收集器有哪些?CMS 收集器与 G1 收集器的特点?
答:并行收集器:串行收集器使用一个单独的线程进行收集,GC 时服务有停顿时间
串行收集器:次要回收中使用多线程来执行
CMS 收集器是基于“标记—清除”算法实现的,经过多次标记才会被清除
G1 从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个 Region 之间) 上来看是基于“复制”算法实现的。

7、Minor GC 与 Full GC 分别在什么时候发生?
答:新生代内存不够用时候发生 MGC 也叫 YGC,JVM 内存不够的时候发生 FGC。

8、几种常用的内存调试工具:jmap、jstack、jconsole、jhat
jstack 可以看当前栈的情况,
jmap 查看内存,
JConsole 是一个内置 Java 性能分析器,可以从命令行或在 GUI shell 中运行。您可以轻松地使用 JConsole(或者,它更高端的 “近亲” VisualVM )来监控 Java 应用程序性能和跟踪 Java 中的代码。
jhat 进行 dump 堆的信息 mat(eclipse 的也要了解一下)

9、类加载的几个过程?
答:加载、验证、准备、解析、初始化。然后是使用和卸载了 通过全限定名来加载生成 class 对象到内存中,然后进行验证这个 class 文件,包括文 件格式校验、元数据验证,字节码校验等。准备是对这个对象分配内存。解析是将符 号引用转化为直接引用(指针引用),初始化就是开始执行构造器的代码 。

10、JVM内存分哪几个区,每个区的作用是什么?
答:java虚拟机主要分为以下一个区:
方法区:

  1. 有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生GC,在这里
    进行的GC主要是对方法区里的常量池和对类型的卸载
  2. 方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后
    的代码等数据。
  3. 该区域是被线程共享的。
  4. 方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池
    具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量
    池中。
    虚拟机栈:
    1.虚拟机栈也就是我们平常所称的栈内存,它为java方法服务,每个方法在执行的时候都
    会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。
    2.虚拟机栈是线程私有的,它的生命周期与线程相同。
    3.局部变量表里存储的是基本数据类型、returnAddress类型(指向一条字节码指令的地
    址)和对象引用,这个对象引用有可能是指向对象起始地址的一个指针,也有可能是代表
    对象的句柄或者与对象相关联的位置。局部变量所需的内存空间在编译器间确定
    4.操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量表通过索
    引来访问,而是压栈和出栈的方式
    5.每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了
    支持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在运行期转化为直接
    引用。
    本地方法栈:本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。
    堆:java堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都在这
    里创建,因此该区域经常发生垃圾回收操作。
    程序计数器:内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数器完成。该内
    存区域是唯一一个java虚拟机规范没有规定任何OOM情况的区域。

11、简述java垃圾回收机制?
答:在java中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在
JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚
拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将
它们添加到要回收的集合中,进行回收。

12、java类加载过程
答:java类加载需要经历一下5个过程:
一、加载
加载时类加载的第一个过程,在这个阶段,将完成一下三件事情:

  1. 通过一个类的全限定名获取该类的二进制流。
  2. 将该二进制流中的静态存储结构转化为方法去运行时数据结构。
  3. 在内存中生成该类的Class对象,作为该类的数据访问入口。
    二、验证
    验证的目的是为了确保Class文件的字节流中的信息不回危害到虚拟机.在该阶段主要完成
    以下四钟验证:
    1.文件格式验证:验证字节流是否符合Class文件的规范,如主次版本号是否在当前虚拟
    机范围内,常量池中的常量是否有不被支持的类型.
    2.元数据验证:对字节码描述的信息进行语义分析,如这个类是否有父类,是否集成了不
    被继承的类等。
    3.字节码验证:是整个验证过程中最复杂的一个阶段,通过验证数据流和控制流的分析,
    确定程序语义是否正确,主要针对方法体的验证。如:方法中的类型转换是否正确,跳转
    指令是否正确等。
    4.符号引用验证:这个动作在后面的解析过程中发生,主要是为了确保解析动作能正确执
    行。
    三、准备
    准备阶段是为类的静态变量分配内存并将其初始化为默认值,这些内存都将在方法区中进
    行分配。准备阶段不分配类中的实例变量的内存,实例变量将会在对象实例化时随着对象
    一起分配在Java堆中。
    四、解析
    该阶段主要完成符号引用到直接引用的转换动作。解析动作并不一定在初始化动作完成之
    前,也有可能在初始化之后。
    五、初始化
    初始化时类加载的最后一步,前面的类加载过程,除了在加载阶段用户应用程序可以通过
    自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正
    开始执行类中定义的Java程序代码。

13、类加载器双亲委派模型机制?
答:当一个类收到了类加载请求时,不会自己先去加载这个类,而是将其委派给父类,由父类
去加载,如果此时父类不能加载,反馈给子类,由子类去完成类的加载。

14、什么是类加载器,类加载器有哪些?
答:实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。
主要有一下四种类加载器:

  1. 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接
    引用。
  2. 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的
    实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
  3. 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)
    来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过
    ClassLoader.getSystemClassLoader()来获取它。
  4. 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。

15、简述java内存分配与回收策率以及Minor GC和 Major GC
答:1. 对象优先在堆的Eden区分配。
2. 大对象直接进入老年代.
3. 长期存活的对象将直接进入老年代.
当Eden区没有足够的空间进行分配时,虚拟机会执行一次Minor GC.Minor Gc通
常发生在新生代的Eden区,在这个区的对象生存期短,往往发生Gc的频率较高,
回收速度比较快;Full Gc/Major GC 发生在老年代,一般情况下,触发老年代GC
的时候不会触发Minor GC,但是通过配置,可以在Full GC之前进行一次Minor
GC这样可以加快老年代的回收速度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值