您现在的位置是:首页 > 文章详情

JVM常见面试题解析

日期:2019-07-31点击:375

总结了JVM一些经典面试题,分享出我自己的解题思路,希望对大家有帮助,有哪里你觉得不正确的话,欢迎指出,后续有空会更新。

1.什么情况下会发生栈内存溢出。
思路:描述栈定义,再描述为什么会溢出,再说明一下相关配置参数,OK的话可以给面试官手写是一个栈溢出的demo。

我的答案:

栈是线程私有的,他的生命周期与线程相同,每个方法在执行的时候都会创建一个栈帧,用来存储局部变量表,操作数栈,动态链接,方法出口等信息。 局部变量表又包含基本数据类型,对象引用类型
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常,方法递归调用产生这种结果。
如果Java虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是无法申请到足够的内存去完成扩展,或者在新建立线程的时候没有足够的内存去创建对应的虚拟机栈,那么Java虚拟机将抛出一个OutOfMemory 异常。 (线程启动过多)
参数 -Xss 去调整JVM栈的大小
2.详解JVM内存模型
思路:给面试官画一下JVM内存模型图,并描述每个模块的定义,作用,以及可能会存在的问题,如栈溢出等。

我的答案:

JVM内存结构

程序计数器: 当前线程所执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址,线程私有。

Java虚拟栈: 存放基本数据类型、对象的引用、方法出口等,线程私有。

Native方法栈: 和虚拟栈相似,只不过它服务于Native方法,线程私有。

Java堆: java内存最大的一块,所有对象实例、数组都存放在java堆,GC回收的地方,线程共享。

方法区: 存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据等。 (即永久带),回收目标主要是常量池的回收和类型的卸载,各线程共享

3.JVM内存为什么要分成新生代,老年代,持久代。 新生代中为什么要分为Eden和Survivor。
思路: 先讲一下JAVA堆,新生代的划分,再谈谈它们之间的转化,相互之间一些参数的配置(如: –XX:NewRatio,–XX:SurvivorRatio等),再解释为什么要这样划分,最好加一点自己的理解。

我的答案:

1)共享内存区划分
共享内存区 = 持久带 + 堆
持久带 = 方法区 + 其他
Java堆 = 老年代 + 新生代
新生代 = Eden + S0 + S1
2)一些参数的配置
默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ,可以通过参数 –XX:NewRatio 配置。
默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定)
Survivor区中的对象被复制次数为15(对应虚拟机参数 -XX:+MaxTenuringThreshold)
3)为什么要分为Eden和Survivor?为什么要设置两个Survivor区?
如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。 老年代很快被填满,触发Major GC.老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多,所以需要分为Eden和Survivor。
Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。
设置两个Survivor区最大的好处就是解决了碎片化,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空; 等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)

  1. JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代
    思路:先描述一下Java堆内存划分,再解释Minor GC,Major GC,full GC,描述它们之间转化流程。

我的答案:

Java堆 = 老年代 + 新生代
新生代 = Eden + S0 + S1
当 Eden 区的空间满了, Java虚拟机会触发一次 Minor GC,以收集新生代的垃圾,存活下来的对象,则会转移到 Survivor区。
大对象(需要大量连续内存空间的Java对象,如那种很长的字符串) 直接进入老年态 ;
如果对象在Eden出生,并经过第一次Minor GC后仍然存活,并且被Survivor容纳的话,年龄设为1,每熬过一次Minor GC,年龄+1, 若年龄超过一定限制(15),则被晋升到老年态 。即 长期存活的对象进入老年态 。
老年代满了而 无法容纳更多的对象 ,Minor GC 之后通常就会进行Full GC,Full GC 清理整个内存堆 – 包括年轻代和年老代 。
Major GC 发生在老年代的GC , 清理老年区 ,经常会伴随至少一次Minor GC, 比Minor GC慢10倍以上 。
5.你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms和G1,包括原理,流程,优缺点。
思路:一定要记住典型的垃圾收集器,尤其cms和G1,它们的原理与区别,涉及的垃圾回收算法。

我的答案:

1)几种垃圾收集器:
Serial收集器:单线程的收集器,收集垃圾时,必须stop the world,使用复制算法。
ParNew收集器:Serial收集器的多线程版本,也需要stop the world,复制算法。
Parallel Scavenge收集器: 新生代收集器,复制算法的收集器,并发的多线程收集器,目标是达到一个可控的吞吐量。 如果虚拟机总共运行100分钟,其中垃圾花掉1分钟,吞吐量就是99%。
Serial Old收集器:是Serial收集器的老年代版本,单线程收集器,使用标记整理算法。
Parallel Old收集器:是Parallel Scavenge收集器的老年代版本,使用多线程,标记-整理算法。
CMS(Concurrent Mark Sweep) 收集器:是一种以获得最短回收停顿时间为目标的收集器, 标记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除 ,收集结束会产生大量空间碎片。
G1收集器: 标记整理算法实现, 运作流程主要包括以下:初始标记,并发标记,最终标记,筛选标记 。 不会产生空间碎片,可以精确地控制停顿。
2)CMS收集器和G1收集器的区别:
CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;
G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用;
CMS收集器以最小的停顿时间为目标的收集器;
G1收集器可预测垃圾回收的停顿时间
CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。
6.简单说说你了解的类加载器,可以打破双亲委派么,怎么打破。
思路:先说明一下什么是类加载器,可以给面试官画个图,再说一下类加载器存在的意义,说一下双亲委派模型,最后阐述怎么打破双亲委派模型。

我的答案:

1) 什么是类加载器?
类加载器就是根据指定全限定名称将class文件加载到JVM内存,转为Class对象。

启动类加载器(Bootstrap ClassLoader): 由C++语言实现(针对HotSpot),负责将存放在lib目录或-Xbootclasspath参数指定的路径中的类库加载到内存中。
其他类加载器: 由Java语言实现,继承自抽象类ClassLoader。 如:
扩展类加载器(Extension ClassLoader): 负责加载libext目录或java.ext.dirs系统变量指定的路径中的所有类库。
应用程序类加载器(Application ClassLoader)。 负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。 一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
2)双亲委派模型
双亲委派模型工作过程是:

如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。 每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。

双亲委派模型图:

3)为什么需要双亲委派模型?
在这里,先想一下,如果没有双亲委派,那么用户是不是可以 自己定义一个java.lang.Object的同名类 , java.lang.String的同名类 ,并把它放到ClassPath中,那么 类之间的比较结果及类的唯一性将无法保证 ,因此,为什么需要双亲委派模型? 防止内存中出现多份同样的字节码

4)怎么打破双亲委派模型?
打破双亲委派机制则不仅 要继承ClassLoader 类,还要 重写loadClass和findClass 方法。

7.怎么打出线程栈信息。
思路:可以说一下jps,top ,jstack这几个命令,再配合一次排查线上问题进行解答。

我的答案:

输入jps,获得进程号。
top -Hp pid 获取本进程中所有线程的CPU耗时性能
jstack pid命令查看当前java进程的堆栈状态
或者 jstack -l > /tmp/output.txt 把堆栈信息打到一个txt文件。
可以使用fastthread 堆栈定位,http://fastthread.io/
需要java学习路线图的私信笔者“java”领取哦!另外喜欢这篇文章的可以给笔者点个赞同,关注一下,每天都会分享Java相关文章!还有不定时的福利赠送,包括整理的学习资料,面试题,源码等~~

原文链接:https://yq.aliyun.com/articles/712207
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章