如何合理的规划一次 JVM 性能调优

转载自   如何合理的规划一次 JVM 性能调优

摘要: JVM性能调优涉及到方方面面的取舍,往往是牵一发而动全身,需要全盘考虑各方面的影响,那么如何进行一次优雅的调优,提升应用的性能?


这是 JVM 优化系列第三篇:


  • JVM 调优系列之监控工具

  • JVM 调优系列之图解垃圾回收


JVM性能调优涉及到方方面面的取舍,往往是牵一发而动全身,需要全盘考虑各方面的影响。但也有一些基础的理论和原则,理解这些理论并遵循这些原则会让你的性能调优任务将会更加轻松。为了更好的理解本篇所介绍的内容。你需要已经了解和遵循以下内容:


1、已了解jvm 垃圾收集器

2、已了解jvm 性能监控常用工具

3、能够读懂gc日志

4、确信不为了调优而调优,jvm调优不能解决一切性能问题

 

这些内容在之前的两篇文章已经介绍过了,如果有不了解的可以去点击上述连接进行回顾,如果对这些不了解不建议读本篇文章。


本篇文章基于jvm性能调优,结合jvm的各项参数对应用程序调优,主要内容有以下几个方面:


1、jvm调优的一般流程

2、jvm调优所要关注的几个性能指标

3、jvm调优需要掌握的一些原则

4、调优策略&示例


性能调优的层次


为了提升系统性能,我们需要对系统的各个角度和层次来进行优化,以下是需要优化的几个层次。



从上面我们可以看到,除了jvm调优以外,还有其他几个层面需要来处理,所以针对系统的调优不是只有jvm调优一项,而是需要针对系统来整体调优,才能提升系统的性能。本篇只针对jvm调优来讲解,其他几个方面,后续再介绍。


在进行jvm调优之前,我们假设项目的架构调优和代码调优已经进行过或者是针对当前项目是最优的。这两个是jvm调优的基础,并且架构调优是对系统影响最大的 ,我们不能指望一个系统架构有缺陷或者代码层次优化没有穷尽的应用,通过jvm调优令其达到一个质的飞跃,这是不可能的。


另外,在调优之前,必须得有明确的性能优化目标, 然后找到其性能瓶颈。之后针对瓶颈的优化,还需要对应用进行压力和基准测试,通过各种监控和统计工具,确认调优后的应用是否已经达到相关目标。


JVM 调优流程


调优的最终目的都是为了令应用程序使用最小的硬件消耗来承载更大的吞吐。


jvm的调优也不例外,jvm调优主要是针对垃圾收集器的收集性能优化,令运行在虚拟机上的应用能够使用更少的内存以及延迟获取更大的吞吐量。当然这里的最少是最优的选择,而不是越少越好。


性能定义


要查找和评估器性能瓶颈,首先要知道性能定义,对于jvm调优来说,我们需要知道以下三个定义属性,依作为评估基础:


吞吐量:重要指标之一,是指不考虑垃圾收集引起的停顿时间或内存消耗,垃圾收集器能支撑应用达到的最高性能指标。

延迟:其度量标准是缩短由于垃圾啊收集引起的停顿时间或者完全消除因垃圾收集所引起的停顿,避免应用运行时发生抖动。

内存占用:垃圾收集器流畅运行所需要 的内存数量。


这三个属性中,其中一个任何一个属性性能的提高,几乎都是以另外一个或者两个属性性能的损失作代价,不可兼得,具体某一个属性或者两个属性的性能对应用来说比较重要,要基于应用的业务需求来确定。


性能调优原则


在调优过程中,我们应该谨记以下3个原则,以便帮助我们更轻松的完成垃圾收集的调优,从而达到应用程序的性能要求。


1. MinorGC回收原则: 每次minor GC 都要尽可能多的收集垃圾对象。以减少应用程序发生Full GC的频率。

2. GC内存最大化原则:处理吞吐量和延迟问题时候,垃圾处理器能使用的内存越大,垃圾收集的效果越好,应用程序也会越来越流畅。

3. GC调优3选2原则: 在性能属性里面,吞吐量、延迟、内存占用,我们只能选择其中两个进行调优,不可三者兼得。


性能调优流程



