垂直打击之JVM剖析

让Java应用程序运行是一回事,但让他们跑得快就是另一回事了。在面对对象的环境中,性能问题就像来势凶猛的野兽。但JVM的复杂性将性能调整的复杂程度增长了一个级别。这里Refcard涵盖了JVM internals、class loading(Java8中更新以映射最新的元空间)、垃圾回收、故障诊断、检测、并发性,等等。html

介绍

Java是目前软件开发领域中使用最普遍的编程语言之一。Java应用程序在许多垂直领域(银行、电信、医疗保健等)中都有普遍使用。Refcard的目的是,帮助开发者经过专一于JVM内部,性能调整原则和最佳实践,以及利用现有监测和故障诊断工具,来提高应用程序在商业环境中的性能。java

它能以不一样的方式定义“optimal performance(最佳性能)”,但基本要素是:Java程序在业务响应时间要求内执行计算任务的能力,程序在高容量下执行业务功能的能力,并具备可靠性高和延迟低的特色。有时,数字自己变得模式化:对于一些大型网站,优秀的页面响应时间应该在500ms如下。在适当的时候,Refcard包括目标数字。但在大多数状况下,您须要根据业务需求和现有的性能基准本身决定这些。git

JVM Internals

基础知识

代码编译和JIT

编译Java字节码显然没有直接从主机执行本机代码那么快。为了提升性能,Hotspot JVM找出最繁忙的字节码区域,而后将其编译成更高效地原生、机器代码(自适应优化)。而后这种本地代码就会存储在非堆内存中的代码缓存中。apache

注意:多数的JVM是经过禁用JIT编译器实现的(Djava.compiler=NONE)。您只须要考虑禁用的关键性优化,好比JVM崩溃。编程

下图说明了Java源代码,即时编译流程和生命周期。windows

 

内存空间

HotSpot Java Virtual Machine是由如下的存储空间组成。数组

存储空间 描述
Java Heap Java程序类实例和数组的主存储器。
Permanent Generation(JDK 1.7及如下版本)Metaspace (JDK 1.8及以上版本) Java类元数据的主存储器。注意:从Java 8开始,PermGen空间就由元空间和使用本地存储器替换了,相似于IBM J9 JVM。
Native Heap(C-Heap) 本地内存存储线程、栈、包括对象的代码缓存,如MMAP文件和第三方本机库。

类加载

Java的另外一个重要特色是,在JVM启动以后,它可以加载编译的Java类(字节码)。根据程序的大小,在刚刚重启以后,程序在类加载过程当中性能会显著下降。这种现象是由于内部JIT编译器在重启以后须要从新开始优化。缓存

自JDK 1.7版本以后,有一些改进值得你们重视。例如默认的JDK class loader具备更好的装在类并发能力。安全

热点

关注的区域 建议
JVM重启后的性能降低 避免部署过量的Java类到一个单一的应用程序类加载器(例如:很是大的WAR文件)
运行时发现过多的类加载争夺(thread lock, JAR file searches…) ,下降了总体性能。 分析您的应用程序并识别代码模块进行动态类加载操做过于频繁。积极寻找非一站式类加载错误,如ClassNotFoundException和NoClassDefFoundError。再访Java映射API和适用状况下优化的过分使用。
java.lang.OutOfMemoryError: PermGen space (JDK 1.7及如下版本)java.lang.OutOfMemoryError:元空间(JDK 1.8及以上版本) 再访JVM Permanent Generation、Metaspace (MaxMetaSpaceSize)和本地内存容量在适用状况下的尺寸。分析应用程序类加载器和识别元数据的内存泄漏的源头。

 

故障诊断和监视

目标 建议
跟踪那些加载到不一样的类加载器的Java类。 配置程序选择使用的Java profiler,例如JProfiler或Java VisualVM。将重点放在类加载器的操做和内存占用上。能够经过–verbose:class. for the IBM JVM,生成多个Java核心快照跟踪活动的类加载器和加载类。
调查类元数据的内存泄露的能够来源。 配置程序和定义可能的culprit(s)。生成并分析JVmheap dump快照,专一于类加载器和java.lang.Class中的实例。

 

确保适当的Permanent Generation / Metaspace和本地内存大小。 密切监视你的PermGen、元空间和本机内存利用率,并调整到适合的最大容量。分析程序类加载器的大小,并寻找机会适当地减小元数据足迹。

垃圾回收

Java垃圾回收流程对于程序性能是相当重要的。为了提供有效的垃圾回收,Heap(堆)本质上是划分在子区域中。并发

堆区域

区域 描述
最新一代-Young Generation (nursery space) 新的或短暂的对象分配保留堆的一部分。垃圾被一个fast but stop-the-world YG的收集器进行回收。

 

在young space中呆了足够久的对象就会提高到old space。

