查看原文
其他

Java 在 Linux 中性能调优最佳实践

ImportNew ImportNew 2020-09-14

(给ImportNew加星标,提高Java技能)

编译:ImportNew/覃佑桦

www.coderbuzz.com/2019/12/04/performance-tuning-java-applications-in-linux


本文能帮助你把Java应用程序性能发挥到极值。


在应用程序进行性能调优时,应当在优化代码的同时考虑代码运行的硬件。本文将介绍在Linux上把Java性能推到极致需要注意的方方面面。


线程争用


  1. 减少关键部分的代码量。

  2. 优先考虑使用同步代码块,其次才是同步方法。

  3. 优先考虑在同步代码块上加锁。

  4. 密切注意锁定资源的顺序,你可能会遇到死锁。

  5. 隔离低并发、中等并发和高并发用例,区别对待。

  6. 对低并发和中等并发尽可能使用CAS(Compare-And-Swap)操作。

  7. 请勿在同步代码块或已加锁情况下调用第三方Web服务或执行其他需要长时间运行的方法。

  8. 对比wait、notify和notifyAll机制,更推荐lock、CountDownLatch、CyclicBarrier这样的高级线程结构。

  9. 长时间运行的线程应允许中断。线程内部定期检查“interrupted”状态。

  10. 当读操作数量远大于写操作时,建议使用ReadWriteLock。它提高了读操作并发性。

  11. 仅在有多个线程访问数据结构的情况下才考虑使用并发数据结构。

  12. 不要在应用程序中到处修改状态。对可变状态加以限制并有意识地进行处理。这样可以减少锁的数量以及各种线程争用问题。

  13. 注意下面的自旋锁。它们会占用CPU资源。while(true) {//执行操作不休眠}

  14. 在这种无限while循环中,一定要合理地休眠。轮询线程就是一个这样的示例。


堆配置与垃圾回收


  1. 遵守用StringBuilder处理日志消息的标准实践。

  2. 分析应用程序,设置合理的堆与和垃圾收集参数。

  3. 不合理的GC设置可能会占用CPU资源,造成应用程序长时间暂停,由于内存不足导致应用程序崩溃,以及因并发模式故障带来的内存溢出或应用假死。

  4. 为了保证低延迟,应用程序需使用并发标记(Concurrent Mark)以及 CMS 或 G1 GC扫描算法。


避免磁盘交换


务必保证有足够的RAM提供给Java进程,Java进程发生磁盘交换是性能杀手。如果应用程序交换到磁盘,由于在GC期间必须从磁盘读取对象,垃圾回收周期会更长。


磁盘


  1. 使用异步logger记录日志。

  2. 在允许的情况下采用上下文日志。上下文日志是指将API调用的日志或事件存储到上下文对象中,并通过异步logger记录上下文数据。这将大大提高Java应用程序的性能,并且更易于排除故障。

  3. 无论进行何种持久性,优先考虑数据库而非本地磁盘。数据库针对磁盘读写进行了优化。


CPU


  1. 使用线程池。尽管线程本身是轻量级,但是创建线程仍然会带来很大的开销。

  2. 盲目地创建线程会导致上下文切换与平均负载激增,超过指定的阈值后会造成应用程序和计算机无响应。

  3. 除了CPU利用率,还要注意平均负载和上下文切换。它们在一起组成了性能相关的完整信息。


密切注意网络


  1. 查找网络上的丢包情况。

  2. 数据包丢失可能意味着应用程序太忙无法接收数据包,或者所处的网络发生拥塞。

  3. 如果有必要,请调整网络缓冲区大小。


使用缓存


  1. 缓存常用数据减少数据库访问。

  2. 针对每个用例分别进行缓存配置,包括eviction、expiry、size、consistency和concurrency。

  3. 将读密集型缓存与写密集型缓存分开,这样获得更好的锁定效果。

  4. 谨慎地使用分布式缓存。不恰当地使用分布式缓存会引入新问题,而不是解决现有问题。


通讯


  1. 对于近距离实时通信优先考虑异步通信。

  2. 设置发送API或RMI调用超时,这样某个长时间运行的操作不会破坏整个生态系统。


分析并记录更改


持续进行性能分析并记录你的更改。清晰地记录做出决定的原因。今天的重大决策可能到了明天就失去意义。这种情况发生时,工程师应该有足够的信息来还原或扩展应用程序。这就是应用程序的生存之道。没有人愿意坐在一颗定时炸弹上,并且不知道它什么时候会爆炸。


性能分析工具


  1. Jstack用来收集线程dump。

  2. Jmap用来收集堆dump和活动对象计数。

  3. Jvisualvm或Jconsole:用来分析堆、线程、收集线程dump,JDK默认提供。

  4. Eclipse内存分析器(MAT):用来分析堆dump。

  5. 线程Dump分析器(TDA):用来分析线程dump,检测长时间运行的线程与死锁


性能调优建议


在项目早期就进行性能调整计划和预算,但不要过早开始执行。这样可能会让核心业务逻辑变得混乱。我知道有一个应用程序过早进行优化,限制从数据库中获取的记录数,但是这一切并没有记录。


结果出现了各种各样的问题。由于这段代码位于底层,导致花了很多时间才得以解决。当然,如果在这方面你已经身经百战,可以忽略这条建议。


推荐阅读

(点击标题可跳转阅读)

OpenJDK 11 工具概览

Spring AMQP 错误处理策略详解

深入理解 Java 如何测量时间:从 API 到内核调用


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

好文章,我在看❤️

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存