深入理解JVM - 系统性能优化

系统性能优化并不是一上来就是JVM优化,相反JVM优化几乎是最后的手段了。影响一个系统的性能的因素非常多,如图:

image.png

从服务本身来看,影响服务性能的主要包扣:

  • 我们写代码时所选择的数据结构和算法
  • 服务开启的线程时是否合理
  • WEB应用,WEB服务
  • JVM方面的影响
  • 最后是操作系统的影响

从整个服务架构上来看还有:

  • 数据持久化
  • 服务间的远程调用
  • 消息缓存等中间件的选择

常用的性能测试指标

响应时间

一个请求从提交到响应所耗费的时间,一般比较关注平均响应时间,和最大响应时间。

常用组件的响应时间:

操作 响应时间
打开一个站点 几秒
数据库查询一条记录(有索引) 十几毫秒
机械磁盘一次寻址定位 4毫秒
从机械磁盘顺序读取1M数据 2毫秒
从SSD磁盘顺序读取1M数据 0.3毫秒
从远程分布式缓存Redis读取一个数据 0.5毫秒
从内存读取1M数据 十几微妙
Java程序本地方法调用 几微妙
网络传输2Kb数据 1微妙

从上述表格我们能看出:

  1. 数据持久化,使用SSD与使用机械硬盘相比性能可以提高将近10倍;
  2. 数据查询,数据如果直接在本地内存,那么它的读取效率比数据库快将近1000倍,比redis快30倍左右;如果数据在redis缓存,那么它的读取速度比数据库快30倍左右;这也是为什么使用缓存是提升系统性能的“银弹”的原因。

为监控而生的多级缓存框架 layering-cache这是我开源的一个多级缓存框架的实现,如果有兴趣可以看一下。

并发数

并发数是指同一时刻,对服务器有实际交互的请求数。一般并发数是在在线用户数的5%-15%之间,如在线用户数是1000,那么可以预估并发数在50-150之间。

吞吐量

吞吐量是单位时间内完成的工作量(请求)的数量。如:每分钟的数据库事务,每秒传送的文件千字节数,每分钟的 Web 服务器命中数。

关系

通常,平均响应时间越短,系统吞吐量越大;平均响应时间越长,系统吞吐量越小。但是,系统吞吐量越大, 未必平均响应时间越短。

性能优化原则

避免过早优化

不应该把大量的时间耗费在小的性能改进上,过早考虑优化是所有噩梦的根源。在开发初期我们的首要目标是完成功能,编写清晰,直接,易懂的代码。

进行系统性能测试

所有调优都应该建立在新能测试的基础上,不要满目靠猜测进行优化。

寻找系统瓶颈,分而治之,逐步优化

性能测试后,对整个请求经历的各个环节进行分析,排查出现性能瓶颈的地方,定位问题,分析影响性能的的主要因素是什么?内存、磁盘IO、网络、CPU,还是代码问题?架构设计不足?或者确实是系统资源不足?

常用的性能优化手段

高并发优化

提高系统并发能力的方式,方法论上主要有两种:垂直扩展(Scale Up)与水平扩展(Scale Out)。

垂直扩展

提升单机处理能力。垂直扩展的方式又有两种:

  1. 增强单机硬件性能,例如:增加CPU核数如32核,升级更好的网卡如万兆,升级更好的硬盘如SSD,扩充硬盘容量如2T,扩充系统内存如128G;
  2. 提升单机架构性能,例如:使用Cache来减少IO次数,使用异步来增加单服务吞吐量,使用无锁数据结构来减少响应时间;

水平扩展

只要增加服务器数量,就能线性扩充系统性能。水平扩展对系统架构设计是有要求的,如何在架构各层进行可水平扩展的设计,以及互联网公司架构各层常见的水平扩展实践,是本文重点讨论的内容。

互联网分层架构中,各层次水平扩展的实践又有所不同:

  1. 反向代理层可以通过“DNS轮询”的方式来进行水平扩展;
  2. 站点层可以通过nginx来进行水平扩展;
  3. 服务层可以通过服务连接池来进行水平扩展;
  4. 数据库可以按照数据范围,或者数据哈希的方式来进行水平扩展;

高可用优化

高可用的核心思想是:资源保护和冗余。
资源保护常用手段是服务限流和熔断降级;
冗余是是指资源备份,如数据库主从设计,redis的哨兵机制。

