JVM面试题(二)
# 1、怎么查看服务器默认的垃圾回收器是哪一个?
可以使用一个参数:-XX:+PrintCommandLineFlags可以打印所有的参数,包括使用的垃圾回收器。
以下是演示:
java -XX:+PrintCommandLineFlags -version
Windows:
C:\Users\HaC> java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=266928960 -XX:MaxHeapSize=4270863360 -XX:+PrintCommandLineFlags -XX:+UseCompress
edClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
Linux:
[root@GZSB-CJB-SHH1-14-GEMINI-0 ~]# java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=261339776 -XX:MaxHeapSize=4181436416 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
可以看到是使用了 UseParallelGC
还可以使用 jmap -heap 进程id 命令查看收集器,还可以查看新生代、老年代的空间大小。
[root@VM_0_12_centos ~]# jmap -heap 19505
Attaching to process ID 19505, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.171-b11
using thread-local object allocation.
Garbage-First (G1) GC with 1 thread(s)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 268435456 (256.0MB)
NewSize = 1363144 (1.2999954223632812MB)
MaxNewSize = 160432128 (153.0MB)
OldSize = 5452592 (5.1999969482421875MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 1048576 (1.0MB)
Heap Usage:
G1 Heap:
regions = 256
capacity = 268435456 (256.0MB)
used = 66499440 (63.41880798339844MB)
free = 201936016 (192.58119201660156MB)
24.772971868515015% used
G1 Young Generation:
Eden Space:
regions = 18
capacity = 74448896 (71.0MB)
used = 18874368 (18.0MB)
free = 55574528 (53.0MB)
25.35211267605634% used
Survivor Space:
regions = 1
capacity = 1048576 (1.0MB)
used = 1048576 (1.0MB)
free = 0 (0.0MB)
100.0% used
G1 Old Generation:
regions = 46
capacity = 58720256 (56.0MB)
used = 46576496 (44.41880798339844MB)
free = 12143760 (11.581192016601562MB)
79.31929997035435% used
34372 interned Strings occupying 3886072 bytes.
Garbage-First (G1) GC with 1 thread(s) ,可以看到是使用了 G1 垃圾收集器。Eden Space、Survivor Space、G1 Old Generation分别说明了Eden区、Survivor区、老年代的大小。
常用的命令行工具还有:jps、jstat、jinfo、jmap、jstack。而更多的可视化工具如jconsole、visualVM等暂不介绍,大家有兴趣请参阅相关文档。查看新生代内存占用、老年代内存占用、其他区内存占用和GC等四大方面的指标。
# 2、为什么要有GC,哪些内存对象需要回收?
对于一个Java开发者来说,了解过Java内存区域的都知道,Java内存区域分了堆、栈、程序计数器等等。
Java的程序计数器,栈内存 ,他们随线程生,随线程灭,方法结束后内存也就回收了。
一个字符串“abc”已经进入常量池,但是当前系统没有任何一个String对象引用了做“abc”的字面量,那么,如果发生垃圾回收并且有必要时,“abc”就会被系统移出常量池。
常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
当Java虚拟机发现内存资源紧张的时候,就会自动地去清理无用变量所占用的内存空间,为我们的程序提升更高的性能。
# 3、如何判断对象需要回收?
一般常见的两种回收判断算法:
# 2.1、 引用计数算法
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1。
任何时刻计数器为0的对象就是不可能再被使用的。

该方法实现简单,效率高,但是它很难它很难解决对象之间相互循环引用的问题。比如图中的 Object3 和Object4相互引用,引用计数不可能为0,虽然它们已经没有被Root引用了。
所以,大多数jvm判断对象是否存活基本并没有采取该方法。
# 2.2、可达性分析算法(根搜索算法)
这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain)。
当一个对象到GC Roots没有任何引用链相连时(不可达)则证明此对象是不可用的。
要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。

被认为GC Roots的有以下几种:
- 虚拟机栈中引用的对象
- 方法区中静态属性、常量引用的对象
- Native方法引用的对象
# 4、JVM中一次完整的GC流程是怎样的
GC是垃圾收集的意思,Java语言没有提供释放已分配内存的显示操作方法。开发者不用担心内存管理,因为垃圾收集器会自动进行管理。
首先,分代收集法是目前虚拟机(包括HotSpot VM)收集器都是采用该方法。所以一般是回答分代收集算法的垃圾回收过程。

