博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM调优示实例<一>
阅读量:6933 次
发布时间:2019-06-27

本文共 4259 字,大约阅读时间需要 14 分钟。

hot3.png

背景:

开发一个Spark Streaming job消费消息队列中的用户行为日志,job会源源不断处理消息队列中的消息。由于集群会运行很多job,现需要调整jvm参数优化job的运行。

下面是调优过程(基于java8):

首先设置VM参数如下 :

-Xmx256m -Xms256m -Xmn128m  -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC  -XX:ParallelGCThreads=4  -XX:MaxMetaspaceSize=512m -XX:MaxTenuringThreshold=7  -XX:+PrintGCDetails

参数注释 :

-Xmx256m:设置JVM最大堆内存为256m

-Xms256m:设置JVM初始堆内存为256m。将此值设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn128m: 设置堆内存年轻代大小为128m
-XX:SurvivorRatio=8 :设置堆内存年轻代中Eden区与Survivor区大小的比值 。设置为8,则两个Survivor区(JVM堆内存年轻代中默认有2个Survivor区)与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10。

-XX:+UseParallelGC :选择垃圾收集器为并行收集器。此配置仅对年轻代有效。

-XX:+UseParallelOldGC :配置年老代垃圾收集方式为并行收集。

-XX:ParallelGCThreads=4 :配置并行收集器的线程数,此值最好配置与处理器数目相等。

-XX:MaxMetaspaceSize=512m:设置元数据空间最大为512m,默认基本是无穷大,建议设置该参数,防止内存泄漏导致内存被无止境扩大,MaxMetaspaceSize并不会在jvm启动的时候分配一块这么大的内存出来,而java8之前MaxPermSize是会分配一块这么大的内存的。

-XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区移动7次还没有被回收就放入年老代。

1如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于年老代比较多的应用,这样做可以提高效率。

2如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代即被回收的概率。

-XX:+PrintGCDetails : 输出GC详细信息

参数设置完成后,启动应用就报错 :

Exception in thread "main" java.lang.IllegalArgumentException: System memory 255328256 must be at least 4.718592E8. Please use a larger heap size.    at org.apache.spark.memory.UnifiedMemoryManager$.getMaxMemory(UnifiedMemoryManager.scala:193)    at org.apache.spark.memory.UnifiedMemoryManager$.apply(UnifiedMemoryManager.scala:175)    at org.apache.spark.SparkEnv$.create(SparkEnv.scala:354)    at org.apache.spark.SparkEnv$.createDriverEnv(SparkEnv.scala:193)    at org.apache.spark.SparkContext.createSparkEnv(SparkContext.scala:288)    at org.apache.spark.SparkContext.
(SparkContext.scala:457) at org.apache.spark.streaming.StreamingContext$.createNewSparkContext(StreamingContext.scala:874) at org.apache.spark.streaming.StreamingContext.
(StreamingContext.scala:81) at WebConsumer$.main(WebConsumer.scala:46) at WebConsumer.main(WebConsumer.scala)

查看Spark源码,发现初始化SparkContext默认需要的最小系统内存为450M;

修改参数为 :

-Xmx512m -Xms512m -Xmn256m -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC  -XX:ParallelGCThreads=4  -XX:MaxMetaspaceSize=512m -XX:MaxTenuringThreshold=7  -XX:+PrintGCDetails

正常启动应用,观察GC日志输出 :

[GC (Metadata GC Threshold) [PSYoungGen: 202202K->16781K(276480K)] 202202K->16870K(493568K), 0.0191049 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] [Full GC (Metadata GC Threshold) [PSYoungGen: 16781K->0K(276480K)] [ParOldGen: 88K->16149K(217088K)] 16870K->16149K(493568K), [Metaspace: 20836K->20836K(1067008K)], 0.0395959 secs] [Times: user=0.08 sys=0.00, real=0.04 secs] [GC (Metadata GC Threshold) [PSYoungGen: 133458K->15592K(276480K)] 149608K->31749K(493568K), 0.0240460 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] [Full GC (Metadata GC Threshold) [PSYoungGen: 15592K->0K(276480K)] [ParOldGen: 16157K->19444K(217088K)] 31749K->19444K(493568K), [Metaspace: 35073K->35073K(1079296K)], 0.0641864 secs] [Times: user=0.19 sys=0.00, real=0.06 secs]

分析:应用刚启动完成则进行了两次Full GC,观察GC日志,此时Old使用率非常低,代码中也没用触发full GC的动作,日志中导致GC的原因是Metadata GC Threshold,因此可以确定由Metadata导致。那么需要优化元数据空间大小。根据GC日志可知,元数据空间达到20.8M则进行了一次,达到35M时又进行了一次,分析可能由于MetaspaceSize大小导致。

java8中MetaspaceSize默认20.8M左右,主要是控制metaspaceGC发生的初始阈值,也是最小阈值,但是触发metaspaceGC的阈值是不断变化的。因此这里最好设置一个合适的初始MetaspaceSize。

根据运行分析我的应用,发现metaspace达到60M左右后增长将非常缓慢,因此修改JVM参数,使用MetaspaceSize设置元数据空间初始容量为64M。现在的参数如下 :

-Xmx512m -Xms512m -Xmn256m -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC  -XX:ParallelGCThreads=4  -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=512m -XX:MaxTenuringThreshold=7  -XX:+PrintGCDetails

运行后发现程序没有发生Full GC,继续观察,发现Old区内存占用非常小,而Young区变化非常大,尤其在繁忙期间,伴随着多次minor gc发生。此时考虑增大堆内存年轻代大小。

再次修改参数为 :

-Xmx512m -Xms512m -Xmn400m -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC  -XX:ParallelGCThreads=4  -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=512m -XX:MaxTenuringThreshold=7  -XX:+PrintGCDetails

运行程序,发现程序运行平稳,发生 minor gc次数也大量减少了。

总结:整个调优过程是不断修改jvm参数,观察运行情况,一步步测试出来的。从来就没有最优的配置,配置需根据应用的运行情况,环境资源限制,性能目标等多种因素决定。正如该示例,首先设置一些预估参数,然后观察GC日志,并结合工具(本示例主要使用Java自带工具jvisualvm)分析;然后调整参数,再次运行并分析,最终得出满足应用需求的配置参数。由于该应用中Old区中的对象相对固定且较小,大多数对象都是朝生夕灭,因此old区设置的堆空间相对较小 。

欢迎指出本文有误的地方,转载请注明原文出处

转载于:https://my.oschina.net/7001/blog/871011

你可能感兴趣的文章
宫崎骏首次因为自己的新作流泪
查看>>
SAP sybase16 安装的一些细节问题
查看>>
linux服务器同步时间
查看>>
Android开发常见问题及解决方法
查看>>
Linux 基础 - 磁盘管理 - 01
查看>>
我的友情链接
查看>>
繁忙的IT基础设施可能导致安全灾难
查看>>
Objective-C之Block
查看>>
iOS 图片加载框架-SDWebImage 解读
查看>>
安景业安京业安敬业anjingye
查看>>
java rest的说明
查看>>
Angular在页面加载很慢的时候,会出现双花括号的问题
查看>>
JS判断客户端是否是iOS或者Android手机移动端
查看>>
我的友情链接
查看>>
清除浏览器自动填充用户名、密码框
查看>>
HTML5 meta viewport参数详解
查看>>
C#中==、Equals、ReferenceEquals的区别是什么
查看>>
linux 服务器I/O读写测试
查看>>
ubuntu下的wireshark编译安装
查看>>
多学一点(一)——在 Linux 下安装 JDK 和 Tomcat
查看>>