注意:YG space的尺寸和GC频率太高将会显著影响程序的响应时间,从而致使JVM的暂停时间增长。

老一代-Old Generation  (tenured space) heap的一部分留给了long-lived对象。垃圾一般经过平行或并发(多数时候)进行收集,诸如CMS或gencon (IBM JVM)。

 

性能提示:根据应用程序的需求选择并测试最佳的GC策略是很是重要的。例如,当切换到并发GC收集(如CMS或G1)能够显著提升应用程序的平均响应时间(减小延迟)。

GC Collectors

选择正确的collector或GC policy能够将程序的性能、可扩展性和可靠性优化到最佳状态。许多应用程序对于响应时间延迟都很敏感,所以大多须要使用并发的回收器,例如HotSpot CMS或IBM GC policy balanced。

咱们强烈建议您经过适当的性能和负载测试肯定最合适的GC策略。应该在生产环境中执行全面监控策略,以跟踪总体的JVM性能,并肯定在以后须要改进的领域。

GC 论据 描述
串行回收器 -XX:+UseSerialGC (Oracle HotSpot) 不管新旧回收器都使用单独CPU,像是一种stop the world的时尚。


并行回收器(吞吐量回收器) -XX:+UseParallelGC-XX:+UseParallelOldGC
(Oracle Hotspot)

 

-Xgcpolicy:optthruput
(IBmJ9, single space, stop-the-world)

旨在利用CPU的内核优点。不管新旧回收器都使用多个Gcthreads(via –XX:ParallelGCThreads=n),从而更好地利用来自主机的可用的CPU内核来完成。注意:虽然回收时间能够显著减小,可是有着大尺寸堆的程序面临着large、stop-the-world、old回收,而且响应时间也受到影响。


确保适当的Permanent Generation / Metaspace和本地内存大小。 密切监视你的PermGen、元空间和本机内存利用率,并调整到适合的最大容量。分析程序类加载器的大小,并寻找机会适当地减小元数据足迹。 旨在最大限度地减小旧一代stop-the-world回收器对程序响应时间的影响。大多数使用CMS collector的老一代回收器与所述应用程序的执行同时进行。

 

注意:YoungGen collections仍然有stop-the-world事件,所以须要适当的微调,以减小总JVM暂停时间。

Garbage First (G1) Collector

HotSpot G1 collector是专为是专为知足用户定义的垃圾回收(GC)高几率暂停时间设计的,同时实现高吞吐量。

最新的HotSpot collector将heap基本划分到一组大小相等的堆区域,虚拟内存的每一个区域连续范围。它将回收压缩的活动集中在heap区域,那里充满了可回收的对象(garbage first)。换句话说就是,这个区域有最低限度的“live”对象。

Oracle建议在如下例子和状况下使用G1 collector,尤为是对于目前正在使用CMS或parallel collectors的:

  • 专为large heaps(>= 6 GB),并限制GC延迟(暂停时间<= 0.5秒)的应用程序设计。
  • 超过50%的Java heap被实时数据占用(对象不能被GC回收)。
  • 对象分析率和促进做用显著变化。
  • 不指望过长的垃圾回收或压缩停顿(超过0.5至1秒)。

Java Heap尺寸

你必定要知道没有GC策略能够挽救Java Heap尺寸不足的现象。这些演习涉及到为不一样的存储空间(包括新旧不一样的版本)配置最大和最小的容量,包括元数据和本地内存容量。这里有一些建议准则:

  • 在32-bit或64-bit JVM之间进行明智的选择。若是程序运行须要超过2GB内存,而且JVM暂停时间在可接受范围内,能够考虑使用64-bit JVM。
  • 永远将应用程序放在第一考虑。确保将其配置好,并根据程序的内存占用量调整heap尺寸。建议经过性能和负载测试来衡量实时数据占有量。
  • larger heap并不老是表现得更好、更快,所以不须要过分调整Java heap。并行中的JVM性能调优,找准机会减小或“spread”程序的内存占有量,以保证JVM的平均响应时间<1%。
  • 对于32-bit JVM,为了从元数据和本地heap中留出一些内存,考虑2GB的最大heap尺寸。
  • 对于64-bit JVM,咱们要想办法在垂直和水平层面进行扩展,而不是试图将Java heap尺寸增长到15GB以上。这种作法每每提供更好的吞吐量,更好地利用硬件,提升应用程序的故障切换功能。
  • 不准重复开发:充分利用开源以及商业故障排除的优点和监控工具,使这些变成可能。APM(应用性能管理)产品在过去十年里发展迅猛。

JDK 1.8 Metaspace指南

目标 建议
内存大小GC调整

 

监控和故障排除

