JVM面试题

JVM面试题

1.什么是JVM

JVM,即Java Virtual Machine(Java 虚拟机)的简称。

JVM位于操作系统之上,是一个抽象化的计算机,有自己独立的硬体架构,如寄存器、堆、栈等。JVM屏蔽了与不同操作系统平台的相关信息,使得Java程序在编译时只需要生成能在虚拟机上运行的字节码文件,就可以在多种平台上不加修改的运行。

Java语言一个非常重要的特点就是平台无关性,一个重要的原因就是Java程序编译的字节码文件在虚拟机运行时,会被解释器解释为特定操作系统的机器指令代码,这也是Java程序能够一次编译,到处运行的原因。

2.JVM体系结构

JVM面试题_第1张图片

3.类加载器、双亲委派、沙箱安全

类加载器的作用就是用来加载Class文件。除虚拟机自带的加载器外,一般按照顺序还有根加载器(BootstrapClassLoader,负责加载lib包下的jar包和class),拓展类加载器(ExtClassLoader,负责加载lib/ext下的jar包和class)和应用程序类加载器(APPClassLoader,负责加载classpath下的类文件)。

类在进行加载时,首先会调用AppClassLoader的loadClass方法来加载这个类,这个方法会首先调用ExtClassLoader的loadClass方法,同理继续调用BootstrapClassLoader中的loadClass方法,如果BootstrapClassLoader加载到了就直接成功并返回,否则由ExtClassLoader来进行加载,成功则返回,否则由AppClassLoader进行加载。如果没有任何加载器能加载,就会抛出ClassNotFoundException。

所谓双亲委派,就是指类加载的时候会委派给Ext和Bootstrap类加载器来进行加载,如果没加载到才由自己加载。

沙箱安全(双亲委派安全性和健壮性)

package java.lang;

public class String{
	//会报出在类java.lang.String中找不到main方法,因为实际加载的是Bootstrap下的String类
	public static void main(String[] args){
		System.out.println("1231232");
	}
}

优点:

  • 防止重复加载同一个类,加载时向上层进行询问,如果被加载过就不再加载
  • 防止核心类被篡改,即使篡改也不会被加载,一定程度上防止了危险代码的植入。

4.GC如何判断对象可以被回收

1.引用计数法

  • 每个对象都有一个引用计数器,新增一个引用时计数器的值就+1,引用释放时就-1,为0则说明可回收。
  • 如果A引用B,B引用A,则A和B的引用计数器永远不为0,所以它无法解决循环引用问题。

2.可达性分析法

  • 从GC Roots向下分析,搜索所走过的路径称为引用链,到GC Roots没有任何引用链的对象就被虚拟机判断为不可达对象,证明该对象是不可用的,可回收的。
  • 不可达对象并不是立即死亡的。经历死亡至少要经历两个过程
    • 可达性分析中没有引用链
    • 在虚拟机建立的Finalizer队列中判断是否需要执行finalize方法

JVM面试题_第2张图片

GC Roots的对象:

  • 栈中引用的对象
  • 类静态方法引用的对象
  • 常量引用的对象
  • JNI(Java Native Interface)中引用的对象

5.JVM常用垃圾回收算法

垃圾回收主要针对的是JVM的堆内存,分为新生代和老年代,在1.7及之前,还有永久代,它在方法区里保存了class信息,静态变量和常量池等。JDK1.8之后取消了永久代的概念,多出了一个元空间的概念,内存上也独立了出来,跟老年代物理上不再连续,直接使用JVM内存,拓展变得更加容易实现。同时方法区也将一些数据转移了出去,比如类的静态变量,字符串常量池。

JVM面试题_第3张图片

我们平常使用的JVM通常是Hotspot(都是Sun公司的,合并了JRockit)版本,此外还有Zing,J9VM等等。 永久代只是Hotspot独有的概念。

在垃圾回收中,针对新生代的垃圾回收,叫minorGC,也叫YoungGC,YGC,针对老年代的垃圾回收,叫majorGC,由于老年代垃圾回收的时候经常会伴随新生代的垃圾回收,因此也叫FullGC(全局GC),FGC。

  • 标记清除法:

当垃圾回收器将内存扫描后会标记出所有的垃圾对象,然后将他们回收,但是会不断的产生内存碎片,使得内存的使用率越来越低。

  • 复制算法:

准备两块一模一样的内存,当其中一块空间不足时,我们可以将所有需要保留的对象拷贝到另一块内存,然后将原来的这块内存全部清空,这样就既做到了垃圾回收,又做到了碎片整理。但是对于空间的要求比较大,且利用率不高。像新生代中的两块幸存者区就是为了实现这个算法。

  • 标记压缩法:

其实就是在清理垃圾的基础上,多了碎片整理的工作,显然这样的工作不适合高频率的执行,一般当老年代的空间不足时,会触发一次FullGC,这时就会做碎片整理的工作。

