初探调优2:Java应用调优思路与工具

简介: 对于调优这个事情来说,一般就是三个过程: 性能监控:问题没有发生,你并不知道你需要调优什么?此时需要一些系统、应用的监控工具来发现问题。

对于调优这个事情来说,一般就是三个过程:

  • 性能监控:问题没有发生,你并不知道你需要调优什么?此时需要一些系统、应用的监控工具来发现问题。
  • 性能分析:问题已经发生,但是你并不知道问题到底出在哪里。此时就需要使用工具、经验对系统、应用进行瓶颈分析,以求定位到问题原因。
  • 性能调优:经过上一步的分析定位到了问题所在,需要对问题进行解决,使用代码、配置等手段进行优化。

Java调优也不外乎这三步。

此外,本文所讲的性能分析、调优等是抛开以下因素的:

  • 系统底层环境:硬件、操作系统等
  • 数据结构和算法的使用
  • 外部系统如数据库、缓存的使用
  • Java中一些Api的使用,如Random、StringBuilder等。

调优准备

调优是需要做好准备工作的,毕竟每一个应用的业务目标都不尽相同,性能瓶颈也不会总在同一个点上。在业务应用层面,我们需要:

  • 需要了解系统的总体架构,明确压力方向。比如系统的哪一个接口、模块是使用率最高的,面临高并发的挑战。
  • 需要构建测试环境来测试应用的性能,使用ab、loadrunner、jmeter都可以。
  • 对关键业务数据量进行分析,这里主要指的是对一些数据的量化分析,如数据库一天的数据量有多少;缓存的数据量有多大等
  • 了解系统的响应速度、吞吐量、TPS、QPS等指标需求,比如秒杀系统的相应速度和QPS是要求非常高的。
  • 了解系统相关软件的版本、模式和参数等,有时候限于应用依赖服务的版本、模式等,性能也会受到一定的影响。

此外,我们还需要了解Java相关的一些知识:

性能分析

在系统层面能够影响应用性能的一般包括三个因素:CPU、内存和IO,可以从这三方面进行程序的性能瓶颈分析。

CPU分析

当程序响应变慢的时候,首先使用top、vmstat、ps等命令查看系统的cpu使用率是否有异常,从而可以判断出是否是cpu繁忙造成的性能问题。其中,主要通过us(用户进程所占的%)这个数据来看异常的进程信息。当us接近100%甚至更高时,可以确定是cpu繁忙造成的响应缓慢。一般说来,cpu繁忙的原因有以下几个:

  • 线程中有无限空循环、无阻塞、正则匹配或者单纯的计算
  • 发生了频繁的gc
  • 多线程的上下文切换

确定好cpu使用率最高的进程之后就可以使用jstack来打印出异常进程的堆栈信息:

jstack [pid]

jstack

接下来需要注意的一点是,Linux下所有线程最终还是以轻量级进程的形式存在系统中的,而使用jstack只能打印出进程的信息,这些信息里面包含了此进程下面所有线程(轻量级进程-LWP)的堆栈信息。因此,进一步的需要确定是哪一个线程耗费了大量cpu,此时可以使用top -p [processId]来查看,也可以直接通过ps -Le来显示所有进程,包括LWP的资源耗费信息。最后,通过在jstack的输出文件中查找对应的lwp的id即可以定位到相应的堆栈信息。其中需要注意的是线程的状态:RUNNABLE、WAITING等。对于Runnable的进程需要注意是否有耗费cpu的计算。对于Waiting的线程一般是锁的等待操作。

也可以使用jstat来查看对应进程的gc信息,以判断是否是gc造成了cpu繁忙。

jstat -gcutil [pid]

jstat

还可以通过vmstat,通过观察内核状态的上下文切换(cs)次数,来判断是否是上下文切换造成的cpu繁忙。

vmstat 1 5

jstat

内存分析