默认状况下,元空间内存空间是无界的,并使用可用于动态扩展的process或OS native memory。内存空间分红快并经过mmap被JVM进行存储。咱们建议保持默认设置,以动态调整模式为出发点,将简化的尺寸与密切监测的应用程序元数据占有量相结合,从而进行更好的容量规划。新增一个JVM选项(-XX:MaxMetaspaceSize=<NNN>),可让您限制分配给class metadata的本地内存。当面临物理资源(RAM)紧张或相似于内存泄露的状况时,建议将它做为一个保障机制。

 

对那种具备larger class metadata footprint或dynamic classloading的Java应用程序,咱们建议经过新的JVM选项调整初始元空间大小 :-XX:MetaspaceSize=<NNN>,例如:1GB。这种调整方法将有助于避免包括class metadata在内的早期垃圾回收,尤为是在Java应用程序的 “warm-up”期。

Hot Spots

故障诊断和监视

目标 建议
测量和监视应用程序YoungGen和OldGen内存占用,包括GC活动。为您的应用程序决定正确的GC策略和Java堆大小。

 

调整应用程序的内存占用量,如live对象。

分析、监控您所使用的Java分析工具,如JProfiler、Java VisualVM或其余商业APM产品。容许经过–verbose:gc记录JVM GC活动。您也可使用相似GCMV(GC Memory Visualizer)的工具查看JVM的暂停时间和内存分配率。

 

性能提示:过多的内存分配率可能意味着须要进行垂直和横向扩展,或从多个JVM进程中分离出实时数据。

为了long-lived对象或long-term实时数据考虑,能够生成并分析JVM heap dump快照。Heap dump分析对于程序内存占用(retention)的优化是很是有帮助的。

性能提示:因为从32位到64位,Java应用程序对heap 的需求会比原来高1.5倍。因此,在Java 1.7及如下的版本(这是默认的)中使用 -XX:+UseCompressedOops是很是重要的。这样的参数调整大大减轻了64位JVM的性能压力。

调查OutOfMemoryError 问题,寻找OldGen内存泄露的根源。 使用相似Java VisualVM、Plumbr的工具(Java内存泄漏检测器),分析可能存在的内容泄露。性能提示:要着重分析最大的Java对象上。要意识到下降内存占有量就意味着提高性能,并下降GC活动。


使用相似 Memory Analyzer的工具生成并分析JVM heap dump快照。

Java并发性

Java并发性能够定义为程序同时执行多个任务的能力。对于大型的Java EE系统,这意味着执行多个用户的业务功能的同时,实现最佳的吞吐量和性能的能力。

不管是硬件能力仍是JVM稳定情况,Java并发性问题可能引发程序的瘫痪,严重影响程序的总体性能和可用性。

Thread Lock Contention

当您评估Java应用程序的并发线程的稳定情况时,你会常常遇到Thread lock contention的问题,这是目前最多见的Java并发问题。

例如:Thread lock contention会触发non-stop,它会尝试将一个缺乏Java类(ClassNotFoundException的)加载到默认的JDK 1.7 ClassLoader。

若是您在成熟的技术环境中碰见像Thread Dump analysis这样的问题,咱们强烈建议您积极面对它。这个问题的根源一般不一样于以前的Java synchronization to legitimate IO blocking或者其余的non-thread safe calls。Lock contention问题每每是另外一个问题的“症状”。

Java-level Deadlocks

真正的Java-level deadlocks是不太常见的,它一样能够极大程度地影响应用程序的性能和稳定性。当遇到两个或多个线程永远阻塞的时候,就会触发这样的问题。这种状况不一样于其余常见的那种“day-to-day”线程问题,例如 lock contention、threads waiting on blocking IO calls等等。真正的lock-ordering deadlock问题能够被看作以下:

Oracle HotSpot 和IBM JVM为大多数的deadlock detectors状况提供了解决方案,帮助您快速找出形成这种情况的罪魁祸首的线程。遇到相似lock contention troubleshooting的问题,建议从诸如线程转储分析为出发点来解决该问题。

一旦找到形成问题的代码根源,解决方案涉及lock-ordering条件寻址和来自JDK其余可用的并发编程技术,如java.util.concurrent.locks.ReentrantLock,提供了诸如tryLock()的方法。这种方法给予开发人员更大的灵活性,也为防止deadlock和thread lock “starvation”提供了更多方式。

Clock Time和CPU Burn

在进行JVM调优的同时,也有必要检查应用程序的行为,更确切地说是最高clock time和CPU burn的贡献者。

当Java垃圾回收和线程并发再也不是压力点,深刻到你的应用程序代码的执行模式,并专一于顶级响应时间贡献者(也叫做clock time)是很重要的。检查应用程序代码的消CPU耗和Java 线程(CPU burn)也一样相当重要。CPU使用率较高(>75%)是不正常的(良好的物理资源的利用率)。由于这每每意味着效率低下和容量问题。对于大型的Java EE企业应用,保持安全的CPU缓冲区是必要的,以应对突发的负载冲击状况。

