16. 吞吐量优先和响应时间优先的回收器是哪些?
- 吞吐量优先:Parallel Scavenge + Parallel Old(多线程并行)->简称: PS+PO -> JDK1.8默认
- 响应时间优先:ParNew + CMS(并发回收垃圾)
17. GC一定会导致停顿吗,为什么一定要停顿?
GC进行时必须暂停所有Java执行线程,称为Stop The World。
原因:可达性分析过程中不允许对象的引用关系还在变化,否则无法保证可达性分析的准确性。GC只会在“安全点”和“安全区”内开始。
18. Full GC后老年代的空间反而变小?
HotSpot虚拟机的Full GC实现中,默认新生代里所有活的对象都要晋升到老年代,实在晋升不了才会留在新生代。如果Full GC的时候,老年代里的对象几乎没有死掉的,而新生代又要晋升活对象上来,那么Full GC结束后老年代的使用量自然就上升了。
19. 发生Young GC的时候需要扫描老年代的对象吗?
不扫描老年代。
在分代收集中,新生代的规模一般都比老年代要小许多,新生代的收集也比老年代要频繁许多,如果回收新生代时也不得不同时扫描老年代的话,那么Young GC的效率可能下降不少。显然是不可能区扫描老年代的。
大多数垃圾收集器(G1不同),通过CardTable来维护老年代对年轻代的引用,CardTable中标记每个Card的状态,它用BitMap来实现。 如果一个OLD区CardTable中有对象指向Y区,就将它设为Dirty,下次扫描时,只需要扫描DirtyCard。
20. HotSpot虚拟机为什么要分为新生代和老年代?
HotSpot根据对象存活周期将内存分块,它把Java堆分为新生代(Eden)和老年代(Survivor),然后根据各个年代的特点采用合适的收集算法。在新生代中,每次GC时都有大批对象死去,只有少量存活,所以选用复制算法(只需要付出少量存活对象的复制成本就可以完成收集)。在老年代中,对象存活率高、没有额外空间对它进行分配担保,所以使用“标记-清理”或“标记-整理”算法来回收。
21. HotSpot虚拟机GC的分类(准确分为两大种):
Partial GC(分代式收集):并不收集整个GC堆的模式
- Young GC:只收集young gen的GC
- Old GC:只收集old gen的GC。只有CMS的concurrent collection是这个模式
- Mixed【每克死特】 GC:收集整个young gen以及部分old gen的GC。->只有G1有这个模式
Full GC(全部收集):收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。
22. HotSpot 虚拟机GC的触发条件:
Young GC:
Eden区分配满的时候触发,Young GC时,有部分存活对象会晋升到old gen,所以young GC后old gen的占用量通常会有所升高。
Full GC之前会进行一次Young GC。
---- ----
CMS 垃圾回收器:配置-XX:+CMSScavengeBeforeRemark,在CMS GC前启动一次young gc,目的在 于减少old gen对ygc gen的引用,降低remark时的开销。
Full GC:
- 老年代没有足够的空间时,发生Full GC
- 准备要触发Young GC时,统计要晋升old gen的对象大于目前old gen剩余的空间时,则不会触发Young GC,而是触发Full GC(除了CMS收集器之外,Full GC至少伴随一次Young GC);
- 如果有永久代的话,在永久代需要分配空间但已经没有足够空间时,也要触发一次Full GC(JDK1.8后,没有永久代);
- System.gc()默认触发Full GC;
- heap dump(堆转储文件,是一个Java进程在某个时间点上的内存快照)带GC默认触发Full GC;
- CMS GC时出现Concurrent Mode Failure会导致一次Full GC的产生。
23. 安全点与安全区:
OopMap:
- 概念:记录了栈上本地变量到堆上对象的引用关系。
- 作用:避免全栈扫描,加快枚举根节点的速度;实现HotSpot JVM的准确式GC。
安全点:
- 概念:JVM在一些特定的位置记录OopMap信息,这些位置称为“安全点”,运行的程序(获得CUP时间的线程)只有在“安全点”才能GC。
- 如何让所有线程“跑”到“安全点”?
- 抢先式中断:不需要线程的执行代码主动配合,GC发生时,中断所有线程,如果发现有线程不再“安全点”上,就恢复线程,让它“跑”到“安全点”上(几乎不用这种方案)。
- 主动式中断:设置一个标志,每个线程主动轮询这个标志,发现中断标志为真就挂起自己。轮询标志的地方:它与“安全点”重合,然后再加上创建对象分配内存的地方(HotSpot JVM使用)。
安全区:
- 概念:在一段代码片段中,引用关系不会发生变化,在该区域GC都是安全的,它可以理解成是“安全点”的拓展。
- 作用:解决没有得到CPU分配时间的线程,无法响应JVM的中断请求,“跑”到“安全点”去挂起的问题。
- 使用:当用户线程执行到“安全区”里面的代码时,首先标识自己进入了“安全区”,在这段时间里发起GC,就不用管标识自己为“安全区”的线程,在线程离开安全区域时,会检查是否正在执行GC,如果执行GC就等完成后再离开安全区域。
24. Happens-Before规则?(@&@)
作用:
先行发生原则是判断数据是否存在竞争、线程是否安全的主要依据。
口诀:
如果两个操作之间具有happen-before关系,那么前一个操作的结果就会对后面的一个操作可见。是Java内存模型中定义的两个操作之间的偏序关系。
常见规则:
- 程序顺序规则:
- 一个线程内,按照程序代码顺序,书写在前面的操作先行发生与书写在后面的操作。
- 锁规则:
- 一个unlock操作"先行发生"与后面对同一个锁的lock操作,这里必须指同一个锁,后面指的是时间上的先后顺序。目的是先解锁对其他线程可见。
- 传递性:
- 如果A happen-before B,且B happen-before C,那么A happen - before C。
- volatile变量规则:
- 对一个volatile变量的写操作"先行发生"于后面对这个变量的读操作,这里的后面同样指时间上的先后顺序。
- 线程启动规则:
- Thread对象的start()"先行发生"于此线程的每一个动作。
- 线程终止规则:
- 线程中的所有操作都"先行发生"于对此线程的终止检测,我们可以通过thread.join方法结束,thread.isAlive的返回值等手段检测到线程已经终止执行。
- 线程中断规则:
- 对线程interrupt方法的调用"先行发生"于被中断线程的代码监测到中断时间的发生,可以通过interrupt方法检测到是否又中断发生
25. 内存屏障的汇编指令是啥?
内存屏障概念:
它是一个CPU的指令,可以保证一些特定操作的执行顺序和影响一些数据的可见性。
硬件内存屏障 X86的指令:
- sfence(写串行化):在sfence指令前的写操作当必须在sfence指令后的写操作前完成。
- lfence(读串行化):在lfence指令前的读操作当必须在lfence指令后的读操作前完成。
- mfence(读写串行化):在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。
- 原子指令:
- 如x86上的”lock …” 指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。
- Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序。
JVM级别如何规范(JSR133):
- LoadLoad屏障:
- 对于(这样的语句)Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
- StoreStore屏障:
- 对于Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
- LoadStore屏障:
- 对于Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
- StoreLoad屏障:
- 对于Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。
26. new一个对象的流程:***
- 首先判断类是否被加载过:JVM收到new指令后,先去静态常量池,寻找这个类的符号引用(全限定名),检查符号引用代表的类是是否已经被加载、解析和初始化过。如果不存在,先执行类加载过程。
- 根据类信息,先给对象分配内存(包括本类和父类的所有实例变量,不包括上面的静态变量),并设置默认值。
- 执行初始化代码实例化,先初始化父类再初始化子类,赋予给定值。
- 如果存在引用对象,还需要将栈对象指向到堆内存中的实际对象。
27. jvm监控系统是通过jmx做的么?(先pass)
JMX:
JMX(Java Management Extensions,即Java管理扩展)在Java编程语言中定义了应用程序以及网络管理和监控的体系结构、设计模式、应用程序接口以及服务。通常使用JMX来监控系统的运行状态或管理系统的某些方面,比如清空缓存、重新加载配置文件等。
解答:
一般都是,要是记录比较详细的性能定位指标,都会导致进入safepoint(安全点),从而降低了线上应用性能。
例如 jstack,jmap打印堆栈,打印内存使用情况,都会让jvm进入safepoint,才能获取线程稳定状态从而采集信息。
同时,JMX暴露向外的接口采集信息,例如使用jvisualvm,还会涉及rpc和网络消耗,以及JVM忙时,无法采集到信息从而有指标断点。这些都是基于 JMX 的外部监控很难解决的问题。所以,推荐使用JVM内部采集 JFR,这样即使在JVM很忙时,也能采集到有用的信息。