Java虚拟机模拟面试一

如果你到一家公司面试,被问到GC,肯定会被到1.JVM是什么?2.JVM有什么用?3.JVM优缺点是什么?被问到乍一听是不是有点懵,此时不要慌,静下理清脉络一个一个回答。

1.1 面试官:JVM是什么?

JVM就是java虚拟出来的一个计算机,位于Java与操作系统的中间件。有自己完善的硬件结构,如堆栈、寄存器,还具有相应的执行系统。

1.2 面试官:你刚刚讲JVM有堆栈等组成结构,哪你讲讲JVM有哪些组成结构以及各组成结构具有的作用?
Java运行时数据区的组成由Java栈、本地方法栈、程序计数器、堆、方法区组成。加上外部的直接内存、执行引擎、类装载器子系统等组件,一起协同工作。
Java栈:用于存储局部变量,它是线程私有的,生命周期是该方法调用到执行完成的过程;
本地方法栈:用于支持本地方法调用时使用。
程序计数器:它也是线程私有的,JVM通过读取程序计数器来决定下一条需要执行的字节码指令,进而进行选择语句、循环、跳转、异常处理等操作。
堆:所有线程共享的一块区域,也是内存中最大的一块。在JVM启动时创建,存放实例,也是垃圾收集器最主要的回收部分。
方法区:用于存放已被JVM加载的类信息、常量、静态变量、即使编译后的代码等数据。
直接内存: 这块区域是计算机的内存空间,主要目的是为JAVA NIO服务的。JAVA NIO是基于通道和缓存区,并使用native函数直接分配堆外内存。
执行引擎:由即时编译器,解释器,与GC组成。
类装载器子系统:用来装载.class文件。

1.3 面试官:刚才你解释了JVM各组件的含义,哪它们是如何协同工作?
第一步:把我们写的.java代码,会编译成.class文件。
第二步:启动Java虚拟机,执行JVM初始化操作,常见的比如对堆的最小值,最大值的设置。
第三步:由类加载器子系统负责.class文件的加载,链接,初始化。
加载:类加载器读取.class文件,生成相应的二进制数据并将其保存在方法区域中。
链接:确保.class文件的正确性,为类变量分配内存并初始化变量默认值。
初始化:对静态变量和静态代码块执行初始化工作。
第四步:进入到运行使用阶段。程序读入的位置数据由程序计数器来保存,方法执行完成时清理;局部变量String类型与new出来的对象由堆来保存,GC来清理;哪些基础的数据类型与引用变量地址由Java来保存,方法执行完成时清理。
第五步:肯定执行卸载操作。

1.4 面试官:刚才反复提到GC,哪它有什么用?
防止内存溢出,让我们写代码的时候,不需要去关心比如new出来的对象,系统会自动调用去回收内存。

1.5 面试官:GC什么时候回收?那些需要回收?如何回收?
1.5.1 什么时候回收?
GC经常发生的区域是堆区,堆区还可以细分为新生代、老年代、永久代。新生代分为1个Eden区和2个Survivor区。
注解:
Minor GC : 从年轻代(Eden 和 Survivor 区域)回收的空间;
Major GC: 是清理老年代;
Full GC: 是清理整个堆空间—包括年轻代和老年代。
(1)对象优先在Eden中分配,当Eden中没有足够空间是,虚拟机讲发生一次Minor GC,Java大多数对象都是朝生夕灭,所以Minor GC非常繁琐,而且速度也很快。
(2)Full GC,发生在老年代的GC,当老年代没有足够的空间时即发生Full GC,发生Full GC一般都会有一次Minor GC。大对虾直接进入老年代,如很长的字符串数组,虚拟机提供一个-XX:PretenureSizeThreadhold参数,令大于这个参数值的对象直接在老年代中分配,避免在Eden区和两个Survivor区发生大量的内存拷贝。
(3)发生Minor GC时,虚拟机会检查之前每次晋升到老年代的平均大小是否大于老年代的剩余空间大小,如果大于,则进行一次Full GC,如果小于,则查看HandlePromotionFailure设置是否允许担保失败,如果允许,哪只会进行一次Minor GC,如果不允许,则改为进行一次Full GC。
1.5.2 哪些内存需要回收?
Java采用根搜索算法(GC Root Tracing),当一个对象到GC Roots没有任何引用相连接,用图论的来说就是从GC Roots到这个对象不可达,则证明此对象是不可用的,说明此对象可以被GC。对于这些不可达对象,也不是一下子就被GC,而是至少要经历两次标记过程:
如果对象在进行根搜索算法后发现没有与GC Roots相连接的引用链,那它将会第一次标记并且进行一次筛选,
筛选条件是此对象有没有必要执行finalize()方法,当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用执行过一次,
这两种情况都被视为没有必要执行finalize()方法,对于没有必要执行finalize()方法的将会被GC,
对于有必要有必要执行的,对象在finalize()方法中可能会自救,也就是重新与引用链上的任何一个对象建立关联即可。
1.5.3 如何回收?
选择不同的垃圾收集器组合对“新生代与老年代”进行GC。垃圾收集器所使用的收集算法也不同。
新生代: 复制算法
老年代: “标记-清理”或“标记-整理”

1.6 面试官:如何对象存活判断?
引用计数算法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。
根搜索算法:从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。
Java用的是“根搜索算法”,真正宣告一个对象的死亡,至少要经历两次标记过程。

1.7 面试官:讲一讲GC(垃圾收集器)算法?
复制算法:两个区域A和B,初始对象在A,继续存活的对象被转移到B。此为新生代最常用的算法
标记清除:一块区域,标记要回收的对象,然后回收,一定会出现碎片,那么引出
标记-整理算法:多了碎片整理,整理出更大的内存放更大的对象
分代收集算法:
把Java堆分为新生代和老年代, 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用“复制算法”需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

1.8 面试官:垃圾收集器有哪些?
Serial
新生代;
单线程,GC时必须停止直到其它线程收集结束;JVM运行在client模式下的默认收集器,简单有效;采用复制算法。

ParNew
新生代;
Serial的多线程版本,保留Serial的参数控制,算法等,暂停所有用户线程,采用复制算法;JVM运行在server的首先的新生代收集器;只有它能和CMS配合工作。

Parallel Scavenge
新生代;
采用复制算法,并行的多线程收集器,与ParNew不同的是,关注点不是停顿时间,而是可控的吞吐量,即运行用户代码的时间/(运行用户代码的时间+垃圾收集的时间),可设置最大GC时间和吞吐量大小等参数,也可以让JVM自适应调整策略。

G1
新生代/老年代;
收集器最前沿版本,JDK1.7代替CMS的新产品

CMS
新生代;
Concurrent Mark Sweep, 已获取最短回收停顿为目标,大部分的互联网站及服务端采用的方法,标记-清除算法。

Serial Old
老年代;
Serial的老年版,单线程收集器,采用标记-整理算法,主要是client模式的JVM使用

Parallel Old
老年代;
Parallel Scavenge的老年版,多线程,标记整理算法。

参考:
《Java虚拟机(第二版)》
https://www.breakyizhan.com/java/3697.html
https://www.2cto.com/kf/201804/740298.html
https://www.cnblogs.com/ityouknow/p/5614961.html

你可能感兴趣的:(Java基础,Java,JVM)