分享

工作经验分享:Spark JVM调优示实例

nettman 2021-1-11 19:00:13 发表于 Spark [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 2101
问题导读

1.为什么要进行调优?
2.JVM都有哪些参数?
3.JVM调优有没有标准?


背景:

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

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

首先设置VM参数如下 :
  1. -Xmx256m -Xms256m -Xmn128m  -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC  
  2. -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详细信息

参数设置完成后,启动应用就报错 :
  1. Exception in thread "main" java.lang.IllegalArgumentException: System memory 255328256 must be at least 4.718592E8. Please use a larger heap size.
  2.     at org.apache.spark.memory.UnifiedMemoryManager$.getMaxMemory(UnifiedMemoryManager.scala:193)
  3.     at org.apache.spark.memory.UnifiedMemoryManager$.apply(UnifiedMemoryManager.scala:175)
  4.     at org.apache.spark.SparkEnv$.create(SparkEnv.scala:354)
  5.     at org.apache.spark.SparkEnv$.createDriverEnv(SparkEnv.scala:193)
  6.     at org.apache.spark.SparkContext.createSparkEnv(SparkContext.scala:288)
  7.     at org.apache.spark.SparkContext.<init>(SparkContext.scala:457)
  8.     at org.apache.spark.streaming.StreamingContext$.createNewSparkContext(StreamingContext.scala:874)
  9.     at org.apache.spark.streaming.StreamingContext.<init>(StreamingContext.scala:81)
  10.     at WebConsumer$.main(WebConsumer.scala:46)
  11.     at WebConsumer.main(WebConsumer.scala)
复制代码
查看Spark源码,发现初始化SparkContext默认需要的最小系统内存为450M;

修改参数为 :

  1. -Xmx512m -Xms512m -Xmn256m -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC  
  2. -XX:ParallelGCThreads=4  -XX:MaxMetaspaceSize=512m -XX:MaxTenuringThreshold=7  -XX:+PrintGCDetails
复制代码


正常启动应用,观察GC日志输出 :
  1. [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]
  2. [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]
  3. [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]
  4. [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。现在的参数如下 :
  1. -Xmx512m -Xms512m -Xmn256m -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC  
  2. -XX:ParallelGCThreads=4  -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=512m -XX:MaxTenuringThreshold=7  -XX:+PrintGCDetails
复制代码
运行后发现程序没有发生Full GC,继续观察,发现Old区内存占用非常小,而Young区变化非常大,尤其在繁忙期间,伴随着多次minor gc发生。此时考虑增大堆内存年轻代大小。

再次修改参数为 :
  1. -Xmx512m -Xms512m -Xmn400m -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC  
  2. -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



可加微信w3aboutyun



获取更多资源:
领取100本书+1T资源
http://www.aboutyun.com/forum.php?mod=viewthread&tid=26480

大数据5个项目视频
http://www.aboutyun.com/forum.php?mod=viewthread&tid=25235

名企资源、名企面试题、最新BAT面试题、专题面试题等资源汇总
https://www.aboutyun.com/forum.php?mod=viewthread&tid=27732


名企资源、名企面试题、最新BAT面试题、专题面试题等资源汇总
https://www.aboutyun.com/forum.php?mod=viewthread&tid=27732



加微信w3aboutyun,可拉入技术爱好者群

没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条