对Java应用来说,内存主要是由堆外内存和堆内内存组成。

  1. 堆外内存

    堆外内存主要是JNI、Deflater/Inflater、DirectByteBuffer(nio中会用到)使用的。对于这种堆外内存的分析,还是需要先通过vmstat、sar、top、pidstat等查看swap和物理内存的消耗状况再做判断的。此外,对于JNI、Deflater这种调用可以通过Google-preftools来追踪资源使用状况。

  2. 堆内内存

    此部分内存为Java应用主要的内存区域。通常与这部分内存性能相关的有:

    • 创建的对象:这个是存储在堆中的,需要控制好对象的数量和大小,尤其是大的对象很容易进入老年代
    • 全局集合:全局集合通常是生命周期比较长的,因此需要特别注意全局集合的使用
    • 缓存:缓存选用的数据结构不同,会很大程序影响内存的大小和gc
    • ClassLoader:主要是动态加载类容易造成永久代内存不足
    • 多线程:线程分配会占用本地内存,过多的线程也会造成内存不足

    以上使用不当很容易造成:

    • 频繁GC -> Stop the world,使你的应用响应变慢
    • OOM,直接造成内存溢出错误使得程序退出。OOM又可以分为以下几种:
      • Heap space:堆内存不足
      • PermGen space:永久代内存不足
      • Native thread:本地线程没有足够内存可分配

    排查堆内存问题的常用工具是jmap,是jdk自带的。一些常用用法如下:

    • 查看jvm内存使用状况:jmap -heap
    • 查看jvm内存存活的对象:jmap -histo:live
    • 把heap里所有对象都dump下来,无论对象是死是活:jmap -dump:format=b,file=xxx.hprof
    • 先做一次full GC,再dump,只包含仍然存活的对象信息:jmap -dump:format=b,live,file=xxx.hprof

    此外,不管是使用jmap还是在OOM时产生的dump文件,可以使用Eclipse的MAT(MEMORY ANALYZER TOOL)来分析,可以看到具体的堆栈和内存中对象的信息。当然jdk自带的jhat也能够查看dump文件,并启动web端口供浏览器浏览。

IO分析

通常与应用性能相关的包括:文件IO和网络IO。

  1. 文件IO

    可以使用系统工具pidstat、iostat、vmstat来查看io的状况。这里可以看一张使用vmstat的结果图。

    这里主要注意bi和bo这两个值,分别表示块设备每秒接收的块数量和块设备每秒发送的块数量,由此可以判定io繁忙状况。进一步的可以通过使用strace工具定位对文件io的系统调用。通常,造成文件io性能差的原因不外乎:

  2. 网络IO

    查看网络io状况,一般使用的是netstat工具。可以查看所有连接的状况、数目、端口信息等。例如:当time_wait或者close_wait连接过多时,会影响应用的相应速度。

     netstat -anp
    

    此外,还可以使用tcpdump来具体分析网络io的数据。当然,tcpdump出的文件直接打开是一堆二进制的数据,可以使用wireshark阅读具体的连接以及其中数据的内容。

     tcpdump -i eth0 -w tmp.cap -tnn dst port 8080 #监听8080端口的网络请求并打印日志到tmp.cap中
    

    还可以通过查看/proc/interrupts来获取当前系统使用的中断的情况。

    各个列依次是:

     irq的序号, 在各自cpu上发生中断的次数,可编程中断控制器,设备名称(request_irq的dev_name字段)
    

    通过查看网卡设备的终端情况可以判断网络io的状况。

其他分析工具

上面分别针对CPU、内存以及IO讲了一些系统/JDK自带的分析工具。除此之外,还有一些综合分析工具或者框架可以更加方便我们对Java应用性能的排查、分析、定位等。

  • VisualVM

    这个工具应该是Java开发者们非常熟悉的一款java应用监测工具,原理是通过jmx接口来连接jvm进程,从而能够看到jvm上的线程、内存、类等信息。如果想进一步查看gc情况,可以安装visual gc插件。此外,visualvm也有btrace的插件,可以可视化直观的编写btrace代码并查看输出日志。 与VisualVm类似的,jconsole也是通过jmx查看远程jvm信息的一款工具,更进一步的,通过它还可以显示具体的线程堆栈信息以及内存中各个年代的占用情况,也支持直接远程执行MBEAN。当然,visualvm通过安装jconsole插件也可以拥有这些功能。但由于这俩工具都是需要ui界面的,因此一般都是通过本地远程连接服务器jvm进程。服务器环境下,一般并不用此种方式。

  • Java Mission Control(jmc)

    此工具是jdk7 u40开始自带的,原来是JRockit上的工具,是一款采样型的集诊断、分析和监控与一体的非常强大的工具。docs.oracle.com/javacompone…

  • Btrace 这里不得不提的是btrace这个神器,它使用java attach api+ java agent + instrument api能够实现jvm的动态追踪。在不重启应用的情况下可以加入拦截类的方法以打印日志等。具体的用法可以参考Btrace入门到熟练小工完全指南

  • Jwebap

    Jwebap是一款JavaEE性能检测框架,基于asm增强字节码实现。支持:http请求、jdbc连接、method的调用轨迹跟踪以及次数、耗时的统计。由此可以获取最耗时的请求、方法,并可以查看jdbc连接的次数、是否关闭等。但此项目是2006年的一个项目,已经将近10年没有更新。根据笔者使用,已经不支持jdk7编译的应用。如果要使用,建议基于原项目二次开发,同时也可以加入对redis连接的轨迹跟踪。当然,基于字节码增强的原理,也可以实现自己的JavaEE性能监测框架。

    上图来自笔者公司二次开发过的jwebap,已经支持jdk8和redis连接追踪。

  • useful-scripts

    这里有一个本人参与的开源的项目:github.com/superhj1987…,封装了很多常用的性能分析命令,比如上文讲的打印繁忙java线程堆栈信息,只需要执行一个脚本即可。

