Apache Spark由于其出色的性能、简单的接口和丰富的分析和计算库而获得了广泛的行业应用。与大数据生态系统中的许多项目一样,Spark在Java虚拟机(JVM)上运行。因为Spark可以在内存中存储大量数据,因此它主要依赖于Java的内存管理和垃圾收集(GC)。但是现在,了解Java的GC选项和参数的用户可以调优他们的Spark应用程序的最佳性能。本文描述了如何为Spark配置JVM的垃圾收集器,并给出了实际的用例来解释如何调优GC,以提高Spark的性能。我们在调优GC时考虑关键因素,如收集吞吐量和延迟。
 
spark和GC的介绍:
随着Spark的广泛应用,Spark应用程序的稳定性和性能调优问题越来越成为人们关注的话题。由于Spark以内存为中心的方法,通常使用100GB或更多内存作为堆空间,这在传统的Java应用程序中很少见到。在与使用Spark的大公司合作时,我们收到了许多关于GC在执行Spark应用程序时所面临的各种挑战的担忧。例如,垃圾收集需要很长的时间,导致程序经历长时间的延迟,甚至在严重的情况下崩溃。在本文中,我们使用真实的例子,结合具体的问题,讨论可以缓解这些问题的Spark应用程序的GC调优方法。
Java应用程序通常使用这两种GC策略 : 并发标记清理(Concurrent Mark Sweep garbage collection简称CMS)垃圾收集和并行垃圾收集(ParallelOld garbage collection)。前者旨在降低延迟,而后者则是针对更高的吞吐量。这两种策略都有性能瓶颈:CMS GC不执行压缩,而并行GC只执行全堆压缩,这会导致相当大的停顿时间。我们建议客户按照应用的实际需求选择不同的策略。对于需要实时响应的应用,推荐CMS GC;对于离线分析的应用,推荐使用并行GC。
那么,对于像Spark这样支持流计算和传统批处理的计算框架,我们能找到一个最佳的GC收集器吗?Hotspot JVM1.6 版本引入了垃圾收集的第三个策略:The Garbage-First GC (简称G1 GC)。G1收集器由Oracle计划,作为CMS GC的长期替代品。最重要的是,G1收集器的目标是实现高吞吐量和低延迟的双特点。在详细介绍使用Spark的G1收集器之前,让我们先讨论一下Java GC基础的一些背景知识。
 
JVM GC工作流程:
 

Spark的内存管理介绍
弹性分布数据集(RDD)是Spark的核心抽象。RDD的创建和缓存与内存消耗密切相关。Spark允许用户将数据进行缓存以便在应用程序中重用,从而避免重复计算造成的开销。持久化的数据自然是存放在JVM中的。Spark的executor将JVM堆空间划分为两个部分:一部分用于存放持久化的数据(storeage),另一部分用作RDD转换期间的内存消耗(shuffle)。我们可以使用spark.storage来调整这两个部分的比例(1.6版本之前)
 
当发现由GC延迟引起的效率下降时,我们应该首先检查并确保Spark应用程序是否以有效的方式使用有限的内存空间。内存空间RDD占用的空间越小,程序执行的堆空间就越多,从而可以提高GC效率;相反,RDDs的过度内存消耗导致了老年代大量的缓冲对象导致的性能损失。在这里,我们用一个例子来介绍这一点:
 
例如,有一个Spark应用程序,它执行简单的迭代计算。反复迭代计算,每步迭代的结果都会被持久化到内存空间中。在程序执行过程中,我们观察到当迭代次数增加时,进程所使用的内存空间会快速增长,从而导致GC变得更糟。当我们仔细研究时,我们发现它会在内存中缓存每一个RDD,而且不会随着时间的推移而释放它们,哪怕它们在下一次迭代之后不会被使用。那么这将导致内存消耗增长,从而引发更多GC尝试。我们在SPARK-2661中删除了这种不必要的缓存。在此修改缓存后,RDD大小在三个迭代和缓存空间被有效控制后稳定(如表1所示)。结果,GC效率大大提高,程序的总运行时间缩短了10%~20%。
 

结论:
当GC时间很长时,它可能表明内存空间没有被Spark应用程序有效地使用。您可以通过显式地清理缓存后的RDD,从而提高性能。
 
