首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

使用 IBM Support Assistant 找出在企业应用程序中确定和解决性能问题的模式(3)

使用 IBM Support Assistant 找出在企业应用程序中确定和解决性能问题的模式(3)

案例研究 3:高 CPU 和内容使用我们的第三个案例研究来自与前一个案例研究相同的 TelecomApp。在该案例研究中,我们试图解决应用程序的内存使用率高的问题,以及高 CPU 使用率的问题。这导致在应用程序运行两三天后会出现堆内存耗尽的情况,并在生成堆转储时停止。应用程序使用 IBM JDK 中的默认 optthruput GC 策略,其目标是实现最佳吞吐量。
首先,我们使用 GCMV 分析了所使用的堆的生产环境详细 GC 日志。如图 12 所示,所使用的堆的曲线(粉红线)显示,该堆总是有大约 750 MB 的数据。所使用的堆在夜间(1:30 am 和 6:00 am 之间)变得最低。日志也有相当数量的标记堆栈溢出错误;当堆中有太多活动对象时(或者,更准确地说,嵌套非常深的对象),以及 GC 在标记阶段使用的堆栈溢出时会发生这种情况。图 12 也显示了通过堆大小的突然下降所表示的两次重新启动。
图 12. GCMV: 在 TelecomApp 中堆的使用一直很高 (750 MB)详细 GC 日志还显示出因压缩时间长而造成暂停时间长。这种模式在整个过程中非常均匀,并且几乎所有长暂停时间都因长压缩时间造成(参见图 13)。进一步分析这些长暂停时间的日志,可以发现原因代码 16 的分配失败;当垃圾收集器无法使可用存储量至少增加 10% [9] 时,原因代码 16 。由于垃圾收集的开销大约为 13%(GC 开销= GC 暂停所花费的时间/总的执行时间 * 100)。图 13 还显示,GC 一直在释放内存;但是,被释放的内存(绿色曲线)由于碎片而一直减少,并在最终需要压缩(红线)。请注意,堆碎片也可能带来暗物质,暗物质不能用于满足分配请求。在两个分配对象之间任何小于 512 字节的堆空间,都不能被 JVM 用于分配请求,并被称为 “暗物质”。
图 13. TelecomApp: GC 释放的内存(绿色曲线)由于碎片而一直减少,并在最终需要压缩(红线)生产堆(转储时发生内存不足的错误)的分析表明,共 1.2 GB 内存正在被使用,其中接近 585 MB 的留存堆被属于两个类的对象所占用(参见图 14):
  • com.TelecomApp.cache.impl.CacheEntryImpl
  • com.TelecomApp.mcs.devices.InternalDeviceImpl。
请注意,一个对象的 Retained 大小是它的 Shallow 大小加上只可以从该对象直接或间接访问的对象的 Shallow 大小。表 1 显示了按对象类型分解的浅堆。还记得,对象的 Shallow 大小是为存储对象本身而分配的内存量,并未考虑到所引用的对象。您可以看到,前三位的贡献者都与 HashMaps: java.util.HashMap$Entry 相关,java.util.HashMap$Entry 贡献了近 390 MB(接近 33% 的总堆大小)。这意味着,com.TelecomApp.cache.impl.CacheEntryImpl 和 com.TelecomApp.mcs.devices.InternalDeviceImpl 都由较大的 HashMap 组成。
应用程序开发团队再次强调,应用程序在内部使用了很多缓存,以缓存移动设备的特定策略,从而实现更快的响应时间。这就解释了为什么堆的使用量从来没有大幅下降。这似乎是一个应用程序设计的问题,因为应用程序的内存占用似乎超出了系统可以提供的量。然而,应用程序团队坚持认为,缓存条目应该每隔 30 分钟左右自动失效一次,而且他们排除了重新设计的方案,因为这将需要相当大量的工作。
图 14. TelecomApp 生产堆转储细分(留存堆)表 1. TelcomApp 生产堆转储细分(浅堆)计数总大小类型1,871,075305,453,504array of char8,558,144273,860,608java/util/HashMap$Entry927,421114,439,696array of java/util/HashMap$Entry1,774,00256,768,064java/lang/String639,45352,688,552array of java/lang/Object92,92249,771,728array of byte901,80843,286,784java/util/HashMap899,46735,978,680com/TelecomApp/mcs/themes/PropertyValue109,74027,300,328array of int26,62216,834,352array of  com/TelecomApp/mcs/themes/StyleValue514,37616,460,032java/util/Hashtable$Entry329,67515,824,400com/TelecomApp/styling/properties/PropertyDetailsImpl398,76912,760,608com/TelecomApp/styling/impl/engine/StylerImpl251,68911,886,160array of com/TelecomApp/mcs/themes/PropertyValue324,78210,393,024com/TelecomApp/mcs/css/version/DefaultCSSProperty340,8369,177,496array of long95,2468,393,000array of  java/util/Hashtable$Entry338,9058,133,720java/util/BitSet491,1917,859,056java/util/HashSet281,8177,144,296array of com/TelecomApp/styling/impl/engine/matchers/SimpleMatcher284,6486,831,552com/ibm/ws/cache/Bucket203,7816,520,992javax/servlet/jsp/tagext/TagAttributeInfo1755,708,528array of  org/apache/xpath/objects/Xobject234,1555,619,720com/TelecomApp/styling/impl/device/DeviceStylesDelta221,1205,306,880java/util/ArrayList
详细 GC 日志还指出,该应用程序在使用一些大型对象。我们分析了浅对象大小的堆转储。在将近 2,500 万个对象中,只有 30,000 个对象大于 1024 字节。这在数量上构成了几乎 0.12% 的堆对象。然而,在 1.2 GB 的总空间中,这些大对象所占用的累积空间大约有 222 MB (18.5%)。平均对象大小为 51 字节,而中等大小是 32 字节,这也是模式。这表明,堆由相当数量的小对象组成,这些小对象散布在少数大型对象之间的整个堆中。浅对象的直方图如图 15 所示。
图 15. TelecomApp 生产堆的对象大小直方图为了排除任何内存泄漏,在分阶段环境中以较低的恒定负载执行了一次长达 10 小时的运行。在整个实验过程中,详细 GC 日志显示的堆使用情况几乎不变,这排除了任何内存泄漏。然而,分阶段环境无法复制在生产系统中观察到的长暂停时间。其原因可能是提供给系统的负载。
到目前为止,我们的分析排除了任何内存泄漏,并且以下特征变得明显:
  • 堆是零碎的,并导致长压缩时间和长期保持的高 CPU 使用率,以及浪费的堆空间。
  • 应用程序是少量大对象和大量小对象的组合。
  • 应用程序有一些短期对象,但堆的大部分似乎由相对长期的缓存对象组成。
