【Java性能优化思路方向】

概述:java

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

它能以不一样的方式定义“optimallinux

performance(最佳性能)”,但基本要素是:Java程序在业务响应时间要求内执行计算任务的能力,程序在高容量下执行业务功能的能力,并具备可靠性高和延迟低的特色。有时,数字自己变得模式化:对于一些大型网站,优秀的页面响应时间应该在500ms如下。在适当的时候,Refcard包括目标数字。ios

一、背景介绍算法

一个系统的上线除了常规的功能性测试外,还须要通过严格的性能测试,知足预期的性能指标(常见的有响应时间,tps等),才容许上生产环境。广义的性能测试通常还包含负载测试(用于测试系统的容量:即系统在保证必定响应时间的状况下可以容许多少并发用户的访问),压力测试(用于测试系统的稳定性:即在保证必定压力的状况下,查看测试系统的稳定性),并发测试(即测试系统多并发能力:即模拟多用户访问同一应用的测试,用于发现并发问题,好比线程锁,资源竞争,数据库死锁等)等。sql

经过性能测试,能够帮助咱们尽快发现系统的瓶颈。若是发现未能知足预期的业务目标,则须要进行性能调优。性能调优的需求,有时候来自于原型的验证,有时候来自于生产上实际的问题,无论哪一类的性能调优,咱们通常按照性能监控,性能分析,性能优化这几个步骤进行。如下章节会对每一个步骤进行详细分析。数据库

二、性能监控编程

性能监控是性能调优的第一步,主要目的在于了解当前系统运行的状态,了解当前服务器资源使用状况,JVM的内存使用,线程使用等状况,以便于第一时间找到瓶颈点。性能优化

2.1 查看服务器配置服务器

为了更好评估服务器性能,首先应了解当前宿主服务器的配置状况。如下主要是针对linux服务器给出的常见的查看命令。

2.1.1 CPU配置

对于CPU,比较关心的是CPU的总逻辑核数,能够直接使用mpstat查看。

可使用cat /proc/cpuinfo查看CPU的型号:

2.1.2 内存配置

使用free命令进行查看,能够看到总的内存,以及使用的状况

2.1.3 磁盘配置

使用fdisk -l能够查看到全部的磁盘配置状况,使用df -TH能够看到当前磁盘的目录挂载状况

有时候,须要确认当前磁盘是否为SSD盘,判断cat