以上就是对应用程序进行jvm调优的基本流程,我们可以看到,jvm调优是根据性能测试结果不断优化配置而多次迭代的过程。


在达到每一个系统需求指标之前,之前的每个步骤都有可能经历多次迭代。有时候为了达到某一方面的指标,有可能需要对之前的参数进行多次调整,进而需要把之前的所有步骤重新测试一遍。


另外调优一般是从满足程序的内存使用需求开始的,之后是时间延迟的要求,最后才是吞吐量的要求,要基于这个步骤来不断优化,每一个步骤都是进行下一步的基础,不可逆行之。以下我们针对每个步骤进行详细的示例讲解。


在JVM的运行模式方面,我们直接选择server模式,这也是jdk1.6以后官方推荐的模式。


在垃圾收集器方面,我们直接采用了jdk1.6-1.8 中默认的parallel收集器(新生代采用parallelGC,老生代采用parallelOldGC)。


确定内存占用


在确定内存占用之前,我们需要知道两个知识点:


应用程序的运行阶段

jvm内存分配


运行阶段


应用程序的运行阶段,我可以划分为以下三个阶段:


1、初始化阶段 : jvm加载应用程序,初始化应用程序的主要模块和数据。

2、稳定阶段:应用在此时运行了大多数时间,经历过压力测试的之后,各项性能参数呈稳定状态。核心函数被执行,已经被jit编译预热过。

3、总结阶段:最后的总结阶段,进行一些基准测试,生成响应的策报告。这个阶段我们可以不关注。


确定内存占用以及活跃数据的大小,我们应该是在程序的稳定阶段来进行确定,而不是在项目起初阶段来进行确定,如何确定,我们先看以下jvm的内存分配。


JVM 内存分配&参数



jvm堆中主要的空间,就是以上新生代、老生代、永久代组成,整个堆大小=新生代大小 + 老生代大小 + 永久代大小。 具体的对象提升方式,这里不再过多介绍了,我们看下一些jvm命令参数,对堆大小的指定。如果不采用以下参数进行指定的话,虚拟机会自动选择合适的值,同时也会基于系统的开销自动调整。



在设置的时候,如果关注性能开销的话,应尽量把永久代的初始值与最大值设置为同一值,因为永久代的大小调整需要进行FullGC 才能实现。


计算活跃数据大小


计算活跃数据大小应该遵循以下流程:


 

如前所述,活跃数据应该是基于应用程序稳定阶段时,观察长期存活与对象在java堆中占用的空间大小。


计算活跃数据时应该确保以下条件发生:


1.测试时,启动参数采用jvm默认参数,不人为设置。

2.确保Full GC 发生时,应用程序正处于稳定阶段。


采用jvm默认参数启动,是为了观察应用程序在稳定阶段的所需要的内存使用。


如何才算稳定阶段?


一定得需要产生足够的压力,找到应用程序和生产环境高峰符合状态类似的负荷,在此之后达到峰值之后,保持一个稳定的状态,才算是一个稳定阶段。所以要达到稳定阶段,压力测试是必不可少的,具体如何如何对应用压力测试,本篇不过多说明,后期会有专门介绍的篇幅。

 

在确定了应用出于稳定阶段的时候,要注意观察应用的GC日志,特别是Full GC 日志。


GC日志指令: -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<filename>

GC日志是收集调优所需信息的最好途径,即便是在生产环境,也可以开启GC日志来定位问题,开启GC日志对性能的影响极小,却可以提供丰富数据。

 

必须得有FullGC 日志,如果没有的话,可以采用监控工具强制调用一次,或者采用以下命令,亦可以触发


jmap -histo:live pid

 

在稳定阶段触发了FullGC我们一般会拿到如下信息:



从以上gc日志中,我们大概可以分析到,在发生fullGC之时,整个应用的堆占用以及GC时间,当然了,为了更加精确,应该多收集几次,获取一个平均值。或者是采用耗时最长的一次FullGC来进行估算。

 

在上图中,fullGC之后,老年代空间占用在93168kb(约93MB),我们以此定为老年代空间的活跃数据。


其他堆空间的分配,基于以下规则来进行。


基于以上规则和上图中的FullGC信息,我们现在可以规划的该应用堆空间为:


java 堆空间: 373Mb (=老年代空间93168kb*4)

新生代空间:140Mb(=老年代空间93168kb*1.5)

永久代空间:5Mb(=永久代空间3135kb*1.5)

老年代空间: 233Mb=堆空间-新生代看空间=373Mb-140Mb


对应的应用启动参数应该为:



延迟调优


在确定了应用程序的活跃数据大小之后,我们需要再进行延迟性调优,因为对于此时堆内存大小,延迟性需求无法达到应用的需要,需要基于应用的情况来进行调试。


在这一步进行期间,我们可能会再次优化堆大小的配置,评估GC的持续时间和频率、以及是否需要切换到不同的垃圾收集器上。


系统延迟需求


在调优之前,我们需要知道系统的延迟需求是那些,以及对应的延迟可调优指标是那些。


应用程序可接受的平均停滞时间: 此时间与测量的Minor GC持续时间进行比较。

可接受的Minor GC频率:Minor GC的频率与可容忍的值进行比较。

可接受的最大停顿时间: 最大停顿时间与最差情况下FullGC的持续时间进行比较。

可接受的最大停顿发生的频率:基本就是FullGC的频率。


以上中,平均停滞时间和最大停顿时间,对用户体验最为重要,可以多关注。


基于以上的要求,我们需要统计以下数据:


MinorGC的持续时间;

统计MinorGC的次数;

FullGC的最差持续时间;

最差情况下,FullGC的频率;


优化新生代的大小



比如如上的gc日志中,我们可以看到Minor GC的平均持续时间=0.069秒,MinorGC 的频率为0.389秒一次。


如果,我们系统的设置的平均停滞时间为50ms,当前的69ms明显是太长了,就需要调整。

我们知道新生代空间越大,Minor GC的GC时间越长,频率越低。

如果想减少其持续时长,就需要减少其空间大小。

如果想减小其频率,就需要加大其空间大小。

 

为了降低改变新生代的大小对其他区域的最小影响。在改变新生代空间大小的时候,尽量保持老年代空间的大小。


比如此次减少了新生代空间10%的大小,应该保持老年代和持代的大小不变化,第一步调优后的参数如下变化:



优化老年代的大小


同上一步一样,在优化之前,也需要采集gc日志的数据。此次我们关注的是FullGC的持续时间和频率。


 

上图中,我们可以看到



如果没有FullGC的日志,有办法可以评估么?


我们可以通过对象提升率进行计算。


对象提升率


比如上述中启动参数中,我们的老年代大小=233Mb。


那么需要多久才能填满老年代中这233Mb的空闲空间取决于新生代到老年代的提升率。


每次提升老年代占用量=每次MinorGC 之后 java堆占用情况 减去 MinorGC后新生代的空间占用

对象提升率=平均值(每次提升老年代占用量) 除以 老年代空间


有了对象提升率,我们就可以算出填充满老年代空间需要多少次minorGC,大概一次fullGC的时间就可以计算出来了。

 

比如:



上图中:



老年代每次minorGC提升率



我们可以测算出:



FullGC的预期最差频率时长可以通过以上两种方式估算出来,可以调整老年代的大小来调整FullGC的频率,当然了,如果FullGC持续时间过长,无法达到应用程序的最差延迟要求,就需要切换垃圾处理器了。


具体如何切换,下篇再讲,比如切换为CMS,针对CMS的调优方式又有会细微的差别。


吞吐量调优


经过上述漫长 调优过程,最终来到了调优的最后一步,这一步对上述的结果进行吞吐量测试,并进行微调。


吞吐量调优主要是基于应用程序的吞吐量要求而来的,应用程序应该有一个综合的吞吐指标,这个指标基于真个应用的需求和测试而衍生出来的。当有应用程序的吞吐量达到或者超过预期的吞吐目标,整个调优过程就可以圆满结束了。


如果出现调优后依然无法达到应用程序的吞吐目标,需要重新回顾吞吐要求,评估当前吞吐量和目标差距是否巨大,如果在20%左右,可以修改参数,加大内存,再次从头调试,如果巨大就需要从整个应用层面来考虑,设计以及目标是否一致了,重新评估吞吐目标。


