面试题:JVM

1、介绍下 JVM和内部结构。

JVM是java虚拟机的缩写,java虚拟机是一种规范,其中包括了堆、方法区、虚拟机栈、本地方法栈、程序计数器等部分。java虚拟机主要作用是java字节码的引擎,优化java代码,并把字节码根据解释器翻译成对应平台的机器语言,使得java程序具有跨平台性。

面试题:JVM

面试题:JVM

线程共享的

  • 方法区
  • 直接内存(非运行时数据区的一部分)

线程私有的

  • 程序计数器
  • 虚拟机栈
  • 本地方法栈

2、分别介绍一下程序计数器、虚拟机栈、本地方法栈、堆和方法区。

2.1 程序计数器

程序计数器是线程私有的,可以把程序计数器当成代码的行数记录器,可以保存当前线程运行到哪一行代码,防止线程切换的时候不知道执行到哪了,控制代码流程的东西。字节码解释器通过程序计数器来执行指令。
注意:程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

2.2 虚拟机栈

虚拟机栈是线程私有的,当线程有方法要调用的时候就会把方法压入栈中,方法执行完成或者异常会出栈,一个栈帧就是一个方法,里面还会包括局部变量表、操作数栈、方法出口信息等等。所以方法中的局部变量会跟着方法调用而创建,跟着方法销毁而销毁。

2.3 本地方法栈

本地方法栈也是线程私有的,本质和虚拟机栈没什么区别,主要是遇到native方法的时候才会把方法入本地方法栈,在jvm的具体实现hotspot虚拟机中,将虚拟机栈和本地方法栈统一成栈了。

2.4 堆

堆是线程共享的资源,java程序中每个创建的对象都会在堆中开辟一块内存空间来存储对象信息。堆可以分为新生代和老年代,新生代中又可以分为伊甸园区、幸存者0区和幸存者1区,刚创建的对象都会在伊甸园区,经过一次gc后剩下的对象会进入幸存者0区或者幸存者1区,最后经过多次gc(jvm默认15次)后会进入老年代。新生代内存划分是8:1:1,因为伊甸园区的对象很多。进一步划分的目的是更好地回收内存,或者更快地分配内存。

2.5 方法区

方法区是线程共享的资源,里面主要存放类信息、运行时常量池、类的静态变量、即时编译器的热点代码等等信息,方法区在jdk7之前的实现是永久代,jdk8之后的实现是元空间。

2.6 永久代和元空间的区别是什么?

永久代使用的是jvm内部的内存容量,可以存储多少上限是根据jvm来决定的,元空间使用的是外部的内存,根据本机的内存大小来决定的。

3、讲一下类加载的过程

类加载过程:加载->连接->初始化。连接过程又可分为三步:验证->准备->解析

面试题:JVM

类加载器主要有四种、分别是启动类加载器(加载jdk的类)、扩展类加载器(加载jdk扩展类的类)、应用类加载器(加载classpath下的类)、自定义加载器(加载自定义要加载的类)。

java中使用的是双亲委派机制,意思是从上到下判断哪个类加载器可以加载这个类,从下到上检查类是否被加载过。
面试题:JVM

双亲委派的好处就是防止类的重复加载,也保证jdk的源码不会被篡改。

4、对象创建的过程

面试题:JVM

1、类加载检查,检查这个类有没有被加载,没加载执行类加载过程
2、分配内存,在堆中开辟一块内存空间分配内存
3、初始化零值,为字段附上初始化值
4、设置对象头,对象头里会有对象元数据信息、gc次数、对象hash码等等
5、执行init方法创建完成

5、如何判断对象是否死亡?(两种方法)

堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。

引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。

可达性分析算法
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。

面试题:JVM

7、垃圾收集有哪些算法,各自的特点?

面试题:JVM

为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
面试题:JVM

面试题:JVM
算法分为“标记”和“清除”阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:

效率问题
空间问题(标记清除后会产生大量不连续的碎片)

面试题:JVM

根据老年代的特点特出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
面试题:JVM

分代收集算法

当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。