java深度调试【第二章通过堆栈分析性能瓶颈】

2025-12-12 0 386

前言

第二章的主题是通过堆栈分析性能瓶颈,但是介绍通过的堆栈分析的性能瓶颈的内容其实不多。更多的是介绍性能瓶颈相关的基本原理,其次是分享了一些造成性能瓶颈的不合理的设计或者是编码或者是配置,最后分享了定位性能瓶颈的相关手段和解决方案下面就给大家分享一下具体的内容吧

通过堆栈分析性能瓶颈

基本原理分析

这一章节在书中主要讲了:计算机的几大核心计算任务,各自主要消耗哪种硬件的性能;以及哪些操作会消耗 CPU 资源,哪些操作不需要 CPU 参与。
比如我们的IO操作,以及3D加速图形渲染这些操作几乎是不消耗我们CPU的性能的。

比如我们现在的服务器大多都是多核系统,所以在做CPU密集型任务的时候,可以采用多线程的形式的,提高CPU的使用率,从而提高系统的执行效率。

常见性能瓶颈问题

CPU 瓶颈

CPU瓶颈又分为两大类,一类是过度消耗CPU,一类是没有把CPU利用起来。
也可能因为其他的配置导致我们JVM GC频繁也会导致我们的CPU飙高。

CPU过度消耗或者说是CPU被滥用: 比如不恰当API的使用,低效算法的使用等消耗CPU的操作

String +的滥用,在拼接字符串的时候会产生临时对象这个期间不仅仅会消耗CPU也会占用内存。
低效算法的使用,比如高频的使用冒泡排序,或者for循环遍历,可以考虑是否使用Hash算法以空间换取时间的策略,减少CPU的消耗

双重 for 循环匹配(低效)

public List dataUnion(List listA, List listB) {
    List result = new ArrayList();
    // 外层循环遍历 listA(1000次)
    for (DataA a : listA) {
        // 内层循环遍历 listB(1000次)
        for (DataB b : listB) {
            // 每次循环都要比较 id(CPU 密集型操作)
            if (a.getId().equals(b.getId())) {
                result.add(new FullData(a, b));
                break; // 找到匹配后跳出内层循环
            }
        }
    }
    return result;
}

Hash 结构(HashMap)匹配(高效),减少CPU的使用率以空间换时间

public List dataUnion(List listA, List listB) {
    // 1. 将 listB 转为 HashMap(key=id,value=DataB),耗时 O(m)
    Map bMap = new HashMap();
    for (DataB b : listB) {
        bMap.put(b.getId(), b); // 哈希计算耗时极短,平均 O(1)
    }
    
    // 2. 遍历 listA,通过 id 从 HashMap 中直接获取匹配数据,耗时 O(n)
    List result = new ArrayList();
    for (DataA a : listA) {
        DataB b = bMap.get(a.getId()); // 直接通过哈希定位,无需循环比较
        if (b != null) {
            result.add(new FullData(a, b));
        }
    }
    return result;
}

CPU没有被利用起来: 比如锁的粒度过大,导致所有线程都等待IO操作 或者 在处理CPU密集型任务的时候,使用单线程处理。线程池数量设置得太低等操作。

▶ 反例(低效)CPU密集型任务:单线程处理效率:

List files = getFiles(); // 100 个文件
for (File file : files) {
    parseFile(file); // 单线程逐个解析,总耗时 100×10=1000 秒
}

▶ 优化(高效)使用多线程提高CPU的使用率:

ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
List files = getFiles();
files.forEach(file -> executor.submit(() -> parseFile(file))); // 并行解析
executor.shutdown();
executor.awaitTermination(1, TimeUnit.HOURS); // 总耗时约 100/8≈12.5 秒(8 核 CPU)

IO 瓶颈

要提高IO效率的,先不考虑硬件的性能(当然硬件的性能是基础了,磁盘IO速度本身就慢的话,代码效率才高也是微乎其微啦) 代码层面上提高IO效率的方式主要有以下几种减少 I/O 次数、优化 I/O 效率、避免 I/O 阻塞