怎么选择合适的GC策略
如果我们的应用程序已经可以高效地使用内存,那么下一步就是选择使用哪种GC策略以及如何进行GC调优了。在这里我们启用了一个四节点集群,给每个executor分配了88GB的堆内存,并以standalone模式启动Spark来进行我们的实验。最初我们使用默认的Spark Parallel GC,发现因为spark应用程序的内存开销比较大,大多数的对象不能在一个相当短的生命周期被回收,Parallel GC经常出现Full GC。更糟糕的是,Parallel GC提供了非常有限的性能调优选项,因此我们只能使用一些基本参数来调整性能,比如每一代的大小比例,以及在对象被提升到老年代之前的副本数量。但是这些调优策略也只是推迟了Full GC,所以并行GC调优对长时间运行的应用程序没有帮助。因此,在本文中,我们不进行 Parallel GC调优。表2显示了Parallel GC的操作,很明显,当发生FULL GC时,CPU利用率最低。
 
Table 2: Parallel GC Running Status (Before Tuning)
 
CMS GC对于Spark应用中FULL GC的调优选择同样少之又少。此外,CMS GC FULL GC暂停时间比Parallel GC还要长,大大降低了应用程序的吞吐量。
 
接下来,我们使用G1 GC策略(默认配置)运行我们的应用程序。令我们惊讶的是,G1 GC也提供了让人难以接受的FULL GC(请参阅图3中的“CPU利用率”,显然作业3暂停了将近100秒),一个长时间的暂停显著地拖了整个应用程序的运行。如表4所示,虽然总运行时间略长于Parallel GC,但G1 GC的性能略优于CMS GC。
 
Table 3: G1 GC Running Status (Before Tuning)

三种GC策略下,spark应用消耗的时间(均使用默认参数,未做任何优化)
Table 4 Comparison of Three Garbage Collectors’ Program Running Time (88GB Heap before tuning)

基于输出的日志对G1 GC进行优化
 
首先,我们希望JVM能够在日志中记录更多的GC细节。对于Spark,我们可以通过spark.executor.extraJavaOptions设置一些关于JVM的参数。比如:
-XX:+PrintFlagsFinal -XX:+PrintReferenceGC -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintAdaptiveSizePolicy -XX:+UnlockDiagnosticVMOptions -XX:+G1SummarizeConcMark
定义了这些参数以后,我们可以详细的跟踪GC的日志记录(output to $SPARK_HOME/work/$ app_id/$executor_id/stdout at each worker node)接下来,我们可以根据GC日志分析问题的根源,并学习如何提高程序性能。
 
G1 GC日志的结构(具体见原文)
 
从日志中我们可以看到G1 GC日志有一个非常清晰的层次结构。日志列出了暂停发生的时间和原因,以及不同线程的时间消耗、及CPU时间的最大值和平均值。最后,G1 GC在暂停之后列出清理结果,以及总时间消耗。
在当前G1 GC运行日志中,我们发现了这样一个特殊的块:(具体见原文)
 

我们可以看到,最大的性能下降是由FULL GC引起的,并且在日志输出的结果中显示内存耗尽,内存溢出等等(对于不同的JVM版本日志显示略有不同)。而这个原因是G1 GC收集器试图给某些区域进行垃圾回收时,它没有找到可以复制活动对象的自由区域(在GC时,不被引用的对象会被回收掉,还处于引用的对象会被转存到另外一个区域),这种情况称为“疏散失败”,并且将导致FULL GC发生。显然,G1 GC中的FULL GC比Parallel GC更糟糕,因此我们必须尽量避免GC,以获得更好的性能。为了避免G1 GC的FULL GC,有两种常用的方法:
 
一:减小initiatingheapancypercent选项的值(默认值是45),让G1 GC可以较早的启动初始并发标记,这样可以很大概率避免FULL GC的发生。
二:增加ConcGCThreads选项的值,为并发标记提供更多的线程,这样我们就可以加速并发标记阶段。需要注意的是,这个选项也会占用一些工作线程资源,这取决于你实际有多少CPU以及CPU的利用率。
 
对这两个选项进行调优,可以尽可能的避免FULL GC的发生。FULL GC被限制后,性能得到显著提高。但是,我们仍然在GC期间发现了长时间的暂停。在进一步的调查中,我们发现在我们的日志中有以下事件:
280.008: [G1Ergonomics (Concurrent Cycles) request concurrent cycle initiation, reason: occupancy higher than threshold, occupancy: 62344134656 bytes, allocation request: 46137368 bytes, threshold: 42520176225 bytes (45.00 %), source: concurrent humongous allocation]
 