对于垃圾收集器来说,提升吞吐量的性能调优的目标就是就是尽可能避免或者很少发生FullGC 或者Stop-The-World压缩式垃圾收集(CMS),因为这两种方式都会造成应用程序吞吐降低。尽量在MinorGC 阶段回收更多的对象,避免对象提升过快到老年代。


最后


据Plumbr公司对特定垃圾收集器使用情况进行了一次调查研究,研究数据使用了84936个案例。在明确指定垃圾收集器的13%的案例中,并发收集器(CMS)使用次数最多;但大多数案例没有选择最佳垃圾收集器。这个比例占用在87%左右。


 

JVM调优是一个系统而又复杂的工作,目前jvm下的自动调整已经做的比较优秀,基本的一些初始参数都可以保证一般的应用跑的比较稳定了,对部分团队来说,程序性能可能优先级不高,默认垃圾收集器已经够用了。调优要基于自己的情况而来。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328146.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器复制不了文档,服务器复制粘贴不了

服务器复制粘贴不了 内容精选换一换帮助用户完成专属云服务器备份任务的创建&#xff0c;快速完成服务器数据保护。专属云服务器不支持应用一致性备份。当专属对象存储的容量不足时&#xff0c;会导致专属云服务器备份创建失败。已开通专属对象存储。登录管理控制台。单击&…

大神开发的模板框架 包括常见的功能

https://gitee.com/lcg0124/bootdo http://localhost/ 就可以访问了 http://localhost/blog

在Linux开发.NET——拜拜了Win10

我是一个有多年开发经验的.NET程序员。最近&#xff0c;我决定使用*NIX系统来开发.NET程序&#xff0c;虽然看起来有些激进&#xff0c;但是我可以说&#xff0c;这是我做过最好的决定了。 事情的起因是这样的&#xff0c;上周星期五&#xff08;十月14日&#xff09;&#xff…

20个非常有用的Java程序片段

转载自 20个非常有用的Java程序片段下面是20个非常有用的Java程序片段&#xff0c;希望能对你有用。 1. 字符串有整型的相互转换String a String.valueOf(2); //integer to numeric string int i Integer.parseInt(a); //numeric string to an int 2. 向文件末尾添加内…

从工程转向管理,访谈Github公司的Phil Haack

在本次播客中&#xff0c;QCon旧金山大会、 伦敦大会和纽约大会的主席Wes Reisz访谈了GitHub的工程主管Phil Haack&#xff0c;Haack在Github的工作聚焦于实现将软件推送到开发人员的桌面&#xff0c;当前正致力于发布GitHub Desktop这样的软件。GitHub Desktop是用于Visual St…

怎样往阿里云windows服务器传文件

怎样往阿里云windows服务器传文件 2017年12月05日 21:28:56 阿杜_ardo 阅读数 9169 1、在本地电脑上&#xff0c;快捷键“WINR"在“运行”中输入“MSTSC”&#xff0c;点击确定。 2、在“远程桌面连接”框框点击“选项”展开。&#xff08;计算机中输入阿里云服务器的IP地…

Hibernate基本概念

--用户解锁alter user scott account unlock;一个项目对应一个表空间oracle:--创建表空间--创建用户create user 用户名idendified by 密码default tablespace 表空间名-- 授权grant 权限(角色) to 用户--建表--增删改查--存储过程对象 ->jdbc->数据库&#xff08;表…

手机app 服务器 网页同步,手机app接入云服务器

手机app接入云服务器 内容精选换一换通过内网连接云手机实例时&#xff0c;需要在租户VPC中创建一台弹性云服务器&#xff0c;作为连接云手机的跳板机器。若创建云手机服务器时未使用自定义网络&#xff0c;还需在云手机租户的VPC和服务器所在VPC之间建立对等连接&#xff0c;如…

Java中的OutOfMemoryError

转载自 Java中的OutOfMemoryError引子&#xff1a;今天在<Sharding-JDBC官方群>里有个哥们称“不连sjdbc不会把内存吃光&#xff0c;连sjdbc跑一会就把内存吃光”&#xff0c;倍感诧异&#xff0c;我们已经用sj很久了&#xff0c;一直未发现sj吃内存的情况&#xff0c;遂…