摒弃那些传统的跟踪方法,如在代码中加入响应时间“日志”。Java剖析工具和APM解决方案偏偏能够帮助您分析这类型的问题。这种方式更加高效、可靠。对于Java生产环境缺少一个强大的APM解决方案。您仍然能够依赖诸如Java VisualVM的工具,经过多个快照进行thread dump分析,并使用OS CPU分析每一个线程。

最后的建议是,不要妄图同时解决全部的问题。列出排在最前面的5个clock time和CPU burn问题,而后寻找解决方案。

Application预算

其余关于Java应用程序性能的重要方面是稳定性和可靠性。在有着99.9%典型可用目标的SLA umbrella下,稳定和可靠对于程序的操做尤其重要。这些系统应该具备高容错级别,并对应用和资源进行严格的预算,以防止发生多米诺效应。用这种方法能够防止一些这样的状况,例如,一个业务流程使用全部可用的物理,中间件或JVM资源。

Hot Spots

超时管理

Java application与外部系统之间缺少合理的超时时间,因为中间件和JVM线程消耗(blocking IO calls),可能致使严重的性能降低和中断。合理的超时时间能够避免在遇到外部服务提供商速度缓慢的时候,Java线程等待过久。

 

工具

目标 建议工具
自动、实时地性能监控、调节、预警、趋势分析、容量管理,等等 Enterprise APM solutions(企业级APM解决方案)注意:APM解决方案提供了工具,这些现成的功能让您实现如下大部分的Java性能目标。
性能和负载测试 商业性能测试解决方案Apache JMeter

 

http://jmeter.apache.org/

JVM垃圾回收评估,内存分配率和故障排除 Oracle Java VisualVMhttp://docs.oracle.com/javase/8/docs/technotes/guides/visualvm/intro.html

 

http://java.dzone.com/articles/profile-your-applications-java

Oracle Java Mission Control

http://www.oracle.com/technetwork/java/javaseproducts/mission-control/java-mission-control-wp-2008279.pdf

http://www.oracle.com/technetwork/java/javase/jmc53-release-notes-2157171.html

IBM Monitoring and Diagnostic Tools for Java (via IBM Support Assistant tool)

http://www-01.ibm.com/software/support/isa/

JVM verbose:gc logs

JVM argument : -verbose:gc

http://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html

IBM GCMV

https://www.ibm.com/developerworks/java/jdk/tools/gcmv/

JVM堆和类的元数据的内存泄漏分析 Oracle Java VisualVM and Oracle Java Mission ControlIBM Monitoring and Diagnostic Tools for Java

 

Memory Analyzer (heap dump analysis, hprof and phd formats)

https://www.eclipse.org/mat/

https://www.ibm.com/developerworks/java/jdk/tools/memoryanalyzer/

JVM内存分析和堆容量评估 Oracle Java VisualVM and Java Mission ControlIBM Monitoring and Diagnostic Tools for Java

 

Java profilers (JProfiler, YourKit)

http://en.wikipedia.org/wiki/JProfiler

http://www.yourkit.com/

Memory Analyzer (heap dump and application memory footprint analysis)

JVM和中间件并发故障,如thread lock contention和deadlocks Oracle Java VisualVM and Oracle Java Mission Control (threads monitoring, thread dump snapshots)jstack, native OS signal such as kill -3 (thread dump snapshots)

 

http://www.oracle.com/technetwork/java/javase/tooldescr-136044.html#gblfh

IBM Monitoring and Diagnostic Tools for Java

注意:强烈推荐你们关注如何执行一个JVM线程转储分析的相关知识。

Java应用程序clock time分析和评测 Oracle Java VisualVM and Oracle Java Mission Control (build-in profiler, sampler and recorder)Java profilers (JProfiler, YourKit)
Java应用程序和线程CPU burn分析 Oracle Java VisualVM and Oracle Java Mission Control (CPU profiler)Java profilers (JProfiler, YourKit)

 

注意:必要的时候,您还能够依赖JVM线程转储和OS CPU每一个线程分析。

Java IO和remoting contention分析,包括超时管理评估和调整 Oracle Java VisualVM and Oracle Java Mission Control(threads monitoring, thread dump snapshots)

 

jstack, native OS signal such as kill -3 (thread dump snapshots)

IBM Monitoring and Diagnostic Tools for Java

注意:强烈推荐你们关注如何执行一个JVM线程转储分析的相关知识。

中间件,Java EE容器调整,如线程、JDBC数据源,等等 Oracle Java VisualVM and Oracle Java Mission Control (extra focus on exposed Java EE container runtime MBeans)Java EE container administration and management console
相关文章
相关标签/搜索