在这里我们可以看到一些极大的对象(这些对象的大小是标准区域的50%甚至更大)。G1 GC会将这些对象放到相邻的区域集合中。由于复制这些对象会消耗大量的资源,所以这些对象会被直接分配到老年代,然后将其分类为巨大的区域。在jdk 1.8.0_u40之前,需要一个完整的堆分析来回收这些巨大的区域[JDK-8027959]。如果有许多这样的对象,堆将很快被填满,并且回收它们的代价太大。即使后来官方有了修复,但连续区域的分配仍然很昂贵(特别是在遇到严重堆碎片时),因此我们希望避免创建这种大小的对象。我们可以增加 g1heapregions 大小的值,以减少创建巨大区域的可能性,但是如果我们使用相对较大的堆,32M已经是它的最大值了。这意味着我们只能分析程序来找到这些对象并减少它们的创建。否则,它可能会导致更多的并发标记阶段,在此之后,您需要仔细地调整混合GC相关的knobs(e.g., -XX:G1HeapWastePercent -XX:G1MixedGCLiveThresholdPercent),以避免长期混合GC暂停(由大量巨大的对象引起)。
 
接下来,我们可以分析从单个GC不断循环的开始一直到混合GC的结束这段时间的间隔。如果时间太长,可以考虑增加ConcGCThreads的值,但是注意这将占用更多的CPU资源。
 
G1 GC也有减少STW暂停长度的方法,以在垃圾收集的并发阶段做更多的工作。如上所述,G1 GC为每个区域维护一个Remembered Set(简称RSet),以跟踪外部区域对给定区域的对象引用,G1收集器将在STW阶段和并发阶段更新RSet。如果您想要减少G1 GC暂停的时间,您可以降低G1RSetUpdatingPauseTimePercent的值,同时增加G1ConcRefinementThreads的值。前者用于指定总STW时间内rset更新时间的期望比率,默认值为10%,而后者用于定义在程序运行期间维护rset的线程数。有了这两个选项,我们可以将更多的rset的工作负载从STW阶段转移到并发阶段。
此外,对于长时间运行的应用程序,我们使用AlwaysPreTouch 选项,因此JVM在启动时应用了OS所需要的所有内存,并避免了动态应用程序。这提高了运行时性能,以延长启动时间。
最终,经过几轮GC参数调整后,我们到达了表5中的结果。与之前的结果相比,我们最终获得了更令人满意的运行效率。
 
Table 5 G1 GC Running Status (after Tuning)
 
Configuration Options:
-Xms88g -Xmx88g
-XX:+UseG1GC
-XX:+UnlockDiagnosticVMOptions
-XX:+G1SummarizeConcMark
-XX:InitiatingHeapOccupancyPercent=35
-XX:ConcGCThread=20
 
 
通过GC日志分析可以获得细粒度的优化。调优后,我们成功地将应用程序的运行时间缩短到4.3分钟。与调优前的运行时间相比,性能提高了1.7倍;与Parallel GC相比,性能提升了差不多1.5倍。
总结
对于严重依赖内存计算的Spark应用程序来说,GC调优尤为重要。当GC出现问题时,不要急于调试GC本身。首先考虑Spark程序的内存管理是否低效,比如持久化和释放缓存中的RDD。在进行GC调优时,我们首先建议使用G1 GC来运行Spark应用程序。G1 GC可以很好地处理不断增长的堆大小。对于G1,需要更少的选项来提供更高的吞吐量和更低的延迟。当然,GC调优没有固定模式。各种应用程序具有不同的特性,为了解决不可预测的情况,必须根据日志和其他取证来掌握GC调优的技术。最后,我们不要忘记通过程序的逻辑和代码进行优化,比如减少中间对象的创建或复制,控制大型对象的创建,将长时间的对象存储在堆外,等等。
 
通过使用G1 GC,我们在Spark应用程序中取得了重大的性能改进。Spark未来的工作将把内存管理责任从Java的垃圾收集器转移到Spark本身。这将大大减少Spark应用程序的调优需求。但是现在来说,GC策略的选择和调优依旧是提高Sparkd应用性能的关键。

原文:

https://databricks.com/blog/2015/05/28/tuning-java-garbage-collection-for-spark-applications.html?from=timeline