# 分代收集算法的GC过程:
(1)在年轻代中,Eden区提供堆内存如果满了,Eden进行MinorGC,将存活的对象→from ,Eden区清空;
(2)Eden区再次满, Eden 区和 from 区同时进行 Minor GC,把存活对象放入 to 区,Eden和from 同时清空;
如果在to区中的对象仍然存活,则把对象标志 +1。
(3)重复(2)的操作, 某些对象在反复 Survive 15 次后,或者Eden+from 的存活对象 > to ,这些对象就只能放到老年代了,如果老年代放不下了,就进行Full GC);
(4)当 Old 区也被填满时,进行 Full GC,对 Old 区进行垃圾回收。
可以通过参数 SurvivorRatio 手动配置 Eden 区和单个 Survivor 区的比例,默认为 8。可以通过参数–XX:SurvivorRatio 来设定,即将堆内存中年轻代划分为
8:1:1
# 5、GC监控怎么看?
jdk自带的命令:
- jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
- jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
- jmap,JVM Memory Map命令用于生成heap dump文件
- jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
- jstack,用于生成java虚拟机当前时刻的线程快照。
- jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。
工具:
jdk自带监控工具:jconsole和jvisualvm,位于bin目录
# 6、有没有遇到过OutOfMemoryError和StackOverflowError,这两者的区别是什么,怎么解决?
OOM问题。
1、OutOfMemoryError 常见场景:
Java.lang.OutOfMemoryError: PermGen space
程序中使用了大量的jar或class,使java虚拟机装载类的空间不够。 **解决:**增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,如:
-XX:PermSize=64M -XX:MaxPermSize=128mjava.lang.OutOfMemoryError: Java heap space java虚拟机创建的对象太多,在进行垃圾回收之前,虚拟机分配的到堆内存空间已经用满了。
**解决:**设置堆的起始大小、最大空间,如
-Xms256m -Xmx1024mjava.lang.OutOfMemoryError: unable to create new native thread
环境提供的服务器配置偏低,而项目本身为了性能,大量的使用的线程,因为线程的开辟比new Object是需要很大空间的。创建线程数超过了操作系统的限制。
解决:减少程序的线程数量;或者增大服务器的系统线程限制数量。
2、StackOverflowError 常见场景:
StackOverflowError 是一个java中常出现的错误:在jvm运行时的数据区域中有一个java虚拟机栈,当执行java方法时会进行压栈弹栈的操作。在栈中会保存局部变量,操作数栈,方法出口等等。jvm规定了栈的最大深度,当执行时栈的深度大于了规定的深度,就会抛出StackOverflowError错误。
java.lang.StackOverflowError
出现这种异常,大多是由于循环调用
典型的例子:
public class StackOverFlowErrorDemo {
public static void HaC(){
HaC();
}
public static void main(String[] args) {
HaC();
}
}
解决方案:
把递归调用函数改用while或者for循环来实现 。
增大栈的大小值。(如 -Xss100m )
改用堆内存。(把局部变量改成全局静态变量)
# 7、怎么看死锁的线程?
Jconsole Jconsole是JDK自带的图形化界面工具,使用JDK给我们的的工具JConsole,可以通过打开cmd然后输入jconsole打开。
jstack
通过jstack命令,可以获得线程的栈信息。死锁信息会在非常明显的位置(一般是最后)进行提示。
演示一下:
1、用jps查找当前Java进程:
[root@VM-8-8-centos ~]# jps
9368 Jps
15850 jar
3244 jar
2、 jstack -l 15850 查看进程的堆栈情况,拉到最后就有死锁的信息了 :

# 8、MinorGC,MajorGC、FullGC都什么时候发生?
MinorGC在年轻代空间(Eden区)不足的时候发生。
MajorGC指的是老年代的GC,出现MajorGC一般经常伴有MinorGC。
但是新的虚拟机已经没有MajorGC这一说法了。Major GC的速度一般会比Minor GC慢10倍以上。
FullGC有以下几种情况
FullGC 是清理整个堆空间—包括年轻代和老年代、永久代(如果有)
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
# 9、三色标记
三色标记是一种并发标记算法,用于在不停顿应用线程的情况下遍历对象图。
三色含义:
- 白色:尚未被GC访问到的对象(初始状态所有对象都是白色)
- 灰色:对象本身已被访问,但其引用的子对象还没有被检查
- 黑色:对象本身和其所有子对象都已被访问完成