沉沦17年,这位昔日科技霸主、最值钱企业,终于回来了……

1999年12月30日&#xff0c;这是微软最高光的时刻。当天其股价创出历史最高位59.97美元/股&#xff0c;市值达到了6130亿美元&#xff0c;是有史以来最值钱的公司——直至13年后&#xff0c;苹果公司的市值才超越这一纪录。 ▲微软股价 处在新千年之交&#xff0c;意气风发的微…

mysql5.5安装配置 在阿里云服务器上 本地navicat连接

MySQL 5.5安装配置教程. 2018年03月20日 19:18:49 沸点数据 阅读数 18046 1、官网下载MySQL 5.5。 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/5.5.html#downloads 2、安装MySQL5.5 注意&#xff1a;安装之前&#xff0c;请关闭杀毒软件。 &#xff08;1…

Hibernate基本概念 (2)

框架&#xff1a;1.一个应用程序的半成品2.提供了一系列可以重用的API3.能开发效率更高&#xff0c;性能更好的项目Hibernate:持久化&#xff08;jdbc&#xff09; ORM( 对象 关系 映射)hibernate环境搭建1.导包&#xff1a;核心包&#xff0c;必须包&#xff0c;数据驱动包2…

通过网页查看服务器算法,java分析html算法(java网页蜘蛛算法示例)

遇到复杂而繁琐的html页面大家都望而却步。因为很难获取到相应的数据。最古老的办法的是尝试用正则表达式&#xff0c;估计那么繁琐的东西得不偿失&#xff0c;浪费我们宝贵的时间。第二个办法用开源组织htmlparser的包&#xff0c;这个是一个比较老的项目&#xff0c;但是效果…

JVM 调优系列之监控工具

转载自 JVM 调优系列之监控工具 摘要: 项目部署线上之后&#xff0c;我们该如何基于监控工具来快速定位问题.... 通过上一篇的jvm垃圾回收知识&#xff0c;我们了解了jvm对内存分配以及垃圾回收是怎么来处理的。理论是指导实践的工具&#xff0c;有了理论指导&#xff0c;定位…

写给开发者:记录日志的10个建议

尽管在写这篇博文的时候&#xff0c;我是在负责运维工作&#xff0c;不过本文主要是写给开发者的。 对我来说&#xff0c;明白如何记录日志和记录什么&#xff0c;是软件工程师必须明了的最艰巨的任务之一。之所以这么说&#xff0c;是因为这项任务与预测&#xff08;divinatio…

解决org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)错误

解决org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)错误 2018年07月23日 16:55:42 犹抱琵琶半遮面 阅读数 29677 &#xff01;&#xff01;&#xff01;此类问题出现的原因是——不细心 一般的原因 Mapper interface和xml文件的定义对应…

Java 9 中的 GC 调优基础

转载自 Java 9 中的 GC 调优基础在经过了几次跳票之后&#xff0c;Java 9终于在原计划日期的整整一年之后发布了正式版。Java 9引入了很多新的特性&#xff0c;除了闪瞎眼的Module System和REPL&#xff0c;最重要的变化我认为是默认GC&#xff08;Garbage Collector&#xf…

mysql删除索引 增加索引

使用ALTER TABLE语句创建索引alter table table_name add index index_name (column_list) ; alter table table_name add unique (column_list) ; alter table table_name add primary key (column_list) ;删除索引drop index index_name on table_name ; alter table table_n…

Hibernate基本概念 (3)

一、hibernate配置&#xff1a;1.导jar包2.配置主文件a。数据库连接 url 驱动 用户名&#xff0c;密码&#xff0c;方言b。相关 显示sql&#xff0c;sql格式化c。映射文件3.映射文件表到实体 字段到属性查询&#xff1a;select dname from Dept d;参数&#xff1a;1.按位置&…

全自动迁移数据库的实现 (Fluent NHibernate, Entity Framework Core)

在开发涉及到数据库的程序时&#xff0c;常会遇到一开始设计的结构不能满足需求需要再添加新字段或新表的情况&#xff0c;这时就需要进行数据库迁移。实现数据库迁移有很多种办法&#xff0c;从手动管理各个版本的ddl脚本&#xff0c;到实现自己的migrator&#xff0c;或是使用…