减少IO次数的因为是我们程序员最应该注意的,比如和数据库交互,能批量处理的就批量处理,不仅仅能减少网络IO也能减少磁盘IO大大提高程序性能

避免循环读取 / 写入单行数据,改用缓冲区一次性批量处理。
▶ 反例(低效):

// 逐行写入,每次 write 都可能触发磁盘 I/O
try (FileWriter writer = new FileWriter(\"data.txt\")) {
    for (String line : lines) {
        writer.write(line + \"n\"); // 多次 I/O 操作
    }
}

▶ 优化(高效):

// 先拼接成批量数据(内存操作),再一次性写入
try (BufferedWriter writer = new BufferedWriter(new FileWriter(\"data.txt\"))) {
    StringBuilder sb = new StringBuilder();
    for (String line : lines) {
        sb.append(line).append(\"n\"); // 内存拼接,无 I/O
    }
    writer.write(sb.toString()); // 1 次 I/O 完成
}

JVM的异常

JVM的异常 比如Full gc 首先也是非常消耗CPU 的,涉及到大量的运算和对象复制回收。同时在这期间还会到 stop the word 问题,可能导致这期间堆积大量的请求 引发更多的系统问题。

当然JVM 还可能出现内存分配效率低、内存溢出(OOM) 这些问题会直接导致服务响应变慢、频繁卡顿甚至崩溃。

所以JVM的稳定运行至关重要:关于Full GC 以及OOM 的问题我都写过相关的文章,大家可以去看看定位过程和解决思路

  • 如何排查FullGC异常的
  • 上午系统崩溃(OOM)

如何判断系统是否存在性能瓶颈

这块在书中也是介绍了比较多内容,简单的理解就是 通过逐步增加系统压力,CPU使用率呈现(半)抛物线的增长趋势直到CPU使用达到100%,如果随着系统的压力增加 CPU的使用率 无法增长说明系统存在性能瓶颈,当然就算CPU使用率随着压力的增大到达100%了也不能排除没有性能问题,比如CPU被大量String + 所占用。

f8fc400b9ca5fa3ea994c678ba6ccd69.jpg

性能瓶颈分析的手段和工具

性能瓶颈是动态的,低负载下不是瓶颈的地方,在高负载下可能成为瓶颈。许多商业工具JProfiler、Optimizeit
本身带来很大的开销。所以这种情况采用线程堆栈才是有效的分析方法。

  • 线程数量太少,没有把cpu利用起来
  • 锁粒度太大,线程被阻塞(比如锁中有耗时的IO操作等)
  • 资源不足,如数据库连接不够,线程被挂起
  • 处理效率低(如http操作,SQL性能低)

如果是锁粒度太大,通过堆栈很容易分析出

// 等待锁 0x00007f9a880059b8
\"Thread-2\": waiting to lock monitor 0x00007f9a880059b8 (object 0x00000000f771aec8, a java.lang.Object), which is held by \"Thread-11\" 
// 等待锁 0x00007f9a880059b8

\"Thread-1\": waiting to lock monitor 0x00007f9a880059b8 (object 0x00000000f771aec8, a java.lang.Object), which is held by \"Thread-21\"


多个线程等待同一把锁,所以我们得到堆栈信息,可以群全局搜索 waiting to lock 看看是否存在等待同一把锁得情况,排查是否锁粒度过大是否有低效率的代码问题

总结

通过本篇内容,能实实在在学到一套 “定位 + 解决” 系统性能问题的实用方法论,不是空泛的理论。

我们讨论了 CPU、IO、JVM 这三大性能瓶颈的具体诱因,以及怎么去分析是否存在性能瓶颈问题。比如看压力增大时 CPU 使用率是否跟着涨,就能初步判断系统卡在哪。通过线程堆栈里的 “waiting to lock” 排查锁竞争,定位线程阻塞的代码。

以 [java 深度调式技术] 第一章堆栈分析内容为主线,也在其内容骨架上做了一些补充吧。希望本片文章能对你起到一定的作用。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 java深度调试【第二章通过堆栈分析性能瓶颈】 https://www.zuozi.net/35691.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务