intel关于spark gc的优化建议的更多相关文章

  1. SparkSQL的一些用法建议和Spark的性能优化

    1.写在前面 Spark是专为大规模数据处理而设计的快速通用的计算引擎,在计算能力上优于MapReduce,被誉为第二代大数据计算框架引擎.Spark采用的是内存计算方式.Spark的四大核心是Spa ...

  2. .NET程序的性能要领和优化建议

    前几天在老赵的博客上看到,Bill Chiles (Roslyn 编译器的Program Manager)写了一篇文章叫做<Essential Performance Facts and .NE ...

  3. Unity开发-你必须知道的优化建议

    转自:http://blog.csdn.net/leonwei/article/details/18042603 最近研究U3D开发,个人认为,精通一种新的技术,最快最好的方法就是看它的documen ...

  4. unity优化建议

    使用Profiler工具分析内存占用情况 System.ExecutableAndDlls:系统可执行程序和DLL,是只读的内存,用来执行所有的脚本和DLL引用.不同平台和不同硬件得到的值会不一样,可 ...

  5. OCM_第十四天课程:Section6 —》数据库性能调优_各类索引 /调优工具使用/SQL 优化建议

    注:本文为原著(其内容来自 腾科教育培训课堂).阅读本文注意事项如下: 1:所有文章的转载请标注本文出处. 2:本文非本人不得用于商业用途.违者将承当相应法律责任. 3:该系列文章目录列表: 一:&l ...

  6. [转] - Spark排错与优化

    Spark排错与优化 http://blog.csdn.net/lsshlsw/article/details/49155087 一. 运维 1. Master挂掉,standby重启也失效 Mast ...

  7. Unity 几种优化建议

    转: http://user.qzone.qq.com/289422269/blog/1453815561?ptlang=2052 Unity 几种优化建议 最简单的优化建议: 1.PC平台的话保持场 ...

  8. mysql性能优化学习笔记-参数介绍及优化建议

    MySQL服务器参数介绍 mysql参数介绍(客户端中执行),尽量只修改session级别的参数. 全局参数(新连接的session才会生效,原有已经连接的session不生效) set global ...

  9. Jquery学习笔记--性能优化建议

    一.选择器性能优化建议 1. 总是从#id选择器来继承 这是jQuery选择器的一条黄金法则.jQuery选择一个元素最快的方法就是用ID来选择了. 1 $('#content').hide(); 或 ...

随机推荐

  1. 用apt-get解决dpkg过程中出现的依赖问题

    dpkg命令不解决依赖问题,这点对新手很不友好 当使用dpkg -i *.deb 安装出现依赖问题的时候,可以尝试如下解决方法: apt-get -f -y install # 复制粘贴回车,inst ...

  2. Educational Codeforces Round 81 + Gym 102267

    UPD:变色了!!!历史最高1618~ Educational Codeforces Round 81 (Rated for Div. 2) The 2019 University of Jordan ...

  3. 「JSOI2011」任务调度

    「JSOI2011」任务调度 传送门 一开始还在想写平衡树,看到 \(\text{TRANS}\) 操作后就晓得要用可并堆了. 这题好像就是个可并堆的板子题??? ADD 直接往对应的对里面加元素 D ...

  4. maven热部署

    1.启动tomcat 2.修改 tomat/conf/tomcat-users.xml   配置用户名.密码.角色       manager-gui:图形界面的权限(调试时配置)       man ...

  5. 在javaweb中对于session的使用

    1.初次调用session时: String username="student"; HttpSession session=request.getSession(true);// ...

  6. spark实验(一)--spark安装(1)

    一.实验目的 (1)掌握 Linux 虚拟机的安装方法.Spark 和 Hadoop 等大数据软件在 Linux 操作系统 上运行可以发挥最佳性能,因此,本教程中,Spark 都是在 Linux 系统 ...

  7. C语言程序编译

    原来GCC的含义是GNU C Compiler,当初知识编译C语言,而现在GCC不知编译C语言,除此之外它还支持编译Ada.C++.Java.Object C.Pascal.COBOL.等等许多语言, ...

  8. css中class后面跟两个类,这两个类用空格隔开

    css中class后面跟两个类,这两个类用空格隔开,那么这两个类对这个元素都起作用,如果产生冲突,那么后面的类将替代前面的类.

  9. uniapp - 手机调试 ( vivo )

    打开开发者选项,打开USB设置,把默认USB选项改成MIDI模式,就可以检测到手机

  10. 设计模式课程 设计模式精讲 8-11 单例模式源码解析(jdk+spring+mybaties)

    1 源码解析 1.1 单例解析1 1.2 单例解析2(容器单例) 1.3 单例解析3 1.4 单例解析4 1 源码解析 1.1 单例解析1 java.lang.Runtime /** * 饿汉式加载, ...