Java 不仅是一种编程语言,而且还是一个包含许多工具的非常丰富的生态系统。 JDK包含的程序允许我们编译自己的程序并在程序执行的整个生命周期中监视它们的状态以及Java虚拟机的状态。 前言 我们在工作中经常面临以下一系列问题: 不清楚应用程序的运行状态,包括但不限于GC情况、热代码、热线程、异常(尤其是吞没异常); 目前尚不清楚应用程序代码是如何执行的,甚至不清楚某个模块是否仍然可以运行。尤其是有一定历史的应用,可能很多开发者都为其贡献过代码,但随着业务的发展,这些功能可能已经不复存在了。用过的; 不知道应用程序的IO情况。注意,我这里说的绝对不只是zabbix上的“磁盘IO”,而是针对某个文件、某个端口、某个线程具体的IO情况。 这些问题的直接后果就是无从下手性能优化。我们只能根据经验和直觉去猜测、埋点、优化。当然,最终的结果通常是“加机容量、扩大产能”。 那么有没有什么工具可以准确定位我们的问题呢?首先我们来看看Java有哪些监控工具。 Java监控工具 Java 不仅仅是一种编程语言,而且是一个拥有许多工具的非常丰富的生态系统。 JDK包含的程序允许我们编译自己的程序并在程序执行的整个生命周期中监视它们的状态以及Java虚拟机的状态。 JDK发行版的bin文件夹包含以下可用于分析和监控的程序: Java VisualVM (jvisualvm.exe) JConsole(jconsole.exe) Java 任务控制 (jmc.exe) 诊断命令工具(jcmd.exe) Java VisualVM 曾经是 Oracle 和 Open JDK 发行版的一部分。但是,从 Java 9 开始,JDK 发行版不再附带 Java VisualVM。 但令人欣喜的是,JDK7及以上版本内置了一个新的性能分析工具,这就是我们今天要重点介绍的——Java Flight Recorder,简称JFR。 Java Flight Recorder及其基本概念Java Flight Recorder (JFR) 是一种监视工具,用于收集 Java 应用程序执行期间 Java 虚拟机 (JVM) 中的事件信息。 JFR 是 JDK 发行版的一部分,并集成到 JVM 中。 JFR 旨在尽可能减少对运行应用程序性能的影响。 为了使用JFR,我们应该激活它。我们可以通过两种方式实现这一目标: 启动 Java 应用程序时 当 Java 应用程序已运行时将诊断命令传递给 jcmd 工具 JFR 没有独立的工具。我们使用 Java Mission Control (JMC),它包含一个插件,使我们能够可视化 JFR 收集的数据。 这三个组件(JFR、jcmd 和 JMC)构成了一个完整的套件,用于收集有关正在运行的 Java 程序的低级运行时信息。我们可能会发现这些信息在优化程序或在出现问题时诊断程序时很有用。 JFR 以最小的开销记录有关 Java 运行时和在其中运行的 Java 应用程序的详细信息。数据被记录为数据时间点(称为事件)。典型的事件可以是等待锁的线程、GC、CPU 周期使用数据等。 创建飞行记录时,您可以选择保存哪些事件,这称为记录模板。有些模板只保存基本事件,对性能影响不大。其他模板可能会产生轻微的性能开销,并且可能会触发 GC 收集更多信息。通常,超过百分之几的管理费用很少见。飞行记录可用于调试各种问题,从性能问题到内存泄漏或严重的锁争用。 需要注意的是,JFR是一个性能数据收集工具。它将最终收集的数据存储在jfr格式的文件中。由于它不具备数据分析或可视化功能,所以我们一般需要配合JDK的另一个内置工具。 ,与JMC一起使用。 什么是江铃汽车 JMC,Java Mission Control,是一种图形化性能监控工具,包含 Java7 (7u40) 和 Java8 商业版本的新监控和控制功能。它可以直接打开 jfr 生成的原始数据采集文件。 你可以在你机器的命令行输入jmc来体验一下。 与其他Profile工具相比有哪些优势? 开发环境中我们使用VisualVM、JProfiler等,功能强大且支持图形界面操作,可以快速定位代码问题。但是,它们对应用程序性能也有很大影响,因此不适合在生产环境中使用。这些软件也需要依附于jvm进程。生产环境一般与网络隔离,这是很难实现的。 我们在生产环境中最常用的分析工具是java/bin下的jstack。做几次jstack就相当于profiling了。 jstack 方便且易于使用,但它不是特别适合分析。操作频率低,会导致安全点指标急剧上升等。 这里重点来了,使用jfr不需要给现有的应用程序添加任何额外的参数、重启进程等,直接在命令行执行就可以实时生效。 100%非侵入,稳定可靠,不影响线上应用的运行。 用法 选择一台机器,登录,找到你想要监控的进程的PID。 执行以下命令 pid=`jcmd | grep java 进程关键字| awk '{print $1}'` jcmd $pid VM.unlock_commercial_features #先解锁技能 jcmd $pid JFR.start name=myrec settings=profile delay=20s period=2m filename=/tmp/$pid.jfr #其中,其中delay参数表示Profile延迟启动时间,duration表示连续采集时间,设置为2分钟。 #settings 指示使用哪个集合配置。 #官方默认配置带有一个名为profile的配置。此配置不会采集异常信息 #注意,采集数据生成后,请执行以下命令删除此采集 jcmd $pid JFR.stop name=myrec 就是这样。等待 2 分钟 + 20 秒,将生成 /tmp/pid.jfr 文件。该文件可以直接导入到JMC工具中。 如何定制信息收集 默认收集配置收集的信息较少,性能较好。如果您需要收集更多信息,请使用JMC工具的模板管理器进行定制。 生成的自定义文件可以放置在/jre/lib/jfr 目录中。 实用的OkHttpClient内存溢出问题 以最近的一个故障排除问题为例。应用服务使用OkHttpClient时,在创建大量外部连接时,线程堆积导致内存溢出。 我们首先登录在线服务器,通过上面介绍的设置收集信息:等待2-3分钟,查看tmp目录下的目标文件。文件写入完成后,将生成的jfr文件上传到ftp,安全下载到安装JMC的机器上。 打开JMC,加载jfr文件,并开始分析。 通过上图的分析,我们可以看到OkHttpClient创建了大量的线程,并且在栈上分配了大量的内存。查询源码后发现,主要原因是应用程序中创建OkHttpClient对象时,没有创建并复用同一个OkHttpClient实例。所有http请求都会重复创建一个新的实例,并且每个实例都有自己的连接池和线程池,导致线程大量堆积。 本文转载自微信公众号《小王哥写代码》。您可以通过以下二维码关注。转载本文请联系小王哥写代码公众号。