Java性能优化是系统性的工程,涉及代码编写、JVM调优、架构设计、以及生产监控多个层面。过早优化是万恶之源,但忽视性能同样危险。性能优化的正确路径是:测量、分析、优化、验证,循环迭代。
参考:https://aescc.cn/category/bedroom.html
性能指标:延迟(Latency)是单个请求的处理时间,通常关注百分位数(P50、P99、P999)。吞吐量(Throughput)是单位时间处理的请求数。资源利用率(CPU、内存、网络、I/O)指示系统瓶颈。错误的性能指标导致错误的优化方向。
微基准测试:JMH(JavaMicrobenchmarkHarness)是编写微基准测试的标准工具。JMH解决了JVM优化的常见陷阱:预热不足(JIT未生效)、死代码消除(测试代码被优化掉)、常量折叠(编译器预先计算结果)、以及虚假共享(缓存行冲突)。JMH提供@Benchmark、@State、@Warmup、@Measurement等注解,生成可靠的性能数据。
JVM调优:JVM参数调优不是黑魔法,而是基于理解和实验的科学。堆大小(-Xms、-Xmx)设置过小导致频繁GC,设置过大导致GC暂停时间过长。年轻代大小(-XX:NewRatio、-XX:NewSize)影响对象晋升速率。GC选择(G1、ZGC、Shenandoah)取决于延迟和吞吐量目标。
GC日志分析是调优的基础。-Xlog:gc*:file=gc.log启用详细日志。工具如GCViewer、GCEasy可视化GC事件,分析暂停时间、吞吐量、晋升速率。频繁的年轻代GC可能意味着年轻代太小;FullGC应该极少发生。
内存分析:堆转储(HeapDump)分析内存泄漏。jmap-dump:live,format=b,file=heap.hprof。MAT(MemoryAnalyzerTool)分析泄漏嫌疑对象,计算支配树(DominatorTree),追踪GCRoots。内存泄漏的常见模式:静态集合无限增长、监听器未注销、ThreadLocal未remove、类加载器泄漏。
参考:https://qfcrz.cn/category/smart-security.html
CPU分析:采样Profiler(AsyncProfiler、JavaFlightRecorder)定期采样线程栈,生成火焰图(FlameGraph)。火焰图的x轴是采样数量(宽度代表时间),y轴是调用栈深度。平顶山表示热点方法。AsyncProfiler支持CPU、分配、锁、以及Java和Native代码的混合分析。
线程分析:线程转储(ThreadDump)分析死锁、线程饥饿、锁竞争。jstack或kill-3。线程转储显示每个线程的状态(RUNNABLE、BLOCKED、WAITING、TIMED_WAITING)和调用栈。死锁检测器识别循环等待。锁竞争分析查看阻塞线程数和等待时间。
I/O分析:文件I/O使用iostat查看磁盘利用率、平均等待时间。网络I/O使用netstat、ss查看连接状态、发送/接收队列。Java层面的NIO缓冲区调优(-Dio.netty.allocator.type=pooled)减少GC压力。
数据库访问优化:数据库通常是性能瓶颈。使用连接池(HikariCP)减少连接开销。监控慢查询,添加索引。使用批量操作减少网络往返。缓存查询结果(本地缓存或Redis)。读写分离分散负载。
JIT编译优化:JIT编译器将热点代码编译为机器码。-XX:+PrintCompilation查看编译日志。-XX:+PrintInlining查看内联决策。JIT优化包括:方法内联(消除调用开销)、逃逸分析(栈上分配、锁消除)、循环优化(展开、向量化)、以及公共子表达式消除。编写对JIT友好的代码:小方法、避免反射、使用final方法、以及保持代码简单。
参考:https://wkmsa.cn/category/sleep-methods.html
JavaFlightRecorder(JFR):JFR是JVM内置的低开销监控工具,Java11+开源。JFR持续收集事件(GC、线程、锁、I/O、分配),开销通常低于1%。jcmdJFR.start启动录制。JMC(JavaMissionControl)分析JFR文件,提供图形化界面和自动分析建议。
生产监控:APM工具(Datadog、NewRelic、SkyWalking)实时监控应用性能。关键指标:请求延迟(P99)、吞吐量(RPS)、错误率(5xx)、GC指标(频率、暂停时间)、线程数、CPU/内存使用率。设置告警阈值,在性能下降时及时通知。
性能测试:性能测试不是开发完成后才做的事情,而是开发流程的一部分。单元测试中测试算法复杂度。集成测试中测试数据库查询。负载测试(JMeter、Gatling)模拟真实流量。稳定性测试(长时间运行)发现内存泄漏。容量测试确定系统极限。
性能优化的收益递减:第一个优化可能带来50%的提升,第二个20%,第十个可能只有1%。识别真正的瓶颈,避免在非热点代码上浪费精力。优化后的代码应该保持可读性——除非在极端性能场景,微优化不值得。
Java性能工程是持续的过程,不是一次性的任务。建立性能基准,持续监控,定期审查。性能下降是渐进的过程,早发现、早修复。
参考:https://aescc.cn