550 likes | 609 Views
JTC 上海分公司技术主管 - San Hong Li 2013 年 7 月 23 日. 实际性能 认识和改进应用程序性能. 重要免责声明!. 本演示文稿中包含的信息仅作为参考而提供。 尽管已尽力验证本演示文稿中包含信息的完整性和准确性。但是这些信息“按原样”提供,不含任 何形式(无论明示还是暗示)的担保。 本演示文稿中的所有性能数据都是在受控环境中收集。根据硬件、软件或者基础架构差异,您自己 的测试结果可能有所不同。 本演示文稿中的所有数据仅作为指南。
E N D
JTC上海分公司技术主管 - San Hong Li 2013 年 7 月 23 日 实际性能认识和改进应用程序性能
重要免责声明! 本演示文稿中包含的信息仅作为参考而提供。 尽管已尽力验证本演示文稿中包含信息的完整性和准确性。但是这些信息“按原样”提供,不含任 何形式(无论明示还是暗示)的担保。 本演示文稿中的所有性能数据都是在受控环境中收集。根据硬件、软件或者基础架构差异,您自己 的测试结果可能有所不同。 本演示文稿中的所有数据仅作为指南。 此外,本演示文稿中的信息基于 IBM 目前的产品计划和战略,IBM 可能进行更改,恕不另行通知。 IBM 及其附属公司对因使用本演示文稿或任何其他文档(或者其他相关方式)而造成的任何损害均 不负责。 本演示文稿中的任何信息都并非旨在,也不应具有以下效果: - 通过 IBM、其附属公司、其供应商和/或许可方创建任何保证或演示文稿
演讲人简介 .................................................................................................................................. • 8 年 Java 工作经验。 • 近期工作重点: • 针对“云”的 Java 虚拟机改进 • 多承租方技术 • JVM(J9) 开发 • 以往工作经历 • Java 安全性开发(Expeditor,Lotus Notes 内核) • 网络编程 • 我的联系信息: • 邮件:lisanh@cn.ibm.com • 微博:sanhong_li
此演讲的目标 • 介绍了一种用于性能分析的简单且通用方法 • 讨论常见的性能瓶颈 • 说明如何分析一个简单应用程序
议题 • 性能分析方法 • 应用程序的各层 • 识别和解决性能问题
性能分析方法 • 由外而内的方法 – 从性能测量点开始 – 遵循活动路径的顺序 – 非常适于识别性能问题 • 分层方法 –“自下而上”或者“自上而下” – 分析并消除应用程序的各层 – 根据自己的需要来简化问题 – 非常适合于应用程序运行状况检查 • 两种方法结合通常很有效
性能基线 • 务必进行可重复、有代表性的 性能测试 • 度量基线 性能 – 内部度量影响正在度量对象的性能 – 外部度量对系统性能影响较小 • 尽可能地度量多次 – 不同的测试运行会发生变化 • 尽可能地确保一致性 – 不仅指运行的负载测试 – 计算机和网络的状态会有特别的影响
分层方法 • 分为三层部署: – 基础架构:计算机硬件和操作系统 – Java 运行时:垃圾收集 – Java 应用程序:Java 应用程序代码 • 每一层都会受到资源限制,通常为: – 内存 – CPU – 同步 – I/O
基础架构 • 典型资源限制: – 内存:物理内存不足导致分页/交换 – CPU:CPU 时间不足限制应用程序吞吐量 – I/O:I/O 不足限制应用程序吞吐量 – 同步由 Java 运行时/Java 应用程序所驱动 • 易于诊断 • 易于解决(相对来说) • 注意,每一项资源限制也可能由堆栈不足而引起???!
基础架构:内存使用 • 基础架构将内存用于: – 返回进程数据:OS 运行时、Java 运行时、Java 应用程序 –缓存 IO:文件系统和网络缓冲区 • 物理内存不足会引起: – 减少和删除 IO 缓存 – 进程内存分页/交换到磁盘 • 对于 Java 进程而言,分页/交换的成本很高 – 尤其会影响垃圾收集性能 • 分页通常是基于“最近最少使用”来进行 • 在标记与清除阶段对所有 Java 堆进行遍历 • “最近最少使用”对 Java 堆的作用不明显
基础架构:CPU 使用 • CPU 时间可用性不足会降低性能 • 会周期性发生: – 运行批处理程序的 cron 作业 – 数据库备份 • 或在高负载期间: – 系统受 CPU 约束,限制性能
检测基础架构问题 • 使用操作系统级别工具进行检测 • Windows 上的内存: – 分页:将“perfmon”与“Process”计数器结合使用以获得“页错误/秒” – 文件缓存:将“perfmon”与“Memory”计数器结合使用以获得“系统缓存驻留字节” • Windows 上的 CPU: – 按进程:将“perfmon”与“Process”计数器结合使用以获得“处理器时间百分比” – 按计算机:将“perfmon”与“Processor”计数器结合使用以获取“处理器时间百分比” • Windows 上的 IO: – 网络:将“perfmon”与“Network Interface”计数器结合使用以获取“输出队列长度” – 磁盘:将“perfmon”与“Physical Disk”计数器结合使用以获取“当前磁盘队列长度”
解决基础架构问题 • 将更多物理资源添加到进程 – 分配更多到:计算机、来宾操作系统、LPAR、区域等等 • 减少物理资源需求 – 减少应用程序占用空间 – 减少应用程序 CPU 使用 – 减少 IO
页面响应性能基准测试:基线 页面性能 PlantsbyWebSphere
页面响应性能基准测试:删除的分页 页面性能 PlantsbyWebSphere
对页面 URL 性能的影响 • PlantsByWebSphere 1.7% • servlet_ShoppingServlet{2} 22.8% • servlet_ShoppingServlet 0.3% • Shopping{1} 0.4% • Shopping{4} 21.4% • Shopping_1_1 17.9% • Shopping_2_2 2.8% • Shopping_2_3 16.7% • Shopping_2_4 24.1% • Shopping_2_5 13.8% • 页面性能提高 0.3% 至 24.1% • 无异常值:0.4% 至 22.8% • 性能最好的页面增长最多。总增长只有大约 4% • 对整个页面性能的细微影响
对垃圾收集停顿时间的影响 • 减少: • 最长停顿时间: 38% • 平均停顿时间: 13% • 垃圾收集所用时间 11% • 对垃圾收集性能(尤其是停顿时间)有很大影响
交换/分页和 Java 应用程序 • • 交换/分页通常是基于“最近最少使用”来进行 • 将实时但非最近使用的数据写出到磁盘来为所用数据释放 RAM • 而 Java 在垃圾收集期间会“使用”非最近使用的数据 • 为了确定数据是否“实时”以及应用程序是否仍需要该数据。 • 任何已交换出的 Java 堆内存必须在垃圾收集期间交换
Java 运行时 • 典型资源限制: – 内存:Java 堆不足会导致内存不足或垃圾收集开销过高 – CPU 垃圾收集的开销,或由 Java 应用程序驱动 – 同步,由 Java 应用程序驱动 – IO,由 Java 应用程序驱动 • 易于诊断 • 易于解决(相对来说)
Java 进程内存布局 • 操作系统进程与任何其他应用程序相似: • 受操作系统和架构限制 • 32 位架构可寻址范围为: • 2^32 即 0x00000000 – 0xFFFFFFFF • 即 4GB • 应用程序并不能利用所有可寻址空间 • 操作系统需要内存以用于: • 内核和运行时支持库(如 C 运行时) • 因操作系统而有所不同 • 需要多少内存,以及内存位于哪里 0 GB 4 GB 2 GB 1 GB 3 GB 0x40000000 0x40000000 0x40000000 0x0 0xFFFFFFFF
应用程序可使用的原生堆 0 GB 4 GB 2 GB 1 GB 3 GB • 在 Windows 中 • 在 AIX(1.4.2,含小堆)中 Java 堆 原生堆 操作系统空间 0x40000000 0x40000000 0x40000000 0x0 0xFFFFFFFF VM 资源 库 0 GB 4 GB 2 GB 1 GB 3 GB 内核 Java 堆 原生堆 库 0x40000000 0x40000000 0x40000000 0x0 0xFFFFFFFF VM 资源
Java 将本机内存用于哪些方面? 内核空间 • Java 堆 • 作为连续的块进行分配 • 从启动开始就占用 -Xmx 值 • 即时 (JIT) 编译器 • 运行时数据 • 存储执行代码 • 虚拟机资源: • 调试引擎 • 跟踪缓冲区 • 转储缓冲区 • 垃圾收集基础架构 • JNI 分配 • JNI 函数中的原生 malloc/new • 由接口本身使用 • 支撑 Java 对象的资源 • 类和类加载器 • 线程 • 直接 java.nio.ByteBuffers • 套接字 用户空间 Java 堆 JIT 数据 VM 原生分配 Java 库
支撑应用程序的资源 • 应用程序使用和垃圾收集可以间接推动其他内存使用 如 java.lang.Thread 实例有底层原生资源: • 原生线程结构(如 pthread_t) • 相关原生栈(大小取决于平台) • Java 堆栈结构 Java 堆 原生堆
32 位与 64 位 JVM • 32 位 Java 进程有可能达到的最大的堆 • 因使用的操作系统和平台而有所不同 • 取决于进程内存布局 • 在 AIX 上,Java 堆可能达到的最大大小为 3.25 GB,2.5 GB 比较明智 • 64 位进程没有此限制 • 存在限制,但因为上限很大,因此可有效忽略限制因素 • 寻址通常在 2^44 到 2^64 之间 • 即 16+ TB • 升级到 64 位即可消除 Java 堆的大小限制 • 然而,能够使用更多内存并不是“免费”的 • 64 位应用程序执行速度慢 • 必须操作更多数据 • 缓存性能降低 • 64 位应用程序需要更多内存 • Java 对象引用更大 • 内部指针更大 • 由于压缩指针,因此在 Java 6.0 中对于此项进行了重大改进 • 仅当需要远大于 2GB 的堆大小以实现更高性能,或者仅当应用程序使用计算密集型算法来进行统计、加密等用途时(这将受益于高精度支持),才推荐使用 64 位 JVM
Java 运行时问题 • Java 堆内存不足会导致: – 由于 Java 堆耗尽而出现内存不足 – 过度运行垃圾收集,增加 CPU 负担并影响性能 • 非 Java (“原生”)堆内存不足会导致: – 由于进程地址空间耗尽而出现内存不足 – Java 堆垃圾收集的驱动器(DirectByteBuffer 清理器)
检测 Java 运行时问题 • 日志和跟踪分析: – “原生”堆:操作系统级别日志(ps、svmon 和 perfmon) – Java 堆:verbose:gc 输出 • 后处理使用 IBM Monitoring and Diagnostic Tools for Java - Garbage Collection and Memory Visualizer(GCMV) • 实时监视: – “原生”堆:IBM Monitoring and Diagnostic Tools for Java - Health Center – Java 堆:IBM Monitoring and Diagnostic Tools for Java - Health Center Visual VM, Mission Control
选择正确的堆大小: 43 • 垃圾收集将调整堆大小,使其占用空间保持在 40% 至 70% 之间 • 堆占用空间超过 70% 会引起垃圾收集过于频繁 触发器增多便等于变慢 • 堆占用空间低于 40% 意味着垃圾收集频率不足,周期长于所需要的周期长度 触发器收缩等于变慢 • 因此,应将堆最大容量设置成比应用程序最大占用空间大 43% • 最大占用空间 + 43% 意味着占用空间为整个堆的 70% • 堆最大大小 = 占用空间 + (0.3 x 堆最大大小) (0.7 x 堆最大大小) = 占用空间堆最大大小 = 占用空间 / 0.7堆最大大小 = 占用空间 * 1.43 • 例如,对于 79 MB 占用空间,请指定 100MB 最大堆大小= 70 MB + (0.43 x 70 MB) = 70 MB + 30 MB = 100 MB
解决 Java 运行时问题 • 添加更多资源到 Java 运行时 – Java 堆:增加 Java 堆大小 –“原生”堆:升级到 64 位或减少 Java 堆大小 • 降低内存需求 – 减少 Java 应用程序占用空间
对垃圾收集停顿时间的影响 • 减少: • 垃圾收集所用时间 59% • 然而,这仅占全部时间的 4.84%
页面响应性能基准测试:基线 页面性能 PlantsbyWebSphere
页面响应性能基准测试:删除的分页 页面性能 PlantsbyWebSphere
页面响应性能基准测试:增加后的 Java 堆大小 页面性能 PlantsbyWebSphere
页面性能提高 • PlantsByWebSphere 0.4% • servlet_ShoppingServlet 0.0% • servlet_ShoppingServlet{2} 33.2% • Shopping{1} -0.6% • Shopping{4} 4.5% • Shopping_1_1 2.9% • Shopping_2_2 0.1% • Shopping_2_3 2.1% • Shopping_2_4 14.2% • Shopping_2_5 8.2% • 页面性能提高 0.6% 至 33.2% • 无异常值:0.0% 至 14.2% • 总增长只有大约 4% • 对总体页面性能的影响相对较小
Java 应用程序 • 典型资源限制: – 内存:缓存不足影响应用程序吞吐量和响应速度 – CPU:线程不足会限制可伸缩性 – 同步:同步资源限制应用程序的可伸缩性和吞吐量 – I/O:I/O 阻塞限制吞吐量和响应速度 • 难以诊断 • 解决的成本很高(或不可能)
Java 应用程序 CPU 使用 • Java 方法过多使用 CPU 可突显潜在优化范围 – 不必要地过多调用代码 • 通过事件驱动的模型很容易完成 – 算法不是最高效的 • 如果开发时性能不是重点,则很容易完成 • 修复受限于 CPU 的应用程序需要知道运行的是什么代码 – 识别出适合于优化的方法 • 对应用程序不会用到的方法进行优化是在浪费时间 – 识别出哪些方法所花费的时间超过预期 • “为什么花费这么多时间在这个微不足道的方法上?”
Java 应用程序同步 • 吞吐量不会随负载而线性增加 • CPU 吞吐量限制仍然很低 – 无法伸缩 – 并非所有 CPU 都被利用 – 吞吐量和响应速度方面的限制 • 为了应用程序的正确性,线程需要彼此同步,而此处存在瓶颈 – 由于大量线程同时需要同步资源而导致 – 由于拥有资源的线程长时间占用资源而导致 – 或两者兼而有之
运行情况中心:应用程序方法 CPU 使用 http://mcsholding.com/DetailsPage.aspx?Page_Id=42
页面响应性能基准测试:基线 页面性能 PlantsbyWebSphere
页面响应性能基准测试:删除的分页 页面性能 PlantsbyWebSphere
页面响应性能基准测试:增加后的 Java 堆大小 页面性能 PlantsbyWebSphere
页面响应性能基准测试:增加后的 Java 堆大小 页面性能 PlantsbyWebSphere
页面 URL 性能提高 servlet_ShoppingServlet 80.8% 提高 5 倍 Shopping{1} 94.3% 提高 20 倍 • 页面性能提高 80% 至 95% • 受影响页面有 5 倍和 20 倍提高 • 总增长为 48%!
总结 • 重要性: – 可重复的基准测试 – 随着变化而进行的增量测量 – 重复测试和验证 • 可以利用工具查看情况: – 垃圾收集和内存可视化(所有供应商) – HealthCenter(仅限 IBM) – 其他分析器(如,YourKit)(所有供应商) – 内存分析器(所有供应商)