https://zhuanlan.zhihu.com/p/573662668
性能分析
在系统层面能够影响应用性能的一般包括三个因素:CPU、内存和IO,可以从这三方面进行程序的性能瓶颈分析。
1. CPU分析
当程序响应变慢的时候,首先使用top、vmstat、ps等命令查看系统的cpu使用率是否有异常,从而可以判断出是否是cpu繁忙造成的性能问题。
cpu繁忙的原因有以下几个:
- 线程中有无限空循环、无阻塞、正则匹配或者单纯的计算
- 发生了频繁的gc
- 多线程的上下文切换
CPU调优
- 不要存在一直运行的线程(无限while循环),可以使用sleep休眠一段时间。这种情况普遍存在于一些pull方式消费数据的场景下,当一次pull没有拿到数据的时候建议sleep一下,再做下一次pull。
- 轮询的时候可以使用wait/notify机制
- 避免循环、正则表达式匹配、计算过多,包括使用String的format、split、replace方法(可以使用apache的commons-lang里的StringUtils对应的方法),使用正则去判断邮箱格式(有时候会造成死循环)、序列/反序列化等。
- 结合jvm和代码,避免产生频繁的gc,尤其是full GC。
使用多线程的时候,还需要注意以下几点:
- 使用线程池,减少线程数以及线程的切换
- 多线程对于锁的竞争可以考虑减小锁的粒度(使用ReetrantLock)、拆分锁(类似ConcurrentHashMap分bucket上锁), 或者使用CAS、ThreadLocal、不可变对象等无锁技术。此外,多线程代码的编写最好使用jdk提供的并发包、Executors框架以及ForkJoin等,此外Discuptor和Actor在合适的场景也可以使用。
2.内存分析
对Java应用来说,内存主要是由堆外内存和堆内内存组成。
- 创建的对象:这个是存储在堆中的,需要控制好对象的数量和大小,尤其是大的对象很容易进入老年代
- 全局集合:全局集合通常是生命周期比较长的,因此需要特别注意全局集合的使用
- 缓存:缓存选用的数据结构不同,会很大程序影响内存的大小和gc
- ClassLoader:主要是动态加载类容易造成永久代内存不足
- 多线程:线程分配会占用本地内存,过多的线程也会造成内存不足
排查堆内存问题的常用工具是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
内存调优
内存的调优主要就是对jvm的调优。
- 合理设置各个代的大小。避免新生代设置过小(不够用,经常minor gc并进入老年代)以及过大(会产生碎片),同样也要避免Survivor设置过大和过小。
- 选择合适的GC策略。需要根据不同的场景选择合适的gc策略。这里需要说的是,cms并非全能的。除非特别需要再设置,毕竟cms的新生代回收策略parnew并非最快的,且cms会产生碎片。此外,G1直到jdk8的出现也并没有得到广泛应用,并不建议使用。
- jvm启动参数配置-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:[log_path],以记录gc日志,便于排查问题。
代码上,也需要注意:
- 避免保存重复的String对象,同时也需要小心String.subString()与String.intern()的使用
- 尽量不要使用finalizer
- 释放不必要的引用:ThreadLocal使用完记得释放以防止内存泄漏,各种stream使用完也记得close。
- 使用对象池避免无节制创建对象,造成频繁gc。但不要随便使用对象池,除非像连接池、线程池这种初始化/创建资源消耗较大的场景,
- 缓存失效算法,可以考虑使用SoftReference、WeakReference保存缓存对象
- 谨慎热部署/加载的使用,尤其是动态加载类等
3.IO分析
通常与应用性能相关的包括:文件IO和网络IO。
- 文件IO可以使用系统工具pidstat、iostat、vmstat来查看io的状况。这里可以看一张使用vmstat的结果图。
- 网络IO查看网络io状况,一般使用的是netstat工具。可以查看所有连接的状况、数目、端口信息等。例如:当time_wait或者close_wait连接过多时,会影响应用的相应速度。
IO调优
文件IO上需要注意:
- 考虑使用异步写入代替同步写入,可以借鉴redis的aof机制。
- 利用缓存,减少随机读
- 尽量批量写入,减少io次数和寻址
- 使用数据库代替文件存储
网络IO上需要注意:
- 和文件IO类似,使用异步IO、多路复用IO/事件驱动IO代替同步阻塞IO
- 批量进行网络IO,减少IO次数
- 使用缓存,减少对网络数据的读取
- 使用协程: Quasar
标签:Java,调优,线程,内存,IO,使用,gc From: https://www.cnblogs.com/ningshare/p/16975980.html