`
gao_xianglong
  • 浏览: 467749 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

笔者带你剖析大规模分布式Java平台JVM性能调优基础

阅读更多

《笔者带你剖析大规模分布式Java平台JVM性能调优基础》

 

前言

其实说到对JVM进行性能调优早已是一个老生常谈的话题,如果你所在的技术团队还暂时达不到淘宝团队那样的高度,无法满足在OpenJDK的基础之上根据自身业务进行针对性的二次开发定制调优,那么对于你来说,唯一的选择就是尽可能的熟悉JVM的内存布局,以及熟练掌握与GC相关的那些选项配置,否则JVM的基础性能调优不是痴人说梦

 

目录

一、性能调优的一些概念和目标;

二、性能调优的基本原则;

三、新生代的性能调优;

四、老年代的性能调优;

 

一、性能调优的一些概念和目标

相信对JVM有所了解的开发人员,对于调优过程中牵扯的吞吐性、低延迟/高响应应该不会感觉到陌生。既然生产环境中是大规模的分布式Java平台,JVM吃的内存必然不会太少。不知大家是否还曾记得,64位的JVM能够顺利访问大内存,其最主要的原因是因为其采用了64位的指针架构,这同时也是寻址访问大内存的关键要素。而与之相反的32位的JVM的内存却被限定在了2-3GB上限(与操作平台密切相关,Linux平台,Windows则为1.5G上限)。

 

大规模的分布式Java平台除了JVM吃的内存特别大外(笔者之前的项目单点持有内存为30GB),为了增加每一个节点的可用性,都是采用多JVM集群的部署模式,这样一来一旦发生单点故障的时候,不会导致整个服务不可用,从而也能够降低单点负载,提升整体程序的执行性能,更好的满足一些特定的高并发场景。

 

话说生产部署在服务器上的JVM大都是主动或者缺省选择server模式在奔跑,并且在Java7版本之后,JVM缺省开启了分层编译(Tiered Compilation)策略,由C1和C2编译器共同来执行本地代码的编译任务,C1编译器会对字节码进行简单和可靠的优化,以达到更快的编译速度,而C2编译器则会启动一些耗时更长的优化,以获取更好的本地代码编译质量。

 

那么对JVM进行性能调优的真正目的是什么呢?简单来说就是为了满足程序的高吞吐量、低延迟/高响应性等需求。但是笔者不得不提醒大家,调优是一个循序渐进的过程,必然需要经历多次迭代,最终才能换取一个较好的折中方案。笔者在《Java虚拟机精讲》中曾经提及过,垃圾收集器中吞吐量和低延迟这两个目标其实是存在相互竞争的矛盾,因为如果选择以吞吐量优先,那么降低内存回收的执行频率则是必然的,但这将会导致GC需要更长的暂停时间来执行内存回收。相反如果是选择以低延迟优先,那么为了降低每次执行内存回收时的暂停时间,只能够频繁地执行内存回收,但这又引起了新生代内存的缩减和导致程序吞吐量的下降。举个例子,在60s的JVM总运行时间里,每次GC的执行频率是20s/次,那么60s内一共会执行3次内存回收,按照每次GC耗时100ms来计算,最终一共会有300ms(即60/20*100)被用于执行内存回收。但是如果我们将选项“-XX:MaxGCPauseMillis”的值调小后,新生代的内存空间也会自动调整,相信大家都知道,内存空间越小就越容易被耗尽,那么GC的执行频率就会更频繁。之前在60s的JVM总运行时间里,最终会有300ms被用于执行内存回收,而如今GC的执行频率却是10s/次,60s内将会执行6次内存回收,按照每次GC耗时80ms来计算,虽然看上去暂停时间更短了,但最终一共会有480ms(即60/10*80)被用于执行内存回收,很明显程序的吞吐量下降了。因此,在JVM调优这个领域,没有任何一种调优方案是适用于所有应用场景的,同时,切勿极端才能够达到JVM性能调优的真正目的和意义

 

 二、性能调优的基本原则

简而言之,总而言之,对JVM进行性能调优时,有2个基本原则大家需要进行理解。首先是尽可能的让GC发生在新生代中,也就是尽可能的多执行Minor GC,因为我们都知道Full GC的执行频率尽管不会有Minor GC那么频繁,但是对程序响应性的影响是非常大的(笔者之前的项目Full GC诡异般的执行了50s,显然超出了对响应延迟的容忍度)。那么多让Minor GC执行,显然可以减少触发Full GC的频率

 

其次,GC所持有的可用内存越大(Java Heap所占有的堆空间越大),GC的执行效率越好。这是因为内存越大,达到回收阈值就越不容易,那么明显可以提升程序的吞吐量和响应性。当然这并不是说越大越好,如果一个项目JVM撑死只需要1-2G的运行内存,人傻钱多分配120G的内存量,或许程序在稳定情况下运行到硬件故障也不会发生一次Full GC。

 

既然内存并不是越大越好,总有一个阈值。这就牵扯到生产环境中,开发人员究竟应该如何对Heap分配初始大小?其实这很简单,一个经历过严谨测试的项目,必然会在测试环境中测试N个周期才会移交至生产环境中进行部署,那么在测试环境中,我们可以根据多次迭代后观察Full GC的数据信息来估算生产环境中究竟应该给我们的项目初始多大的内存空间。比如经过多次迭代后,Full GC产生的数据信息中,如果老年代中的活跃数据占用内存大小为100m,那么按照通用的计算法则,可以按照约3-4倍的占用倍数来恒定生产环境中应该分配的堆大小(即-Xms和-Xmx),新生代和老年代的比例官方建议按照整个堆的3/8来进行分配,也就是说选项-Xmn可以占用整个堆内存空间的3/8,这是一种非常简单和通用的计算和分配方式。而永久代则可以按照Full GC后产生的数据信息,根据永久代活跃数据占用内存大小的1.5倍进行恒定生产环境中应该分配的初始值。

 

这里笔者稍微补充一下,在一些高并发场景下,尤其关注吞吐量和高响应的应用中,应该将-Xms和-Xmx设定为同一值,以此避免内存动态调整时产生的Full GC操作,永久代-XX:PermSize和-XX:MaxPermSize同理。

 

三、新生代的性能调优

在HotSpot中,串行回收GC与并行回收GC是2个极端,在如今,更多人更倾向于选择后者,并且在一些极其注重吞吐量和高响应的应用场景下,并行回收有着串行回收无法比拟的绝对优势。由于堆空间中的对象大部分都是一些瞬时对象,因此这类对象的生命周期往往更多是由新生代进行“控制”,之前也说过,尽可能的让垃圾收集动作发生在新生代中,而不是Full GC。这样一来,对于新生代的性能调优就主要集中在几个问题上,首先是测量出Minor GC的执行平率和持续时间是否满足需求,以及-XX:ParallelGCThreads选项的配置。如图A-1所示:

 

如果说Minor GC执行的太频繁,那么必然是-Xmn分配得过小,反之Minor GC很久才执行一次,而每次执行的周期较长,则意味着-Xmn分配得过大。那么究竟应该如何对新生代进行调优呢?简单来说,我们需要多次迭代,从最初将-Xmn的值设置到最低,然后逐步微调,慢慢的你会发现Minor GC的执行频率在降低,直到最终满足需求即可停止。经过这样的调试,你会发现程序的吞吐量上来了,但是每次执行Minor GC的周期会变得较长,怎么办呢?我们可以通过-XX:ParallelGCThreads选项调整GC执行的线程数,让更多的GC线程执行垃圾收集,提升GC的回收效率。这样一来,基本可以满足降低GC的回收平率,提升GC的回收效率

 

由于使用的是并行GC,我们可以充分利用多核CPU资源以及线程资源。同微调-Xmn选项一样,我们首先可以将-XX:ParallelGCThreads设置为物理CPU核心数的1/2,比如你的CPU是6核,那么-XX:ParallelGCThreads的值就可以设置为3(最好不要小于2,否则将会影响并行GC的回收效率),这样一来,CPU可用资源就会将一半分配给GC线程使用,而剩下的CPU资源则服务于应用线程中。当然如果你的项目并不重视高响应,-XX:ParallelGCThreads的值可以相对的进行减少,以便于有更多的CPU资源分配给程序中的工作线程。

 

四、老年代的性能调优

新生代的调优如果大家都已经掌握,接下来我们再来看老年代如何进行性能调优。尽管调优原则中笔者提及过,应该让垃圾收集动作尽可能的发生在新生代中,也就是尽可能多执行Minor GC,但是这并不代表程序永远不会执行Full GC,一旦程序触发Full GC时,所花费的时间往往要大于Minor GC的执行周期,如果Full GC执行的周期过长,对用户所带来的直观感受是非常不友好的,比如用户在执行登录操作,恰恰悲催的碰见JVM正在执行长时间的Full GC,请自行补白。。。

 

在GC的命令选项中并不存在直接设置来年代内存大小的选项,那么老年代的内存大小如何设置呢?简单来说,老年代的内存空间大小间接等于-Xmx的值减去-Xmn的值,比如-Xmx为120G,-Xmn的值为45G,那么剩下的75G就是老年代的内存空间。在此大家需要注意,如果当-Xmn产生变化时,-Xmx也要随之成比例的发生变化,否则老年代占用的内存空间将会增大或变小,如果增大,Full GC的执行周期将会变得更长,反之执行频率将会频繁。

 

一般来说,如果<=3G以下的堆内存,建议使用的GC组合是Parallel和Parallel Old,除非真的是需求无法容忍系统出现长时间的“Stop the World”(目前几乎没有任何一款GC不需要暂停工作线程,只是尽可能的缩短暂停时间,包括G1)情况下,才推荐上CMS,不过一般大内存的使用,老年代首推CMS执行垃圾收集,并且CMS也是除G1之外的HotSpot中唯一的一款可以单独执行老年代增量回收,而不必执行Full GC全量回收的垃圾收集器(Promotion Failed和Concurrent Mode Failed情况除外)。

 

之所以要用CMS,是因为CMS天生为低延迟/高响应而生。因为CMS的执行过程中,只有初始标记和再次标记会出现暂停,而其它过程CMS的工作线程将会和程序的工作线程同时工作,大大提升了GC的回收效率。那么使用CMS同样需要进行优化,其中最主要的就是调整-Xmx的大小和-XX:CMSInitiatingOccupancyFraction选项。如图A-2所示:

 

-XX:CMSInitiatingOccupancyFraction用于设置老年代中的内存使用率达到多少百分比的时候执行内存回收(低版本的JDK缺省值为68%,JDK6及以上版本缺省值则为92%),在JDK6以后续版本中,如果按照缺省配置,当老年代的内存使用率达到92%后才进行垃圾收集,这往往会导致从新生代晋升到老年代中的对象将无法进行存放,如果-XX:CMSInitiatingOccupancyFraction设置得太低又会导致CMS GC触发的频率太快。一般来说,在大内存的堆使用上,笔者将这个值设置在70-80之间算是比较合理的。

 

尽管CMS是大内存的首选,但是CMS仍然是有一些令人不满意的地方,比如抢占CPU资源、内存碎片等问题。不过总而言之,CMS目前在大内存的使用上,仍然是首选。

6
4
分享到:
评论
1 楼 sgq0085 2016-01-08  
  

相关推荐

    深入理解Java虚拟机(jvm性能调优+内存模型+虚拟机原理).zip

    《深入理解Java虚拟机》是一本深度探讨Java虚拟机(JVM)的著作,涵盖了JVM性能调优、内存模型以及虚拟机原理等多个关键领域。本文将基于这些主题,详细阐述其中的重要知识点。 首先,我们要了解Java虚拟机(JVM)...

    jvm性能调优+内存模型+虚拟机

    jvm性能调优+内存模型+虚拟机 jvm性能调优+内存模型+虚拟机 jvm性能调优+内存模型+虚拟机

    JAVA JVM性能调优监控工具详解

    ### JAVA JVM性能调优监控工具详解 在Java开发过程中,特别是在企业级应用中,经常会遇到各种性能瓶颈问题,如内存溢出(`OutOfMemoryError`)、内存泄露、线程死锁、锁争用等问题。这些问题如果不能及时有效地解决...

    java虚拟机(JVM)调优案例分析与MyEclipse性能调优实战

    java虚拟机(JVM)调优案例分析与MyEclipse性能调优实战

    个人总结之—JVM性能调优实战

    本文档是一篇关于JVM(Java虚拟机)性能调优的经典实战总结。在实际应用开发与维护过程中,JVM性能调优是一个非常重要的话题,它直接关系到应用程序运行效率、资源利用以及用户体验等多方面因素。本总结旨在分享作者...

    JVM系列之性能调优参考手册(实践篇).pdf

    标题《JVM系列之性能调优参考手册(实践篇)》涉及的知识点主要集中在Java虚拟机(JVM)性能调优的实践操作。JVM作为Java程序运行的基础环境,对程序性能有着决定性影响。本手册的目的是指导开发者如何对JVM进行性能...

    JVM性能调优总结.docx

    JVM性能调优是Java开发中非常重要的一方面,直接影响到系统的性能和稳定性。本文将总结JVM性能调优的经验和技巧,并提供一些实用的配置参数和建议。 一、堆大小设置 堆大小是JVM性能调优中的一个关键参数。堆大小...

    java虚拟机(JVM)调优案例分析与Eclipse性能调优实战

    java虚拟机(JVM)调优案例分析与Eclipse性能调优实战

    006-jvm性能调优

    JVM性能调优 JVM(Java Virtual Machine...JVM性能调优需要了解JVM基础知识、JVM参数调优、堆空间内存分配、垃圾收集等知识点,并结合实际应用中的需求和限制,选择合适的JVM参数配置和垃圾收集算法,以提高JVM性能。

    java稳定可靠的分布式-高并发性能调优

    465页,涵盖Java编程性能调优,Java多线程性能调优,JVM性能检测及调优,设计模式调优,数据库性能调优,实战演、还有其他豆瓣高评分的Java相关书籍:深入理解Java虚拟机、凤凰架构:构建可靠的大型分布式系统、谷歌...

    Java GC与性能调优

    Java GC与性能调优 Java GC与性能调优是 Java programming language 中非常重要的一部分,直接影响着 Java application 的性能。本文档将对 Java GC 与性能调优进行详细的介绍。 一、 Java 平台的逻辑结构 Java ...

    内容主要涉及分布式、并发、jvm调优相关-Poet.zip

    分布式、并发和JVM调优是Java开发中的关键领域,对于构建高性能、可扩展的系统至关重要。在这篇文章中,我们将深入探讨这些主题,并基于提供的压缩包"Poet.zip"进行详细解析。 首先,我们来看看分布式系统。分布式...

    深入理解Java虚拟机(jvm性能调优+内存模型+虚拟机原理).txt

    深入理解Java虚拟机详细视频教程,包括jvm性能调优、Java内存模型及虚拟机原理。有详细的文档资料,配合深入理解Java虚拟机书籍学习效果更佳

    JVM性能调优-JVM内存整理及GC回收.pdf

    了解Java对象引用类型、垃圾回收算法以及分代处理垃圾的概念是进行JVM性能调优的基础。这些知识点对于准备Java面试的开发者来说,是必须掌握的重要内容,同时也是深入理解JVM内存管理和性能优化的基础。

    JVM性能调优-JVM内存整理及GC回收.pdf_java_jvm_

    《JVM性能调优——JVM内存整理及GC回收》是针对Java开发人员的重要主题,尤其是在大型企业级应用中,确保JVM(Java虚拟机)的高效运行是至关重要的。本资料深入探讨了如何通过调整JVM内存设置和优化垃圾回收机制来...

    阿里巴巴Java性能调优实战(2021-2022华山版)+Java架构核心宝典+性能优化手册100技巧.rar

    阿里巴巴Java性能调优华山版是一套系统性能调优教程,!通过这份笔记的学习,你将会有一个系统的调优头脑和策略!快了何止100%?需要的朋友可下载试试! 众所周知性能调优可以使系统稳定,用户体验更佳,甚至在...

    JVM性能调优经典教程

    Java虚拟机(JVM)是Java程序运行的基础,它的性能直接影响到Java应用的效率和稳定性。JVM性能调优是一项关键技术,旨在优化JVM的内存管理、垃圾收集、类加载等方面,以提升程序运行速度、减少内存占用并避免系统...

    阿里巴巴Java性能调优实战(2021华山版)

    模块二主要讲解 Java 编程性能调优,包括基础的数据类型、容器在实际应用场景中的调优,以及现在互联网系统架构中比较重要的网络通信调优。 模块三:多线程性能调优 模块三主要讲解多线程性能调优,包括多线程编程...

    JVM性能调优

    ### JVM性能调优 #### JVM概念与发展历程 Java虚拟机(JVM)是Java Virtual Machine的简称,它是一种能够执行Java字节码的虚拟机。JVM的主要作用是在不同的平台上运行Java程序,使得Java程序具备跨平台的能力。...

    JVM性能调优分析过程

    JVM(Java Virtual Machine)性能调优是提升Java应用程序效率的关键环节,主要涉及内存管理和垃圾回收机制的优化。本文将详细解析JVM性能调优分析的过程,并提供常见参数选择配置的建议。 首先,进行JVM参数调优时...

Global site tag (gtag.js) - Google Analytics