6.JVM垃圾回收器和工作流程

JVM面试题_第4张图片

  • Serial工作在新生区,通常搭配Serial Old一起工作,这种垃圾回收器是单线程的,且不支持并发,使用的时候会触发STW,后来随着物理内存的增大,STW造成的服务器卡顿甚至可达数十个小时。

JVM面试题_第5张图片

  • Parallel Scavenge和Parallel Old,它们是多线程的垃圾回收器,对于多CPU的处理器而言,STW的时间会少很多。

JVM面试题_第6张图片

  • ParNew和CMS,ParNew和Parallel几乎没有任何区别,只是为了搭配CMS做了一些调整,CMS则开始支持并发。
    • 第一阶段依然会触发STW,但是只标记GC Root的第一层对象,因此时间很短
    • 第二阶段结束STW,垃圾回收线程并发标记垃圾,由于此时用户线程和GC线程并发工作,可能会产生错标的情况
    • 第三阶段为了解决错标问题,触发STW,GC线程并发重新标记
    • 第四阶段STW结束,GC线程并发清理垃圾,由于此时用户线程也恢复了工作,新产生的垃圾需要等下一轮回收,这些垃圾被称为浮动垃圾。

JVM面试题_第7张图片

  • G1垃圾回收器,JDK1.7之后出现的垃圾回收器,但直到1.9才变为JDK的默认垃圾回收器。机制比较复杂,它允许用户设置一个期望的STW时间,并尽力向这个时间靠拢。
    • G1将整个堆内存划分为若干大小相等的区域,区域的数量默认为2048,它依然保留了新生区、伊甸园区和老年区的概念,只是物理上不再连续,区域被新生代使用时,就是新生区。因此新生区、老年区的大小不再固定,不仅方便了拓展,GC扫描时也不需要扫描整个堆内存,这也大大提高了G1所支持的堆内存的大小。
    • G1本身的设计就是针对大内存的,因此它的很多算法里都有空间换时间的细节。
    • G1在进行垃圾回收时,会对区域进行回收价值排序(区域内垃圾多的排在前面),因此我们在设置STW时间后,它会按照STW时间尽可能多的清理回收价值高的区域。
    • G1在进行内存回收时采用复制算法,无内存碎片产生,G1还对大对象进行单独存放,区域称为humongous,属于老年代,它在被回收前内存位置固定不变,这样就避免了频繁移动大对象的消耗。
    • G1的工作流程和CMS差不多,但是由于不需要扫描全部区域,因此STW时间很短,并在最终的标记阶段采用三色标记法解决CMS可能出现的错标问题。

JVM面试题_第8张图片

7.什么是三色标记法

  • 白色代表该对象没有被垃圾回收器扫描过
  • 灰色代表该对象已经被垃圾收集器扫描过,但该对象上至少存在一个引用还没有被扫描过
  • 黑色代表该对象及其所有引用都已经被垃圾回收器扫描

JVM面试题_第9张图片

最终:

JVM面试题_第10张图片

上图中B、C、H就是可以回收的对象。

8.一个对象从加载到清除的流程

  • JVM首先到方法区找到对象的类型信息,然后再创建对象
  • 在实例化对象时,JVM首先在堆中创建一个对象,并在栈中存入一个引用变量指向这个对象
  • 对象首先会分配在堆内存新生代的Eden区,经过一次轻GC后,如果存活,就进入幸存区。在后续的每次GC中,如果对象一直存活,会在幸存区的两块区域来回拷贝,每移动一次,年龄+1。当年龄到达一定程度(一般是15),就会移动到老年代。
  • 当方法执行结束后,首先会移除栈中的引用
  • 堆中的对象,经过Full GC,就会被标记为垃圾,被GC线程清理

9.什么是OOM与StackOverFlow

OOM即Out Of Memory。内存溢出问题,包括堆内存溢出、栈内存溢出和方法区内存溢出。

OOM的解决不是一蹴而就的,一般系统在发生这种问题时,都会产生dump文件,可以使用专业工具对dump文件进行分析,找出当时异常的对象和线程(比如cpu占用特别高),接着定位到具体代码,通过分析、推理、实践和总结进行调整的。

StackOverFlow即栈溢出,每一个线程启动时,虚拟机都会为它分配一个栈,当线程调用一个方法时,就压入一个栈帧到这个线程的栈中,如果方法的嵌套太多或者互相递归调用,就会出现StackOverflowError溢出异常。

10.什么是STW

STW即Stop The World,是指在执行垃圾回收算法时将JVM内存冻结的一种状态。

在STW状态下,除了GC线程和native方法外,Java所有线程都是停止运行的,而且无法与JVM进行交互。

GC各种优化算法的重点,就是尽可能的减少STW次数,这也是JVM调优中很重要的一部分。

你可能感兴趣的:(面试,jvm,面试,秋招)