微信公众号【Java技术江湖】一位阿里 Java 工程师的技术小站。(关注公众号后回复”Java“即可领取 Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南、Java程序员面试指南等干货资源)

wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

相关文章
|
11天前
|
监控 算法 Java
Java GC调优详解
Java GC调优详解
27 0
|
16天前
|
移动开发 Java Android开发
构建高效Android应用:探究Kotlin与Java的性能差异
【4月更文挑战第3天】在移动开发领域,性能优化一直是开发者关注的焦点。随着Kotlin的兴起,其在Android开发中的地位逐渐上升,但关于其与Java在性能方面的对比,尚无明确共识。本文通过深入分析并结合实际测试数据,探讨了Kotlin与Java在Android平台上的性能表现,揭示了在不同场景下两者的差异及其对应用性能的潜在影响,为开发者在选择编程语言时提供参考依据。
|
16天前
|
Java
深入理解Java并发编程:线程池的应用与优化
【4月更文挑战第3天】 在Java并发编程中,线程池是一种重要的资源管理工具,它能有效地控制和管理线程的数量,提高系统性能。本文将深入探讨Java线程池的工作原理、应用场景以及优化策略,帮助读者更好地理解和应用线程池。
|
1天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
1天前
|
设计模式 算法 Java
Java中的设计模式及其应用
【4月更文挑战第18天】本文介绍了Java设计模式的重要性及分类,包括创建型、结构型和行为型模式。创建型模式如单例、工厂方法用于对象创建;结构型模式如适配器、组合关注对象组合;行为型模式如策略、观察者关注对象交互。文中还举例说明了单例模式在配置管理器中的应用,工厂方法在图形编辑器中的使用,以及策略模式在电商折扣计算中的实践。设计模式能提升代码可读性、可维护性和可扩展性,是Java开发者的必备知识。
|
1天前
|
安全 Java API
函数式编程在Java中的应用
【4月更文挑战第18天】本文介绍了函数式编程的核心概念,包括不可变性、纯函数、高阶函数和函数组合,并展示了Java 8如何通过Lambda表达式、Stream API、Optional类和函数式接口支持函数式编程。通过实际应用案例,阐述了函数式编程在集合处理、并发编程和错误处理中的应用。结论指出,函数式编程能提升Java代码的质量和可维护性,随着Java语言的演进,函数式特性将更加丰富。
|
3天前
|
Java API 数据库
深入解析:使用JPA进行Java对象关系映射的实践与应用
【4月更文挑战第17天】Java Persistence API (JPA) 是Java EE中的ORM规范,简化数据库操作,让开发者以面向对象方式处理数据,提高效率和代码可读性。它定义了Java对象与数据库表的映射,通过@Entity等注解标记实体类,如User类映射到users表。JPA提供持久化上下文和EntityManager,管理对象生命周期,支持Criteria API和JPQL进行数据库查询。同时,JPA包含事务管理功能,保证数据一致性。使用JPA能降低开发复杂性,但需根据项目需求灵活应用,结合框架如Spring Data JPA,进一步提升开发便捷性。
|
7天前
|
Java
探秘jstack:解决Java应用线程问题的利器
探秘jstack:解决Java应用线程问题的利器
15 1
探秘jstack:解决Java应用线程问题的利器
|
11天前
|
XML JSON JavaScript
Java中XML和JSON的比较与应用指南
本文对比了Java中XML和JSON的使用,XML以自我描述性和可扩展性著称,适合结构复杂、需验证的场景,但语法冗长。JSON结构简洁,适用于轻量级数据交换,但不支持命名空间。在Java中,处理XML可使用DOM、SAX解析器或XPath,而JSON可借助GSON、Jackson库。根据需求选择合适格式,注意安全、性能和可读性。
23 0
|
16天前
|
存储 安全 Java
Spring Security应用讲解(Java案列演示)
Spring Security应用讲解(Java案列演示)