解决上述问题的垃圾收集技术已在文献中讨论过。鉴于上述应用程序的特征,我们考虑两种方案:
  • 使用分代和并发收集器。对于创建大量短期对象的事务型应用程序,通常建议使用该策略(在 IBM JDK 中被称为 Gencon GC 策略)。该策略将堆空间划分为临时空间(用于新的和短期的对象)和用于旧对象的永久空间 (tenured space)。由于该策略使用在临时空间 (nursery space) 执行收集(小的收集)的过程中复制临时空间的活动对象,它就会排除临时空间的碎片。然而,当永久空间填满时,会发生全局收集,并且不可达的对象会被收集,这可能会导致永久空间中出现碎片。理想的情况下,我们希望将所有的长期缓存对象移动到永久空间,并且在临时空间中只留下短期对象。
  • 针对创建得最频繁的对象使用对象池。这通过减少短期对象的创建和收集,可解决碎片的问题。然而,这需要在应用程序代码中为频繁创建的对象创建一个对象池,或使用 IBM JVM 中内置的池功能。
图 16. 已修改 GC 策略 (Gencon) 的 TelecomApp我们尝试了两种解决方案,但意识到第一个选项在生产系统上更容易实现。我们首先尝试使用默认设置的分代和并发收集器。在 Java 命令行上可以很容易地为 WebSphere Application Server 指定垃圾收集策略(使用标志 –Xgcpolicy)。我们使用标志 –Xgcpolicy:gencon 指定分代策略。垃圾收集的开销(在 GC 暂停时所花费的时间百分比)从 13% 下降到 1.18%。在默认的 gencon 设置中,临时堆的大小被设置为 55-60 MB,其余的则被用作永久空间。当此设置投入生产时,长暂停时间就消失了,因为已不需要压缩(参见图 16)。然而,已使用内存中的狭窄的锯齿模式和可用的永久空间(参见图 16)也表示,默认的临时空间大小过小。这导致太多的小的集合,并且对象被过早地激发到永久空间。这也导致永久空间很快就被填满,并需要一个全局连接。
锯齿模式显示,在每个全局集合中,大约有 400-550 MB 内存从每个永久空间释放出来。我们以此为标准,将临时堆的初始大小设置为 256 MB,并将最大临时堆大小设置为 512 MB​​。使用标志 Xmns 和 -xMnx 可以很容易地分别控制初始和临时堆大小。当此设置在生产环境中实现时,GC 开销从默认 Gencon 设置中的 1.18% 进一步下降到 0.8%(表 2)。持续了一段时间的 CPU 使用率过高的问题(这是由默认设置中频繁的分段和压缩造成的)也得到了解决。
表 2. TelecomApp: 通过 GC 调优实现性能提高GC 策略 GC 暂停所花的时间最大暂停时间(秒)平均暂停时间(秒)Optthroughput GC 策略
(原始) 13% 11.2 1.35Gencon GC 策略
(默认临时大小) 1.18% 6.26 0.03Gencon GC 策略
(增加的临时大小) 0.80% 1.18 0.08
本案例研究强调了高 CPU 使用率场景,其中问题发生的原因是应用程序设计和基础架构设置(垃圾收集策略设置)的组合。由于堆碎片和重复压缩,应用程序遇到高 GC 开销的问题,并且这导致了高 CPU 使用率。
该案例研究还说明与性能调优相关的复杂性:为拥有一个有趣的对象组合(大型和小型的对象,长期和短期的对象)的企业应用程序最优地调整 GC 设置,这可能并不简单,并且难以实现自动化。
返回列表