Java代码性能分析:在实践中寻找瓶颈

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么要对Java代码进行性能分析?

\n\n

Java开发不仅仅是编写正确的代码,更是创建快速、可扩展的应用程序。即使是一个设计完美的系统,也可能因为一行非最优的代码而变慢。如何找到这个“瓶颈”?答案是——性能分析。

\n\n

性能分析是收集应用程序运行时数据的过程:方法执行时间、内存使用、线程和CPU负载。没有它,优化就变成了猜测。在本文中,我们将分析Java代码性能分析的实践示例,学习如何解读结果并找到瓶颈。

\n\n

我们将介绍两种流行的工具:VisualVM(免费,内置于JDK)和JProfiler(商业版,但功能强大)。所有示例都将使用真实代码。

\n\n

主要部分:性能分析实践

\n\n

1. 环境设置与启动分析器

\n\n

首先,确保您已安装JDK(8或更高版本)。VisualVM随JDK 8和9一起提供,对于更新的版本,您需要从官方网站单独下载。

\n\n

让我们编写一个简单的“慢速”代码进行测试。创建一个类 SlowApp.java:

\n\n
import java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\npublic class SlowApp {\n    public static void main(String[] args) throws InterruptedException {\n        List<String> data = new ArrayList<>();\n        Random random = new Random();\n\n        // 模拟长时间的数据加载\n        for (int i = 0; i < 100_000; i++) {\n            data.add(generateString(random));\n        }\n\n        // 数据处理\n        long sum = 0;\n        for (String s : data) {\n            sum += processString(s);\n        }\n\n        System.out.println("Result: " + sum);\n        Thread.sleep(10000); // 以便有时间连接分析器\n    }\n\n    private static String generateString(Random random) {\n        StringBuilder sb = new StringBuilder(100);\n        for (int i = 0; i < 100; i++) {\n            sb.append((char) ('a' + random.nextInt(26)));\n        }\n        return sb.toString();\n    }\n\n    private static long processString(String s) {\n        long result = 0;\n        for (char c : s.toCharArray()) {\n            result += c;\n            // 人为延迟\n            for (int j = 0; j < 100; j++) {\n                result += Math.sqrt(j);\n            }\n        }\n        return result;\n    }\n}
\n\n

运行应用程序:

\n
javac SlowApp.java\njava SlowApp
\n\n

当程序运行时(它将休眠10秒),打开VisualVM。在左侧面板中找到您的进程(SlowApp)并双击。转到Sampler或Profiler选项卡——您将看到CPU和内存的统计数据。

\n\n

2. CPU分析:找到最重的方法

\n\n

在VisualVM中,点击Sampler部分的CPU按钮。几秒钟后,您将得到一个按执行时间(Self Time)排序的方法表。

\n\n

在我们的示例中,您会看到processString方法占用了超过80%的时间。在其内部,是嵌套循环Math.sqrt(j)。这就是瓶颈。

\n\n

如何解读?

\n
    \n
  • Self Time — 直接花费在方法执行上的时间(不包括子方法调用的时间)。
  • \n
  • Total Time — 总时间,包括所有嵌套调用。
  • \n
  • Invocations — 方法的调用次数。
  • \n
\n\n

输出示例(简化):

\n
Method                          Self Time (ms)   Invocations\nSlowApp.processString           8500              100000\nSlowApp.generateString          1200              100000\njava.lang.Math.sqrt             6000                

博客

书籍推荐