1、JVM内存模型
(1)程序计数器:线程私有,用来程序跳转,流程控制
(2)方法区(1.8叫元数据区):线程共享,用于存储类信息、常量、静态变量等信息
(3)Java虚拟机栈:线程私有,用于方法调用Java虚拟机栈会出现两种错误:StackOverFlowError和OutOfMemoryError
(4)堆:线程私有,主要的内存区域,存储对象实例,垃圾回收主要针对这一块。
(5)本地方法栈:线程共享,本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
2、G1和CMS垃圾回收器
(1)CMS收集器:是一种以获取最短回收停顿时间为目标的收集器。
过程:
①初始标记:标记GC Roots能直接关联到的对象,需要在safepoint位置暂停所有执行线程。---->STW
②并发标记:进行GC Roots Tracing,遍历完从root可达的所有对象。该阶段与工作线程并发执行。
③重新标记:修正并发标记期间因用户程序继续运作而导致标记产生表动的那一部分对象的标记记录。需要在safepoint位置暂停所有执行线程。--->STW
④并发清除: 开启用户线程,同时GC线程开始对为标记的区域做清扫。
优点:并发收集、低停顿。
缺点:
①CMS收集器对CPU资源非常敏感。
②CMS收集器无法处理浮动垃圾(Floating Garbage)。
③CMS收集器是基于标记-清除算法,该算法缺点都有:标记和清除效率低/产生大量不连续的内存碎片。
④停顿时间是不可预期的。
(2)G1收集器:重新定义了堆空间,打破了原有的分代模型,将堆划分为一个个区域。这么做的目的是在进行收集时不必在全堆范围内进行,这是它最显著的特点。
过程:
①初始标记:标记GC Roots可以直接关联的对象,该阶段需要线程停顿但是耗时短。---->STW
②并发标记:寻找存活的对象,可以与其他程序并发执行,耗时较长。
③最终标记:并发标记期间用户程序会导致标记记录产生变动(好比一个阿姨一边清理垃圾,另一个人一边扔垃圾)虚拟机会将这段时间的变化记录在Remembered Set Logs中。最终标记阶段会向Remembered Set合并并发标记阶段的变化。这个阶段需要线程停顿,也可以并发执行---->STW
④筛选回收:对每个Region的回收成本进行排序,按照用户自定义的回收时间来制定回收计划
优点:
①空间整合:G1使用Region独立区域概念,G1利用的是标记复制法,不会产生垃圾碎片
②分代收集:G1可以自己管理新生代和老年代
③并行于并发:G1可以通过机器的多核来并发处理STW停顿,减少停顿时间,并且可不停顿java线程执行GC动作,可通过并发方式让GC和java程序同时执行。
④可预测停顿:G1除了追求停顿时间,还建立了可预测停顿时间模型,能让制定的M毫秒时间片段内,消耗在垃圾回收器上的时间不超过N毫秒
缺点:
G1 需要记忆集(具体来说是卡表)来记录新生代和老年代之间的引用关系,这种数据结构在G1中需要占用大量的内存,可能达到整个堆内存容量的20%甚至更多。而且G1中维护记忆集的成本较高,带来了更高的执行负载,影响效率。
3、wait/await和sleep区别
(1)两者最主要的区别在于:sleep方法没有释放锁,而wait方法释放了锁 。
(2)两者都可以暂停线程的执行。
(3)wait通常被用于线程间交互/通信,sleep通常被用于暂停执行。
(4)wait()方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll()方法。sleep()方法执行完成后,线程会自动苏醒。或者可以使用wait(long timeout)超时后线程会自动苏醒。
4、Spring生命周期,几种scope区别
5、Linux常用指令
6、线程池底层实现原理
7、手撕代码。牛客题霸上的原题,可以去看看:NC14