记录一次jvm调优过程

1.查看gc情况:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
结果:服务启动时长是1天,进行了3240左右次minorGC,一共使用时间80s左右。也就是一次minirGC时间是0.02s,FGC是一次,时间是2.2s,fullGC时间过长,需要优化。
1.为什么需要优化:
FGC会不会导致请求失败?
1.1查看GC方式是什么:
在这里插入图片描述
新生代使用的是PS Young Generation回收器,也就是Parallel Scavenge回收器,特点是使用复制算法的多线程serial回收器,跟perNew区别是更注重程序的吞吐量,吞吐量是要尽量保证系统运行时间,减少垃圾回收时间。在回收时会暂停所有的用户线程。老年代使用的是PS Old Generation回收器,也就是Parallel Scavenge的年老代版本,使用的是标记整理算法。其他特点见新生代。
几个名词的解释:
吞吐量与收集器关注点说明
(A)、吞吐量(Throughput)
CPU用于运行用户代码的时间与CPU总消耗时间的比值;
即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间);
高吞吐量即减少垃圾收集时间,让用户代码获得更长的运行时间;
(B)、垃圾收集器期望的目标(关注点)
(1)、停顿时间
停顿时间越短就适合需要与用户交互的程序;
良好的响应速度能提升用户体验;
(2)、吞吐量
高吞吐量则可以高效率地利用CPU时间,尽快完成运算的任务;
主要适合在后台计算而不需要太多交互的任务;
(3)、覆盖区(Footprint)
在达到前面两个目标的情况下,尽量减少堆的内存空间;
可以获得更好的空间局部性;
通过分析GC回收器可以知道在GC回收时会暂停用户线程,可能会造成短暂的系统不可用。而且我们系统是更注重用户体验的系统,应该更加关注垃圾回收的时间而不是系统吞吐量,
优化思路:1.新生代回收时间较短不需要优化
2.老年代使用cms回收器或G1回收器
-XX:+UseConcMarkSweepGC :启用cms
-XX:ParallelGCThreads=16 :设置垃圾回收时使用的线程数。默认是8,也可以用命令查询:
在这里插入图片描述
-XX:+CMSScavengeBeforeRemark :强制remark之前开始一次minor gc,减少remark的暂停时间
-XX:+CMSParallelRemarkEnabled :并行remark,减少标记时间
-XX:CMSInitiatingOccupancyFraction :老年代占据多少时进行cms,查看了下默认配置:
java -XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal Benchmark | grep "CMSInitiatingOccupancyFraction "
在这里插入图片描述
竟然是-1,也就是老年代满了的时候再进行cms,暂时不是很确定,但通过日志看像是的:
在这里插入图片描述
后来通过查https://blog.csdn.net/foolishandstupid/article/details/77430875得知计算得到_initiating_occupancy=(100-40+80*40/100)/100=92%。也就是说,如果不配置CMSInitiatingOccupancyFraction参数,默认情况下,initiating_occupancy的值就是92%。这种情况是很危险的。有可能导致cms失败:Prommotion failed和Concurrent mode failed。
Prommotion failed:这个问题的产生是由于救助空间不够,从而向年老代转移对象,年老代没有足够的空间来容纳这些对象,导致一次full gc的产生。解决这个问题的办法有两种完全相反的倾向:增大救助空间、增大年老代或者去掉救助空间。 增大救助空间就是调整-XX:SurvivorRatio参数,这个参数是Eden区和Survivor区的大小比值,默认是32,也就是说Eden区是 Survivor区的32倍大小,要注意Survivo是有两个区的,因此Surivivor其实占整个young genertation的1/34。调小这个参数将增大survivor区,让对象尽量在survitor区呆长一点,减少进入年老代的对象。去掉救助空 间的想法是让大部分不能马上回收的数据尽快进入年老代,加快年老代的回收频率,减少年老代暴涨的可能性,这个是通过将-XX:SurvivorRatio 设置成比较大的值(比如65536)来做到。在我们的应用中,将young generation设置成256M,这个值相对来说比较大了,而救助空间设置成默认大小(1/34),从压测情况来看,没有出现prommotion failed的现象,年轻代比较大,从GC日志来看,minor gc的时间也在5-20毫秒内,还可以接受,因此暂不调整。

Concurrent mode failed的产生是由于CMS回收年老代的速度太慢,导致年老代在CMS完成前就被沾满,引起full gc,避免这个现象的产生就是调小-XX:CMSInitiatingOccupancyFraction参数的值,让CMS更早更频繁的触发,降低年老代被沾满的可能。我们的应用暂时负载比较低,在生产环境上年老代的增长非常缓慢,因此暂时设置此参数为80。在压测环境下,这个参数的表现还可以,没有出现过Concurrent mode failed。
G1回收器:
Eden区和survival区比例是否合理
-XX:+PrintTenuringDistribution让JVM在每次MinorGC后打印出Survivor空间中的对象的年龄分布