前端优化常用手段

浏览器/App

  1. 减少请求数
    合并CSS,Js,图片;
  2. 使用客户端缓存;
    静态资源文件缓存在浏览器中,如果文件发生了变化,则通过改变文件名来更新缓存。
  3. 启用压缩
    它可以减少网络传输量,但会给浏览器和服务器带来性能的压力,需要权衡使用。
  4. 资源文件加载顺序
    css放在页面最上面,js放在最下面。
  5. 减少Cookie传输
    cookie会包含在每次的请求和响应中,因此哪些数据写入cookie需要慎重考虑。

CDN加速

CDN,又称内容分发网络,本质仍然是一个缓存,而且是将数据缓存在用户最近的地方。免费的CDN加速器七牛云

反向代理缓存

将静态资源文件缓存在反向代理服务器上,一般是Nginx。

WEB组件分离

浏览器在同一个域名下下载资源存在并发限制,所以,将js,css和图片文件放在不同的域名下,可以提高浏览器在下载web组件的并发数。

应用服务性能优化

缓存

缓存的本质是将数据存放在访问速度较高的介质中,可以减少数据访问的时间,同时避免重复计算。

从上面响应时间表格我们可以看出,使用缓存将数据缓存在本地或者redis服务器,将会对查询效率有较大的提升。网站性能优化的第一定律也是使用缓存,为监控而生的多级缓存框架 layering-cache这是我开源的一个多级缓存框架的实现,如果有兴趣可以看一下。

集群

负载均衡服务器(nginx,f5等)使用负责均衡算法,将请求分发到多个节点上进行处理。

异步

同步和异步关注的是结果消息的通信机制:

  • 同步:同步的意思就是调用方需要主动等待结果的返回;
  • 异步:异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等;

阻塞和非阻塞主要关注的是等待结果返回调用方的状态:

  • 阻塞:是指结果返回之前,当前线程被挂起,不做任何事;
  • 非阻塞:是指结果在返回之前,线程可以做一些其他事,不会被挂起;

BIO、NIO和AIO

  • 同步阻塞:去商店买衣服,你去了之后发现衣服卖完了,商家说要去库房拿,那你就在店里面一直等,期间不做任何事(包括看手机),等着商家拿货,这就是同步阻塞,效率低;
  • 同步非阻塞:去商店买衣服,你去了之后发现衣服卖完了,商家说要去库房拿,这时你可以去继续逛街,但是时不时需要回去问商家货到了没,这就是同步非阻塞;
  • 异步阻塞:去商店买衣服,你去了之后发现衣服卖完了,这个时候时候你给商家留下电话,等货到了电话通知你,然后你就啥事都不敢,守着电话等通知,这个模式有点傻,用的很少;
  • 异步非阻塞:去商店买衣服,你去了之后发现衣服卖完了,这时候你给商家留下电话,然后就可以去逛街了,等货物到了商家会回电话通知你。

jdk里的BIO就属于同步阻塞;jdk里的NIO就属于同步非阻塞;jdk里的AIO就属于异步。

常见的异步组件

  • Servlet3
  • 多线程
  • 消息队列

代码级别

  1. 选择合适的数据结构,比如ArrayList和LinkedList的适用场景;
  2. 选择更优的算法,比如最大子序列和问题,选择穷举算法那么时间复杂度是O(n^3);如果选择动态规划算法,那么时间复杂度就是O(n)了;
  3. 编写更精简的代码,同样正确的程序,小程序比大程序要快;
  4. 并发编程,充分利用多核CPU资源;
  5. 同步情况下减少锁的竞争;
  6. 资源的复用,比如单例模式,池化技术;
  7. 序列化优化,比如redis的使用默认的JDK序列化和FastJson序列化,最后JDK序列化所暂用的空间是FastJson的3倍左右;

GC优化

GC优化的终极目的

  • GC的时间够小
  • GC的次数够少,发生Full GC的周期足够的长,时间合理,最好是不发生。

GC运行指标

如果满足则一般不需要调优:

  • Minor GC执行时间不到50ms;
  • Minor GC执行不频繁,约10秒一次;
  • Full GC执行时间不到1s;
  • Full GC执行频率不算频繁,不低于10分钟1次;

调优的原则

  1. 大多数的java应用不需要GC调优
  2. 大部分需要GC调优的的,不是参数问题,是代码问题
  3. 在实际使用中,分析GC情况优化代码比优化GC参数要多得多;
  4. GC调优是最后的手段