/sys/block/*/queue/rotational的返回值(其中*为你的硬盘设备名称,例如sda等等),若是返回1则表示磁盘可旋转,那么就是HDD了;反之,若是返回0,则表示磁盘不能够旋转,那么就有多是SSD了。以下图所示,sda是SSD盘。

2.1.4 网络配置

使用ifconfig命令,能够看到网卡的配置状况。有时候,须要查看当前网卡是千M仍是万M,能够经过ethtool查看speed能够判断。以下图所示,eth4是千M网卡。

2.2 服务器监控

为了能实时了解系统运行时,资源的占用状况,咱们就须要对服务器的系统资源进行监控,如下列出常见的命令以及经常使用的监控事项。

2.2.1 CPU监控

使用vmstat命令,vmstat 2表明每2秒统计一次

重点观察

Ø Procs中r值,它表明调度程序运行队列的长度,若是该值长时间大于CPU逻辑核数1倍以上,须要关注,超过3-4倍须要立刻采起行动

Ø System中in(中断),cs(上下文切换)若是两值较大,说明系统内核消耗CPU较多

Ø Cpu列中,若是us(用户态)占比长期大于50%时,就须要考虑优化算法。根据经验us+sy占比参考值为80%

可使用pidstat -w -I -p pid 2,监控应用的锁竞争状况

让步式上下文切换(cswch)时钟周期占用3% ~ 5%,说明Java应用面临锁竞争,抢占式上下文切换率(nvcswch)高,说明预备运行的线程数多于可用的虚拟处理器数。

2.2.2 内存监控

也可使用上述的vmstat查看内存页面交换,

重点观察free,si,so这几列,若是free变小,并且si,so在变化,说明存在内存不足,跟磁盘swap,有发生页面交换的状况,须要考虑加大内存。

2.2.3 网络监控

使用第三方软件iptraf,它提供了可视化的页面,经过它能够实时监控网络流量状况。

2.2.4 磁盘

使用iostat进行监控

cpu属性值说明:

Ø 若是%iowait的值太高,表示硬盘存在I/O瓶颈,%idle值高,表示CPU较空闲,若是%idle值高但系统响应慢时,有多是CPU等待分配内存,此时应加大内存容量。%idle值若是持续低于10,那么系统的CPU处理能力相对较低,代表系统中最须要解决的资源是CPU。

disk属性值说明:

Ø 若是%util接近100%,说明产生的I/O请求太多,I/O系统已经满负荷,该磁盘可能存在瓶颈。若是svctm比较接近await,说明I/O几乎没有等待时间;若是await远大于svctm,说明I/O队列太长,io响应太慢,则须要进行必要优化。若是avgqu-sz比较大,也表示有当量io在等待。

2.3 JVM监控

使用jdk中自带的jvisualvm工具,在要链接的远程java进程,启动时增长jmx的配置,以下:

这样jvisualvm就能够经过ip+1111端口侦听远程JVM的状况了。

2.4 链接池监控

2.4.1 查看数据库链接池数量

使用netstat –an | grep ‘db ip’ | wc –l命令,能够看到与数据库建立的链接池,看这个值跟设置的数据库链接池的最小值,以及最大值的关系。若是始终经过最大值,须要考虑调整链接的最大值。

2.4.2 查看工做线程数

方法1:使用jvisualvm工具远程监控来查看

方法2:使用命令查看

2.5 Oracle监控

2.5.1 查看oracle配置

Ø 使用oracle的用户登陆oracle的服务器(su - oracle)

Ø 启动sqlplus命令行模式(sqlplus / as sysdba)

Ø 查看配置(Show parameter sga;)

2.5.2 性能监控

Ø 使用sqlplus命令行模式

Ø 开始时启动快照命令,中止时再执行一遍快速命令

备注:快照命令(exec DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT();)

Ø 快照执行完后,取报告(@?/rdbms/admin/awrrpt)

Ø 分析报告(重点关注top 5 time events)

3 性能分析

3.1 JVM分析


3.1.1 堆分析

为了避免影响线上的性能,可使用堆转储,命令以下:

jmap -dump:live,format=b,file=heap_dump.hprofpid

而后能够将生成的.hprof文件导入mat,或者jvisualvm进行分析,能够了解哪些对象正在消耗内存。同时对于识别由建立太多某一特定对象所引起的内存问题,软件提供的直方图方法快速且方便。

3.1.2 垃圾回收分析

Jvm启动时,能够设置-Xloggc,-XX:PrintGCDetails等参数,开启gc日志收集。也可使用jstat进行监控分析,好比jstat–gcutil pid 2用于每隔2秒打印当前Java堆及GC状况。

3.1.3 线程分析

使用jdk自带的JMC和jstack工具,能够查看堵塞的线程。JMC内部集成的JFR能够很方便的检索出引起线程堵塞的事件。而jstack在必定程度上能够检查线程是堵塞在什么资源上。如下给出jstack定位思路:

4 性能优化

在深度优化系统前,应该先弄清为什么CPU的使用率低。优化代码的目的是提高而不是下降更短期内的CPU使用率。

4.1 JVM启动参数优化

4.1.1 原生内存的优化

对原生内存的优化,包含使用压缩的OOP(jvm启动参数上增长-XX:+UseCompressedOops)以及调整大内存分页(同时修改linux配置以及jvm启动参数-XX:LargePageSizeInBytes)等,均可以提高性能。

4.1.2 垃圾回收机制的优化

Ø 合理设置堆的大小,以及合理设置好代空间的划分:设置过小容易频繁GC,而设置太大,GC时停顿时间太长。同时为了不可能使用到虚拟内存,内存页交换致使更慢,至少保留1G的物理内存。

Ø 如何选择各分区大小应该依赖应用程序中对象生命周期的分布状况:若是应用存在大量的短时间对象,应该选择较大的年轻代;若是存在相对较多的持久对象,老年代应该适当增大。

Ø 稳定与震荡的堆大小:将-Xms和-Xmx的大小一致,对垃圾回收有利。

4.1.3 大对象分配优化

Ø 大对象尽可能分配在TLAB,若是大量发生在TLAB外,须要考虑调整TLAB参数,或者减小分配对象的大小。能够经过-XX:PrintTLAB标志查看结果。

Ø 大对象划入老年代:将大对象直接分配到老年代,保持新生代对象的结构的完整性,以提升GC效率,以经过-XX:PretenureSizeThreshold设置进入老年代的阀值。

4.2 java编程优化

由于实际的编程中,涉及性能优化的点比较多,如下只是列举一些常见的优化项供参考。

4.2.1 线程池优化

Ø 根据当前服务器CPU的数量合理设置最大线程数,最小线程数,线程池任务队列大小。CPU密集型任务配置尽量小的线程,如配置Ncpu+1个线程的线程池。IO密集型任务则因为线程并非一直在执行任务,则配置尽量多的线程,如2*Ncpu。

Ø 建议使用有界队列,有界队列能增长系统的稳定性和预警能力。

Ø 优先级不一样的任务可使用优先级队列PriorityBlockingQueue来处理。

Ø 执行时间不一样的任务能够交给不一样规模的线程池来处理,或者也可使用优先级队列,让执行时间短的任务先执行。设置线程的优先级。

4.2.2 其它编程细节

Ø 尽可能减小内存的使用,减小对象的大小,设置类型时考虑最小原则,去掉不用的属性,以及没有用到的实例变量。

Ø 经过使用对象池以及线程局部变量的方式来增强对象的重复。对象重用跟GC是有点矛盾,因此主要考虑对象初始化时成本比较高的状况(即初始化时间比较长)。

Ø 对于保存实际不须要在多个线程间共享的同步对象,同时又在不一样的实际中进行传递的对象,能够考虑使用线程局部变量,减小同步竞争。

Ø 在一些场景下,优化使用java8并行流的模式

4.3 数据库优化

4.3.1 使用预编译

使用preparedStatement方式,重用预处理语句池能够极大提高性能,同时也要避免出现大量大型对象池化而引发的GC方面的问题。

4.3.2 使用链接池

引入hikari链接池,在启动时就配置好

Ø 建立链接的代价很大,经过JDBC链接池获取链接可省去建立链接时间。同时须要合理设置链接池的大小。

Ø 合理设值:好比设置检索时的批量值,设置最优的预取值,设置ResultSet的批量值,能够提升检索的性能。

Ø 事务的优化:事务的提交以及事务相关的锁机制都会影响系统的性能,须要考虑合理设置事务隔离的级别,以及批量提交的策略等。

5 性能实战经验汇总

5.1 清算并发性能上不去

5.1.1 问题的现象

使用java8的并行流计算时,发现并发的性能上不去,而且性能会随着时间推移,不断的降低

5.1.2 优化点

Ø 引入hikari链接池,将单笔延时降到5ms

Ø 关闭日志

Ø 将sql改为预编译的模式

Ø Oracle服务器的把oracle的内存提升

5.2 Hsiar跟中台的链接上,存在不少FIN_WAIT2链接

5.3 Server_name是否能够随便配置

6 小结

6.1 性能工具箱

6.1.1 压测工具jmeter

Jmter是开源的压测工具,也易于上手。它的使用就不介绍了,这里主要讲一些注意的事项:

Ø 它的实时绘图依赖于服务器端的响应,若是压测机与服务器时间不一样步的话,会出现展现图断层现象,为了获得更准确的性能曲线,建议使用命令行的方式。

Ø 有时候发现压测性能上不去,有可能的缘由在于客户端。主要考虑的因素:客户端的CPU不足以支持所需数量的客户端线程,或者客户端须要花大量时间处理响应后才能发送请求。

6.1.2 JVM相关

除了上文提到的jdk自带的工具外,还有IBM提供的MemoryAnalyzer,以及商用软件jprofile都很强大。

6.1.3 数据库相关

对于数据库的性能监控,可使用Spotlight,它有基于mysql以及oracle的不一样版本支持。

6.1.4 网络相关

经常使用Linux自带的Tcpdump命令导出抓包,而后使用wireshark进行分析,很强大。

相关文章
相关标签/搜索