综上:jvm参数配置参考:
-server -Xms12288m
-Xmx12288m
-XX:NewRatio=2
-XX:SurvivorRatio=4
-XX:MetaspaceSize=1024m
-XX:MaxMetaspaceSize=1024m
-XX:+UseConcMarkSweepGC
-XX:+CMSScavengeBeforeRemark
-XX:+CMSParallelRemarkEnabled
-XX:CMSInitiatingOccupancyFraction=60
-XX:+UseCompressedClassPointers 压缩类指针
-XX:+UseCompressedOops 压缩对象指针
日志相关:
-XX:+PrintTenuringDistribution让JVM在每次MinorGC后打印出Survivor空间中的对象的年龄分布
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintHeapAtGC
-XX:+PrintFlagsFinal
-Xloggc:/datalog/tomcat-gc.log
运行半个月的情况:
在这里插入图片描述

### 关于 JVM 的常见面试问题及解决方案 #### 1. **什么是 JVM ?为什么需要进行 JVM ?** JVM 是指通过整 Java 虚拟机的各种参数,使应用程序能够在特定硬件环境下达到最佳性能的过程。尽管 JVM 的默认配置已经过多次测试并适用于大多数场景[^1],但在某些高并发、大数据量的情况下,默认设置可能无法满足需求。此时就需要针对具体业务场景进行。 #### 2. **常见的 JVM 性能指标有哪些?** 在 JVM 过程中,通常关注以下几个方面的性能指标: - **吞吐量**:单位时间内完成的任务数量。 - **响应时间**:处理单个请求所需的时间。 - **GC 时间占比**:垃圾回收所占用 CPU 时间的比例。 - **堆内存使用率**:程序运行期间堆内存的实际利用率。 这些指标可以通过工具如 `jstat` 或者 VisualVM 进行监控和分析[^2]。 #### 3. **如何选择合适的垃圾收集器?** 不同的应用有不同的性能需求,因此选择适合的垃圾收集器至关重要。以下是几种常用的垃圾收集器及其适用场景: - **Serial 收集器**:简单高效,适用于客户端模式下的小型应用。 - **Parallel 收集器**:注重吞吐量,适用于多核服务器环境。 - **CMS (Concurrent Mark-Sweep)**:低延迟先,适用于交互性强的应用。 - **G1 收集器**:兼顾吞吐量与延迟,在现代大型应用中广泛采用。 #### 4. **JVM 中的元空间是什么?它有什么特点?** 自 JDK 8 开始,永久代被移除,取而代之的是元空间(Metaspace)。元空间的特点如下: - 存储位置从 JVM 内部转移到了本地内存,减少了因永久代不足而导致的 OOM 错误风险。 - 动态扩展能力更强,可以根据实际加载的类文件自动增长。 - 不再受 `-XX:MaxPermSize` 参数限制,而是由操作系统本身的可用内存决定最大容量[^3]。 #### 5. **如何诊断 JVM 的性能瓶颈?** 当遇到性能问题时,可以按照以下流程逐步排查: - 使用命令行工具(如 jstack, jmap, jconsole)获取线程状态和内存分布情况; - 定期记录 Full GC 发生频率以及持续时间; - 如果发现异常,则进一步深入分析热点方法或者对象分配行为。 #### 6. **举例说明一次完整的 JVM 过程。** 假设某电商网站高峰期出现了严重的卡顿现象,经初步判断可能是由于频繁触发 Major GC 所致。于是采取以下措施解决问题: - 将年轻代比例增大至整个堆的一半以上 (`-Xmn`) ,减少 Minor GC 次数; - 启用 G1 垃圾收集算法代替原来的 Parallel Scavenge/Mark Sweep 组合(`-XX:+UseG1GC`); - 设置合理的目标暂停时间和初始堆大小(-XX:MaxGCPauseMillis 和 -Xms/Xmx). --- ### 提供一段代码示例展示如何启动带有定制化选项的 JVM 实例 ```java public class CustomJvmOptions { public static void main(String[] args) throws InterruptedException { System.out.println("Starting application with custom JVM options..."); Thread.sleep(10_000); // Simulate long-running process Runtime runtime = Runtime.getRuntime(); System.out.printf("Free Memory: %d MB%n", runtime.freeMemory() / (1024 * 1024)); System.out.printf("Total Memory: %d MB%n", runtime.totalMemory() / (1024 * 1024)); System.out.printf("Max Memory: %d MB%n", runtime.maxMemory() / (1024 * 1024)); } } ``` 上述例子展示了如何打印当前 JVM 的内存统计信息,并可通过外部传递不同参数来改变其表现形式。 ---
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值