首页 > 编程语言 >Java调优

Java调优

时间:2022-12-12 14:47:13浏览次数:81  
标签:Java 调优 线程 内存 IO 使用 gc

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

相关文章