首先我让小哥哥介绍了一下自己。
然后开始了正式的面试因为是应届生所以问的全部是理论知识,我们一起来先看看我问的所有问题。
2、JVM内存为什么要分成新生代,老年代,持久代。新生代中为什么要分为Eden和Survivor。
3、你知道哪几种垃圾收集器,各自的优缺点,重点讲下CMS和G1,包括原理,流程,优缺点。
4、JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代
6、ArrayList和LinkedList的区别,如果一直在List的尾部添加元素,用哪个效率高?
8、使用Redis有哪些好处?
正式开始面试
介绍完自己后,我问了第一个破冰问题:
- 程序计数器:是记录当前线程执行程序的位置,改变计数器的值来确定执行的下一条指令,比如循环、分支、方法跳转、异常处理,线程恢复都是依赖程序计数器来完成。
- Java虚拟机栈:是线程私有,生命周期与线程相同。创建线程的时候就会创建一个java虚拟机栈。虚拟机执行java程序的时候,每个方法都会创建一个栈帧,栈帧存放在java虚拟机栈中,通过压栈出栈的方式进行方法调用。栈帧又分为一下几个区域:局部变量表、操作数栈、动态连接、方法出口等。
- 本地方法栈:为虚拟机使用到本地方法服务(native)。本地方法栈为线程私有,功能和虚拟机栈非常类似。线程在调用本地方法时,来存储本地方法的局部变量表,本地方法的操作数栈等等信息。本地方法:是非java语言实现的方法,例如,java调用C语言,来操作某些硬件信息。
- 堆:是被所有线程共享的区域,实在虚拟机启动时创建的。堆里面存放的都是对象的实例(new出来的对象都存在堆中)。
- 方法区(MethodArea):方法区主要是放一下类似类定义、常量、编译后的代码、静态变量等。
解析:这个问题如果大家遇到了那么解答的思路是:描述一下JVM内存模型图,并描述每个模块的定义,作用,以及可能会存在的问题,如栈溢出等。
从这个问题的回答上我能感受到他对JVM的了解程度有一定的深入,到这来我的态度也改观了很多,于是我追问了一下,想要了解到他的层次究竟如何?
- 共享内存区=持久带+堆
- 持久带=方法区+其他
- Java堆=老年代+新生代
- 新生代=Eden+S0+S1
默认情况下,新生代(Young)与老年代(Old)的比例的值为1:2,可以通过参数–XX:NewRatio配置,Edem:from:to比例为8比1比1。
Survivor区中的对象被复制次数为15,如果没有Survivor,Eden区每进行一次MinorGC,存活的对象就会被送到老年代。老年代很快被填满,触发MajorGC.老年代的内存空间远大于新生代,进行一次FullGC消耗的时间比MinorGC长得多,所以需要分为Eden和Survivor。
Survivor的存在意义,就是减少被送到老年代的对象,进而减少FullGC的发生,Survivor的预筛选保证,只有经历16次MinorGC还能在新生代中存活的对象,才会被送到老年代。
设置两个Survivor区最大的好处就是解决了碎片化,刚刚新建的对象在Eden中,经历一次MinorGC,Eden中的存活对象就会被移动到第一块SurvivorSpaceS0,Eden被清空;等Eden区再满了,就再触发一次MinorGC,Eden和S0中的存活对象又会被复制送入第二块SurvivorSpaceS1,这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生。
解析: 先讲一下JAVA堆,新生代的划分,再谈谈它们之间的转化,再解释为什么要这样划分,最好加一点自己的理解。
- Serial收集器:单线程的收集器,收集垃圾时,必须stoptheworld,使用复制算法。
- ParNew收集器:Serial收集器的多线程版本,也需要stoptheworld,复制算法。
- ParallelScavenge收集器:新生代收集器,复制算法的收集器,并发的多线程收集器,目标是达到一个可控的吞吐量。
- SerialOld收集器:是Serial收集器的老年代版本,单线程收集器,使用标记整理算法。
- ParallelOld收集器:是ParallelScavenge收集器的老年代版本,使用多线程,标记-整理算法。
- CMS收集器:是一种以获得最短回收停顿时间为目标的收集器,标记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除,收集结束会产生大量空间碎片。
- G1收集器:标记整理算法实现,运作流程主要包括以下:初始标记,并发标记,最终标记,筛选标记。不会产生空间碎片,可以精确地控制停顿。
CMS和G1的区别
- CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;
- G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用;
- CMS收集器以最小的停顿时间为目标的收集器;
- G1收集器可预测垃圾回收的停顿时间
- CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
- G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。
解析:一定要记住典型的垃圾收集器,尤其cms和G1,它们的原理与区别,涉及的垃圾回收算法。
如果对象在Eden出生,并经过第一次MinorGC后仍然存活,并且被Survivor容纳的话,年龄设为1,每熬过一次MinorGC,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。即长期存活的对象进入老年态。
老年代满了而无法容纳更多的对象,MinorGC之后通常就会进行FullGC,FullGC清理整个内存堆–包括年轻代和年老代。
MajorGC发生在老年代的GC,清理老年区,经常会伴随至少一次MinorGC,比MinorGC慢10倍以上。这是我的大致的理解具体的有做过demo更深入的暂时没有更深入的了解
解析:先描述一下Java堆内存划分,再解释MinorGC,MajorGC,fullGC,描述它们之间转化流程。
- ArrayList:他的内部使用数组的数据结构。它的默认容量为10,当容量不够的时候,就会自动扩大容量,1.5倍; 在遍历时通过索引序号访问比较快,而使用迭代器的效果最慢。
- LindedList:内部使用了双向链表的方式。 LinkdeList的效率会高一些,因为他只需要改变一些尾部的指针就可以了。而ArrayList必须判断是否会越界,如果会,就需要扩大容量。这个过程就会影响到效率。
我:这部分基础挺扎实的(在前面JVM的知识点可以看出他的基本功很扎实所以没有问更多的),那我问问Redis相关的,简单讲下Redis的数据类型吧。
- 字符串:string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。string类型是Redis最基本的数据类型,一个键最大能存储512MB。
- 哈希: Redis hash 是一个键值(key=>value)对集合。Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
- List:Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
- Set: Redis的Set是string类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
- Zset:Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。Redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
- 速度快:因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
- 支持丰富数据类型:支持string,List,set,sortedset,hash
- 支持事务:操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
- 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
不知不觉面试了差不多两个小时了,可能是我第一次面试应届生所以问的有些简单,有些复杂,不过整体来看应届生给我的感受和一年多两年的经验的很多同事的感受类似,不禁有些感叹,现在JAVA应届生也是越来越优秀了,如果你是应届生,遇到我这样喜欢问得深入的面试官,你能应对吗?
评论0