这是以前的文章(第3部分,共1部分)的继续:有关性能调优,Java中的JVM,GC,Mechanical Sympathy等的文章和视频的提要 。
事不宜迟,让我们开始使用我们的下一组博客和视频,印章……印章……! 这次是Martin Thompson的博客文章和讨论。 马丁关于Java垃圾收集的第一篇文章基本上是精简了GC流程及其底层组件,其中包括对许多有趣的GC标志( -XX:… )投光。 在他的下一次演讲中,他讲述了机械同情,人们正确相信的东西以及误解的神话。 在性能测试的讨论中,Martin进一步完善了Java,OS和硬件的融合,以展示对所有这些方面的理解如何有助于编写更好的程序。
Martin Thompson
标志太多,无法调整GC以实现应用程序所需的吞吐量和延迟。 关于铃铛和口哨声的细节有很多文档,但是没有文档可以指导您完成这些操作。
权衡
吞吐量( -XX:GCTimeRatio = 99 ),延迟( -XX:MaxGCPauseMillis =
- 为GC算法提供更多内存
- 可以通过包含活动集并减小堆大小来减少GC
- 通过管理堆和生成大小以及控制应用程序的对象分配率,可以减少暂停的频率
- 可以通过同时运行GC来减少大暂停的频率
对象寿命
GC算法通常经过优化,期望大多数对象的生存期很短,而很少有对象生存期很长。 实验表明,与非代垃圾收集器相比,代垃圾收集器支持的吞吐量要好得多,因此可以在服务器JVM中使用。
世界停止活动
为了使GC发生,有必要使正在运行的应用程序的所有线程都必须暂停–垃圾收集器通过发信号通知线程在到达安全点时停止来实现此目的。 到达安全点的时间是低延迟应用程序中的重要考虑因素,除了其他GC标志外,还可以使用‑XX:+ PrintGCApplicationStoppedTime标志来找到安全点时间。
当发生STW事件时,随着线程从安全点释放时恢复,系统将承受很大的调度压力,因此更少的 STW会使应用程序更高效。
热点堆组织
Java堆被划分为多个区域,在伊甸园中创建了一个对象,并将其移入幸存者空间,并最终移交给了有期限的人。 PermGen用于存储运行时对象,例如类和静态字符串。 收集器利用虚拟空间来满足吞吐量和延迟目标,并调整区域大小以达到目标。
对象分配
TLAB(线程本地分配缓冲区)用于在Java中分配对象,这比使用malloc便宜(在大多数平台上需要10条指令)。 次要收集率与对象分配率成正比。 大型对象( -XX:PretenureSizeThreshold = n )可能必须在Old Gen中分配,但是如果将阈值设置为低于TLAB大小,则将不会在Old gen中创建它们-(请注意)不适用于G1 收集器 。
小型收藏
当Eden变满时,将进行次要收集,一旦对象变旧,即超过阈值( -XX:MaxTenuringThreshold ),对象就会从eden提升到占位空间 。 在次要收集中,将具有已知GC根的活动的可到达对象复制到幸存者空间。 热点使用卡片表维护跨代引用。 因此,老一代的规模也是次要收藏成本的一个因素。 通过将Eden的大小调整为要提升的对象数,可以提高收集效率。 这些很容易发生STW,因此在最近时期成问题。
主要收藏
主要藏品收集了老一代,以便可以推广年轻一代的物品。 收集器跟踪旧一代的填充阈值,并在超过阈值时开始收集。 为避免升级失败,您将需要调整旧版本允许容纳升级的填充( -XX:PromotedPadding =
串行收集器
它是具有最小占用空间( -XX:+ UseSerialGC )的最简单的收集器,并且对次要和主要收集都使用单个线程。
并联收集器
有两种形式( -XX:+ UseParallelGC )和( -XX:+ UseParallelOld GC ),对于次要集合使用多个线程,对于主要集合使用单个线程-因为Java 7u4对这两种类型的集合都使用多个线程。 Parallel Old在多处理器系统上的性能非常好,适用于批处理应用程序。 提供更多的内存,更大但更少的收集暂停可以帮助此收集器。 根据您的应用程序可以承受的暂停时间,在Parallel Old和Concurrent收集器之间进行权衡(在压缩旧一代后,现代硬件上每GB实时数据的暂停时间应为1到5秒)。
并发标记扫描(CMS)收集器
CMS( -XX:+ UseConcMarkSweepGC )收集器在旧版本中运行,以收集在大型收集期间不再可访问的使用期限对象。 CMS不是压缩收集器,随着时间的推移会导致Old gen的碎片化。 当较大的对象无法放入Old gen时,升级失败将触发FullGC。 CMS与您的应用程序一起运行会占用CPU时间。 CMS如果无法以足够的速度跟上升级的速度,就会遭受“并发模式故障”。
垃圾优先(G1)收集器
G1( -XX:+ UseG1GC )是Java 6中引入的新收集器,现已在Java 7中得到正式支持。它是具有部分并发收集算法的分代收集器,它以较小的增量STW暂停压缩了Old gen。 它将堆划分为可变大小的固定大小区域。 G1是由延迟驱动的目标驱动器( –XX:MaxGCPauseMillis =
替代并行收集器
Oracle JRockit Real Time,IBM Websphere Real Time和Azul Zing是替代的并发收集器。 根据作者的说法,Zing是唯一在收集,压缩之间保持平衡的Java收集器,并为所有世代保持高吞吐量。 不论堆大小如何,在所有阶段(包括次要回收期间),Zing都是并行的。 对于所有针对延迟的并发收集器,您必须放弃吞吐量并增加占用空间。 堆大小的预算至少为有效设置的活动集的2到3倍。
垃圾收集监控和调整
始终启用以收集最佳GC详细信息的重要标志:
-verbose:gc -Xloggc:-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime
使用Chewiebug , JVisualVM之类的应用程序(带有Visual GC插件)来研究由于GC动作导致的应用程序行为。 运行可以重复执行的代表性负载测试(当您了解各种收集器时),继续尝试不同的配置,直到达到吞吐量和延迟目标。 jHiccup帮助跟踪JVM中的暂停。 我们都知道,要在等待时间要求,高对象分配和提升率之间取得平衡,这是一个艰巨的挑战,有时选择商业解决方案来实现这一目标可能是一个更明智的想法。
结论: GC本身就是一个很大的主题,它包含许多组件,其中一些组件不断被替换,因此重要的是要知道每个组件代表什么。 GC标志与其相关的组件同样重要,并且了解它们以及如何使用它们也很重要。 启用某些标准GC标志来记录GC日志不会对JVM的性能产生任何重大影响。 只要您遵循作者的方法,就可以使用第三方免费软件或商业工具。
—多次阅读该文章,因为Martin涵盖了许多有关GC和收集器的细节,需要仔细检查并充分理解。 -
马丁·汤普森(Martin Thompson):神话般的现代硬件获得“机械同情”
他将神话分为三类- 可能的 , 合理的和失败的 ! 为了充分利用您拥有的硬件,您需要了解硬件。 进行权衡,改变旋钮,这并不像您想的那样可怕。
好问题:普通开发人员是否了解他们编程的硬件? 还是不知道发生了什么? 还是我们有纪律并努力了解我们使用的平台?
神话1
CPU并没有变得越来越快–时钟速度还不是全部,Sandy Bridge体系结构才是更快的品种。 6个端口支持并行(6个操作/周期)。 Haswell有8个端口! 代码除法运算的执行速度比任何其他算术运算都要慢。 CPU具有前端和后端周期。 随着我们更快地喂食它们,它变得越来越快– 可能
神话2
内存为我们提供了随机访问权限 -CPU寄存器和缓冲区,内部缓存(L1,L2,L3)和内存-分别以对这些区域的访问速度增加的顺序提到。 CPU一直在通过执行直接访问操作来降低其运行温度。 写比读少麻烦-缓冲区丢失代价高昂。 L1被组织为包含处理器将执行的代码的高速缓存行–通过没有高速缓存行未命中来提高效率。 预取器有助于减少延迟,并有助于读取流和可预测的数据。 TLB丢失也可以提高成本效益(4K =内存页面大小)。 简而言之,由于底层硬件的工作方式,读取内存并非接近随机读取,而是逐次读取。 编写高度分支的代码可能会导致程序执行速度变慢–将具有凝聚力的内容放在一起是提高效率的关键。 – 破产
注意: TLAB和TLB是两个不同的概念!
神话3
HDD提供了随机访问权限–旋转的磁盘和手臂在读取数据时移动。 与内部轨道(区域位记录)相比,外部轨道中放置的扇区更多。 更快地旋转光盘并不是提高HDD性能的方法。 最小读写量为4K。 最佳光盘中的搜索时间为3-6毫秒,笔记本电脑驱动器的速度较慢(15毫秒)。 旋转延迟需要一些时间。 数据传输需要100-220 MB /秒。 添加缓存可以改善将数据写入磁盘的功能,而不能改善从磁盘读取数据的速度。 – 破产
神话4
SSD提供随机访问–读写很棒,工作非常快(一次4K)。 删除与真正的删除不同,它被标记为已删除而不是真正被删除-因为您无法以高分辨率擦除,因此需要一次擦除整个块(因此标记为已删除)。 所有这些都会导致碎片,因此需要进行GC和压缩。 读取是平滑的,写入会受到碎片,GC,压缩等的阻碍,也要提防写入放大。 使用SSD时有一些缺点,但总体上还是不错的。 – 可能
我们能理解所有这些并编写更好的代码吗?
结论:不要仅仅因为一切都在表面上就把所有东西都视为理所当然,而是在考虑可能的内部条件之前,对自己的内部结构进行检查,检查和调查,以便编写良好的代码并充分利用这些优势特征。
—很好地谈论了话题,并以很好的幽默感很好地涵盖了机械同情主题,请观看视频以获取针对上述每个硬件组件收集的性能统计信息—
Martin Thompson的
我们使用诸如“如何使用探查器?”之类的东西。 或“如何使用调试器?”
什么是性能? 可能意味着两件事,例如吞吐量或带宽(可以通过多少)和延迟(系统响应的速度)。
响应时间随我们在系统上施加更多负载而变化。 在设计任何系统之前,我们需要性能要求,即系统的吞吐量是多少或您希望系统响应的速度(延迟)如何? 您的系统是否可以随着业务进行经济扩展?
开发者时间很昂贵,硬件也很便宜! 对于任何事情,您都需要一个交易预算(对系统要经历或要经历的不同组件和流程进行很好的分解)。
我们如何进行性能测试? 向系统施加负载,看看吞吐量是上升还是下降? 当我们施加负载时,系统的响应时间是多少? 压力测试不同于负载测试(请参阅负载测试 ), 压力测试 (请参阅压力测试 )是发生故障(系统崩溃)的一个点,理想的系统将以一条平线继续。 同样重要的是,不仅要从一个点执行负载测试,而且要同时从多个点执行负载测试。 最重要的是,高持续时间测试非常重要–这会给表面带来很多异常,例如内存泄漏等。
建立一个测试套件是一个好主意,它由较小的部分组成。 我们需要知道我们使用的系统的基本构建模块以及我们可以从中获得什么。 我们是否知道系统的不同阈值点以及它的组件可以处理多少? 了解我们使用的算法,知道如何测量它们并相应地使用它非常重要–使用真实数据进行反向操作。
我们什么时候应该测试性能?
“过早的优化是万恶之源” –唐纳德·努斯/托尼·霍尔
优化是什么意思? 知道并选择您的数据,并围绕它进行工作以提高性能。 新的开发实践:我们需要经常进行早期测试!
从性能的角度来看,“首先测试”实践非常重要,然后逐步设计系统,因为将来更改可能会花费很多。
红色–绿色– 调试 – 配置文件 –重构,这是一种“测试优先”性能方法的新方法,而不是仅是“红色-绿色重构”方法! 反馈周期更早,更短比将来发现问题要好。
如果您在Performance空间工作,则使用“像实时”配对工作站一样,Mac是一个不好的例子-linux机器是一个更好的选择。
性能测试可能会导致构建失败–并且它会导致CI系统中的构建失败! 微型基准测试仪(即游标卡尺)应该是什么样? 在代码中进行分割可能会非常昂贵,请改用mask操作符 !
并发测试呢? 仅仅是性能吗? 不变量? 争论?
系统性能测试如何? 我们是否应该能够测试不同范围的大型和小型客户。 了解您使用的系统的深入细节很有趣。 业务问题是要解决的核心和最重要的问题,而不是讨论要使用什么框架来构建它 。 请不要使用Java序列化,因为它不是为在线协议设计的! 使用观察者来衡量系统的性能,而不是从系统内部来衡量它。
性能测试课程–很多技术知识和文化知识。 技术课程–学习如何进行测量,查看直方图! 不要对系统进行采样,当系统变得奇怪,离群值等等时,我们会漏掉一些东西–直方图有帮助! 了解系统需要长时间的区域周围发生的事情很重要! 从操作系统捕获时间也非常重要。
随着时间的流逝,您将获得–准确性,精度和分辨率,大多数人将所有这些混合在一起。 在具有双插槽的机器上, 时间可能不同步。 时间信息的质量在很大程度上取决于您所使用的操作系统。 在虚拟化系统上或两台不同的计算机之间,时间可能是个问题。 这个问题可以解决,就是在两个系统之间往返操作(注意启动和停止时钟时间),然后将它们的一半来获得更准确的时间。
了解您的系统及其底层组件–获取指标并进行研究! 使用像perstat这样的linux工具,将在您的CPU和OS上提供大量与性能和统计相关的信息-分支预测和缓存丢失!
RDTSC不是命令指令执行系统,x86是命令指令系统,并且操作不会以无序方式发生。
约束理论! –始终以数字1开头,该数字会花费大部分时间–系统的瓶颈,其余的问题序列可能取决于数字1,而不是单独的问题!
尝试创建绩效团队是一种反模式–让专家帮助将技能带给团队的其他成员,并扩展他们的能力!
提防YAGNI –关于执行性能测试–找借口!
提交构建> 3.40分钟=令人担忧,对于验收测试构建> 15分钟=降低团队信心。
测试环境应该等于生产环境! 这些天容易获得完全相似的硬件!
结论:在编写依赖于低延迟的应用程序时,从“测试优先”性能测试方法开始。 了解您的目标并朝着目标努力。 从硬件到开发环境,全面了解您的基础系统。 在性能测试中,不仅重要的技术问题,文化的问题也同样重要。 在团队中共享和传播知识,而不是将知识隔离给一两个人,即所谓的团队专家。 每个人的责任不只是团队中的几个前辈。 详细了解跨各种硬件和操作系统以及系统之间的时间。
由于查看所有此类视频和文章不切实际,因此在下面的链接中提供了许多视频和文章以供进一步研究。 在许多情况下,我已经解释或直接引用了作者为保留信息和希望传达的意思而不得不说的话。
有用的资源
- 您的GC日志对您说话吗,柯克·佩珀丁(Kirk Pepperdine)的G1GC版– 幻灯片 – 视频
- 表演特别兴趣小组的讨论 –由Richard Warburton主持 (视频)
- 缓存: @RichardWarburto提供的“更有效地理解,衡量和使用CPU缓存” (视频和幻灯片)
- Jonathan Corbet撰写的有关原子I / O操作(Linux)的文章
- Gil Tene的有关Azul Zing,低延迟GC和OpenJDK的文章和演示 (视频和幻灯片)
- Martin Thompson的无锁算法实现终极性能
- Performance Java User's Group –“面向想要将系统推向新高度的专业Java开发人员”
- 通过Kirk Pepperdine 调整线程池的大小
- Gil Tene 如何不衡量延迟
- 理解Java垃圾收集及其处理方法作者:Gil Tene
- Vanilla #Java了解Core Java的真正工作原理可以帮助您编写更简单,更快的应用程序 ,作者Peter Lawrey
- 在生产中对Java进行性能分析 – Kaushik Srenevasan撰写
- Alexey Ragozin的HotSpot JVM垃圾回收选项备忘单(v3)
- 优化Google的仓库规模计算机:NUMA体验 – Univ的作者。 Cal(SD)和Google的资料!
- MegaPipe:多位作者的可扩展网络I / O的新编程接口 !
- 每个程序员应该了解的关于内存的知识Ulrich Drepper
- 内存壁垒:针对软件黑客的硬件视图 – Paul E. McKenney(Linux技术中心– IBM Beaverton)
翻译自: https://www.javacodegeeks.com/2013/12/part-2-of-3-synopsis-of-articles-videos-on-performance-tuning-jvm-gc-in-java-mechanical-sympathy-et-al.html