GC调优的最重要的三个选项

  1. 选择合适的GC回收器
  2. 选择合适的堆大小
  3. 选择年轻代在堆中的比重

GC调优的步骤

  1. 监控GC的状态
    使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化;

  2. 分析结果,判断是否需要优化
    如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化;如果GC时间超过1-3秒,或者频繁GC,则必须优化;

  3. 调整GC类型和内存分配
    如果内存分配过大或过小,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找1台或几台机器进行beta,然后比较优化过的机器和没有优化的机器的性能对比,并有针对性的做出最后选择;

  4. 不断的分析和调整
    通过不断的试验和试错,分析并找到最合适的参数

  5. 全面应用参数
    如果找到了最合适的参数,则将这些参数应用到所有服务器,并进行后续跟踪。

GC日志

以参数-Xms5m -Xmx5m -XX:+PrintGCDetails -XX:+UseSerialGC为例:

[DefNew: 1855K->1855K(1856K), 0.0000148 secs][Tenured: 2815K->4095K(4096K), 0.0134819 secs] 4671K
  • DefNew:指明了收集器类型,而且说明了收集发生在新生代。
  • 1855K->1855K(1856K):表示,回收前 新生代占用1855K,回收后占用1855K,新生代大小1856K。
  • 0.0000148 secs: 表明新生代回收耗时。
  • Tenured:表明收集发生在老年代
  • 2815K->4095K(4096K):回收前后的值
  • 0.0134819 secs:老年代回收耗时
  • 最后的4671K指明堆的大小。

收集器参数变为-XX:+UseParNewGC,日志变为:

[ParNew: 1856K->1856K(1856K), 0.0000107 secs][Tenured: 2890K->4095K(4096K), 0.0121148 secs]

收集器参数变为-XX:+ UseParallelGC或UseParallelOldGC,日志变为:

 [PSYoungGen: 1024K->1022K(1536K)] [ParOldGen: 3783K->3782K(4096K)] 4807K->4804K(5632K)

CMS收集器和G1收集器会有明显的相关字样

GC相关的参数

  • -verbose:gc-XX:+PrintGC:打印简单的GC日志
  • -XX:+PrintGCDetails+XX:+PrintGCTimeStamps:打印详细的GC日志
  • -Xlogger:[logpath]:指定GC日志路径,如Xlogger:log/gc.log
  • -XX:+PrintHeapAtGC:打印推信息,获取Heap在每次垃圾回收前后的使用状况
  • -XX:+TraceClassLoading: 在系统控制台信息中看到class加载的过程和具体的class信息,可用以分析类的加载顺序以及是否可进行精简操作。
  • -XX:+DisableExplicitGC:禁止在运行期显式地调用System.gc()
  • -XX:-HeapDumpOnOutOfMemoryError:默认关闭,建议开启,在java.lang.OutOfMemoryError 异常出现时,输出一个dump.core文件,记录当时的堆内存快照。
  • -XX:HeapDumpPath=./java_pid<pid>.hprof:默认是java进程启动位置,用来设置堆内存快照的存储文件路径。

存储性能优化

  • 尽量使用SSD;
  • 定时清理数据或者按数据的性质分开存放;
  • 结果集处理,如:用setFetchSize控制jdbc每次从数据库中返回多少数据;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • 1.影响一个系统性能的方方面面 一个web应用不是一个孤立的个体,它是一个系统的部分,系统中的每一部分都会影响整个...
    王侦阅读 1,068评论 0 1
  • 1.什么是系统优化 系统优化一个方面是系统化的对IT系统或交易链上的每个环节进行分析并优化,另一个是对单一系统进行...
    java高级分享阅读 559评论 0 3
  • JVM内存模型,垃圾回收算法介绍 根据Java虚拟机规范,JVM将内存划分为: -- 年轻代(New) :年轻代用...
    Kate_Blog阅读 797评论 0 2
  • 性能既是客观指标,诸如响应时间、吞吐量等技术指标;又是实际参与者的主观感受。 1 性能测试 性能测试是性能优化的前...
    deniro阅读 1,330评论 2 15
  • 最近工作中,老是遇到程序假死或者宕掉,最终原因都是full gc导致,刚好回过头再学习一下JVM内存模式,以及GC...
    VIPSHOP_FCS阅读 1,694评论 0 4