JVM面试问题(二)
响应时间
单次GC的暂停时间是一个统计平均值,因为单次GC的时间其实是不可控的,但是取了平均值,GC就可以动态去调整heap的大小,或者其他的一些GC参数,从而保证每次GC的时间不会超过这个平均值。
我们可以通过设置:-XX:MaxGCPauseMillis=
不管怎么设置这个参数,总体需要被GC的对象肯定是固定的,如果单次GC 暂停时间比较短,可能会需要减少heap size的大小,那么回收的对象也比较少。这样就会导致GC的频率增加。从而导致GC的总时间增加,影响程序的Throughput。
吞吐率
吞吐率指在一定时间内,应用程序执行业务逻辑的时间占总时间的比例。
我们可以通过设置:-XX:GCTimeRatio=nnn 来控制。
如果没有达到throughput的目标,那么GC可能会去增加heap size,从而减少GC的执行频率。但是这样会增加单次的Maximum Pause-Time。
如果throughput和maximum pause-time的参数同时都设置的话,JVM会去尝试去动态减少heap size的大小,直到其中的一个目标不能满足为止。
相对而言,G1更加偏重于响应时间,而ZGC更加偏重于吞吐率。
一些垃圾回收策略和算法可能会追求更高的吞吐量,即更高的业务逻辑执行效率,但在追求吞吐量的同时可能会导致较长的垃圾回收暂停时间。另一方面,一些策略可能注重更低的垃圾回收暂停时间,以提高系统的响应性,但可能会降低业务逻辑的执行效率。
JVM调优参数
通用参数
-XX:-UseBiasedLocking 禁用偏向锁
-XX:-UseCompressedOops 停止对象指针压缩
-XX:ObjectAlignmentInBytes=alignment java对象对齐字节大小
-Xbatch 禁用JVM的后台编译功能
-Xcomp 强制JIT编译
-Xint 强制使用解释模式,不使用JIT
-XX:+UseLargePages 开启largepage
虚拟地址空间和物理地址的映射是通过一个叫做映射存储表的地方来工作的。这个地方一般被叫做页表(page table),页表是存储在物理内存中的。
CPU读取物理内存速度肯定会慢于读取寄存器的速度。于是又引入了TLB的概念。
Translation-Lookaside缓冲区(TLB)是一个页面转换缓存,其中保存了最近使用的虚拟到物理地址转换。
TLB容量是有限的。如果发生TLB miss则需要CPU去访问内存中的页表,从而造成性能损耗。
通过调大内存分页大小,单个TLB条目存储更大的内存范围。这将减少对TLB的压力,并且对内存密集型应用程序可能具有更好的性能。
-Xnoclassgc 禁用classes的GC
-XX:+UseStringDeduplication 开启字符串去重 G1垃圾回收器中的
线程
-XX:+UseTLAB 开启TLAB。(Thread-Local Allocation Buffer)是Java虚拟机(JVM)中的一个优化技术,主要用于提高对象分配的效率。
内存大小调整
-Xmnsize young gen的初始化和最大值
-XX:NewSize young gen的初始化大小
-XX:MaxNewSize young gen的最大值
-Xmssize heap的初始值
-Xmxsize heap的最大值
-XX:MaxMetaspaceSize=size 元数据区域的最大大小
XX:MaxDirectMemorySize=size 设置NIO的最大direct-buffer size
-XX:NewRatio=ratio young和old区域的大小比例
-XX:SurvivorRatio=ratio eden和survivor大小的比
-XX:CompressedClassSpaceSize=1g compressed class space大小
-XX:InitialCodeCacheSize=256m codeCache的初始化大小
并发控制
-XX:+AlwaysTenure 将young space中的survivor对象直接提升到old space
-XX:+NeverTenure 除非survivor space不能容纳该对
JIT调优
-XX:+SegmentedCodeCache code cache分区
-XX:-TieredCompilation 取消分层编译
-XX:+DoEscapeAnalysis 开启逃逸分析
-XX:+Inline 开启inline方法
打印GC信息
简单信息: -verbose:gc, -XX:+PrintGC
打印详细的GC信息 -XX:+PrintGCDetails, +XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC, 打印堆信息
-XX:+TraceClassLoading 看到class加载的过程和具体的class信息
-XX:+DisableExplicitGC禁止在运行期显式地调用System.gc()
-XX:-HeapDumpOnOutOfMemoryError 默认关闭,建议开启,在java.lang.OutOfMemoryError 异常出现时,输出一个dump.core文件,记录当时的堆内存快照。
栈大小设置
-Xss:如-Xss128k
JVM基础
GC种类
-
基于分代技术的:
-
Serial (Serial Old)
-
CMS -- JDK9被废弃 ParallelGC —JDK8默认 (ParNew,Parallel Scavenge,Parallel Old) G1 ---JDK9默认
-
不基于分代技术的: ZGC,loom
-
用java编写的graalVM
-
Parallel scavenge和Parallel有什么区别呢
-
Parallel Scavenge收集器
- 主要关注点:这是一个主要关注吞吐量的垃圾收集器。
- 工作区域:它主要用于新生代的垃圾回收。
- 算法:使用复制算法,这是一种适用于新生代的高效垃圾回收算法。
- 特点
- 自适应调节策略(Ergonomics):Parallel Scavenge收集器能够根据系统的当前运行情况调整Java堆大小和其他GC相关参数,以达到最佳的吞吐量性能。
- 吞吐量优先:适用于在后台运行的应用程序,这些应用程序不太关心停顿时间,更关心在单位时间内完成的工作量。
Parallel Old收集器
- 主要关注点:这是一个针对老年代的垃圾收集器,也是关注吞吐量的。
- 工作区域:主要针对老年代。
- 算法:使用标记-压缩算法,这是适用于老年代的一种有效的垃圾回收算法。
- 特点
- 与Parallel Scavenge收集器配合:在JVM 中,Parallel Old通常与Parallel Scavenge收集器一起使用,二者共同提供一个完整的针对新生代和老年代的高吞吐量GC解决方案。
- 适用于多核环境:Parallel Old收集器在多核环境中表现良好,能够并行地处理老年代中的垃圾收集任务。
综合比较
- 适用区域:Parallel Scavenge用于新生代,而Parallel Old用于老年代。
- 关注点:两者都关注于提高吞吐量,但Parallel Scavenge还特别提供自适应调节策略。
- 使用场景:它们通常一起使用,形成一个完整的、针对整个Java堆的高吞吐量垃圾收集策略。
在选择垃圾收集器时,需要根据应用程序的特定需求和运行环境来决定。如果吞吐量是主要关注点,并且应用运行在多核处理器上,那么Parallel Scavenge和Parallel Old的组合是一个不错的选择。
- 如果你的应用程序内存本来就很小,那么使用serial collector : -XX:+UseSerialGC.
- 如果你的程序运行在单核的CPU上,并且也没有程序暂停时间的限制,那么还是使用serial collector : -XX:+UseSerialGC.
- 如果对峰值期的性能要求比较高,但是对程序暂停时间没多大的要求,那么可以使用 parallel collector: -XX:+UseParallelGC。
- 如果更加关注响应时间,并且GC的对程序的暂停时间必须要小,那么可以使用-XX:+UseG1GC。
- 如果响应时间非常重要,并且你在使用大容量的heap空间,那么可以考虑使用ZGC: -XX:UseZGC。