修正图片路径
This commit is contained in:
parent
a01ad0130b
commit
4597816ccc
@ -1,5 +1,6 @@
|
||||
# JVM 性能监控之可视化工具
|
||||
<nav>
|
||||
|
||||
<nav>
|
||||
<a href="#一简介">一、简介</a><br/>
|
||||
<a href="#二JConsole">二、JConsole</a><br/>
|
||||
<a href="#21-简介">2.1 简介</a><br/>
|
||||
@ -13,6 +14,7 @@
|
||||
<a href="#42-使用安全凭证">4.2 使用安全凭证</a><br/>
|
||||
</nav>
|
||||
|
||||
|
||||
## 一、简介
|
||||
|
||||
在 JDK 安装目录的 `bin` 文件夹下,除了提供有命令行监控工具外,还提供了几种可视化的监控工具,以方便用户直观地了解虚拟机的运行状态。常用的可视化监控工具如下:
|
||||
@ -29,11 +31,13 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M
|
||||
|
||||
打开位于 bin 目录下的 `jconsole` 程序后,它会自动扫描当前主机上的所有 JVM 进程:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jconsole-start.png"/> </div>
|
||||
|
||||
|
||||
选中需要监控的进程后,点击连接,即可进入监控界面。监控界面包含了 *概览*、*内存*、*线程*、*类*、*VM 概要*、*MBean* 六个选项卡。其中概览界面显示的是 *内存*、*线程*、*类* 等三个选项卡界面的概览信息,如下所示:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jconsole-概览.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -41,7 +45,8 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M
|
||||
|
||||
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jconsole-内存.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -49,13 +54,15 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M
|
||||
|
||||
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jconsole-检测死锁.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
点击死锁选项卡则可以看到造成死锁的线程:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jconsole-死锁.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -63,7 +70,8 @@ JConsole(Java Monitoring and Management Console)是一款基于 JMX(Java M
|
||||
|
||||
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jconsole-概要.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -83,7 +91,8 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
||||
|
||||
打开位于 bin 目录下的 `jvisualvm` 程序, 它会自动扫描当前主机上的所有 JVM 进程:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -91,7 +100,8 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
||||
|
||||
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual-监视.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -101,7 +111,8 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
||||
|
||||
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual-堆dump.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -109,7 +120,8 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
||||
|
||||
在线程界面可以查看所有线程的状态,如果出现死锁,该界面还会进行提示:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual-线程.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -117,7 +129,8 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
||||
|
||||
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual-dump.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -125,7 +138,8 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
||||
|
||||
在 Profiler 界面,可以进行 CPU 和 内存的性能分析。要开始性能分析,需要先选择 **CPU** 或 **内存** 按钮中的一个,VisualVM 将会开始记录应用程序执行过的所有方法:如果是进行的是 CPU 执行时间分析,将会统计每个方法的执行次数、执行耗时;如果是内存分析,则会统计每个方法关联的对象数以及这些对象所占的空间。想要结束性能分析,点击停止按钮即可:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual-性能分析.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -133,7 +147,8 @@ VisualVM(All-in-One Java Troubleshooting Tool)是 Oracle 提供的功能最
|
||||
|
||||
Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会实时监控虚拟机的状态,在功能上类似于 jstat 命令:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual-gc.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -141,7 +156,8 @@ Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会
|
||||
|
||||
在主界面,点击 **工具 => 插件** ,可以打开插件面板。右击插件选项或者点击安装按钮即可完成对应插件的安装:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual-插件安装.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -149,7 +165,8 @@ Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会
|
||||
|
||||
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual-插件中心.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -157,7 +174,8 @@ Visual GC 面板默认是不显示的,需要通过插件进行扩展。它会
|
||||
|
||||
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual-配置插件中心.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -214,7 +232,8 @@ chown root:root /usr/local/jmxremote.password
|
||||
|
||||
之后在使用 VisualVM 进行远程连接时,配置如下:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jvisual-连接远程主机.png"/> </div>
|
||||
|
||||
|
||||
需要注意的是这里的端口号是配置的 `Dcom.sun.management.jmxremote.port` 的值,而不是 Java 程序的端口号。连接完成后,即可查看到对应进程的监控状态。
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
<a href="#七jstack">七、jstack</a><br/>
|
||||
</nav>
|
||||
|
||||
|
||||
## 一、简介
|
||||
|
||||
在 JDK 安装目录的 `bin` 文件夹下,除了提供有 `javac` 、`java` 这两个常用的编译和运行工具外,还提供了一系列命令行工具用于 JVM 的性能监控和故障诊断,常用的命令如下:
|
||||
@ -64,7 +65,8 @@ jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
|
||||
jstat -gc 9492 3s 5 # 每3s输出一次,一共输出5次
|
||||
```
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jstat_gc.png"/> </div>
|
||||
|
||||
|
||||
输出信息中各个参数含义分别如下:
|
||||
|
||||
@ -113,7 +115,8 @@ jinfo -flags 13604
|
||||
jinfo -flag CMSInitiatingOccupancyFraction 13604
|
||||
```
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jinfo.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -142,7 +145,8 @@ jmap [option] <pid>
|
||||
jmap -dump:format=b,file=test.bin 3260
|
||||
```
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jmap.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -166,19 +170,23 @@ public class StackOverFlowTest {
|
||||
|
||||
其最终会抛出 `java.lang.OutOfMemoryError: Java heap space` 异常,意味着在 JVM 堆上发生了内存溢出。在程序运行期间,我们可以使用上面的 jmap 命令生成堆转储快照,并使用 jhat 命令进行分析:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jhat.png"/> </div>
|
||||
|
||||
|
||||
jhat 命令最终的分析结果会以网页的方式进行提供,端口为 7000,界面如下:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jhat_web.png"/> </div>
|
||||
|
||||
|
||||
jhat 分析的结果并不够直观,因此我们还可以借助第三方工具来分析堆转储快照,这里以 JProfiler 为例,该软件可以直接从[官网](https://www.ej-technologies.com/products/jprofiler/overview.htm)下载并安装,安装完成后,点击 `session` 选项卡,并使用 `Open Snapshot` 打开 jmap 命令生成的堆转储快照:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jprofiler-1.png"/> </div>
|
||||
|
||||
|
||||
之后程序会自动进行分析,分析结果如下:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jprofiler-2.png"/> </div>
|
||||
|
||||
|
||||
通过以上可视化的统计结果,我们就可以很快定位到导致内存溢出的原因。
|
||||
|
||||
@ -242,7 +250,8 @@ jstack 8112
|
||||
|
||||
输出结果如下:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\jstack.png"/> </div>
|
||||
|
||||
|
||||
从输出中结果中可以看出,出现了一个死锁,该死锁由线程 Thread-0 和 Thread-1 导致,原因是 Thread-0 锁住了对象 `<0x00000000d6d8d610>` ,并尝试获取 `<0x00000000d6d8d640>` 对象的锁;但是 Thread-0 却恰恰相反,锁住了对象 `<0x00000000d6d8d640>` ,并尝试获取 `<0x00000000d6d8d610>` 对象的锁,由此导致死锁。
|
||||
|
||||
|
@ -14,13 +14,18 @@
|
||||
<a href="#44-FilterChain">4.4 FilterChain</a><br/>
|
||||
<a href="#五请求流程">五、请求流程</a><br/>
|
||||
<a href="#六启动流程">六、启动流程</a><br/>
|
||||
<a href="#1-startupsh--catalinash">1. startup.sh & catalina.sh</a><br/>
|
||||
<a href="#2-Bootstrap">2. Bootstrap</a><br/>
|
||||
<a href="#3-Catalina">3. Catalina</a><br/>
|
||||
<a href="#1-startupsh--catalinash">1. startup.sh & catalina.sh</a><br/>
|
||||
<a href="#2-Bootstrap">2. Bootstrap</a><br/>
|
||||
<a href="#3-Catalina">3. Catalina</a><br/>
|
||||
<a href="#七类加载器">七、类加载器</a><br/>
|
||||
<a href="#1-Web-App-Class-Loader">1. Web App Class Loader</a><br/>
|
||||
<a href="#2-Shared-Class-Loader">2. Shared Class Loader</a><br/>
|
||||
<a href="#3-Catalina-Class-Loader">3. Catalina Class Loader</a><br/>
|
||||
<a href="#4-Common-Class-Loader">4. Common Class Loader</a><br/>
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
## 一、Tomcat 简介
|
||||
|
||||
Tomcat 是目前主流的基于 Java 语言的轻量级应用服务器,它是对是 Java Servlet,JavaServer Pages(JSP),Java Expression Language(EL 表达式)和 Java WebSocket 技术的开源实现。当前 Tomcat 共有四个版本:
|
||||
@ -36,7 +41,8 @@ Tomcat 是目前主流的基于 Java 语言的轻量级应用服务器,它是
|
||||
|
||||
Tomcat 的整体架构如下:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\tomcat_架构.png"/> </div>
|
||||
|
||||
|
||||
+ **Server**:表示整个 Servlet 容器,在整个 Tomcat 运行环境中只有唯一一个 Server 实例。一个 Server 包含多个 Service,每个 Service 互相独立,但共享一个 JVM 以及系统类库。
|
||||
+ **Service**:一个 Service 负责维护多个 Connector 和一个 Engine。其中 Connector 负责开启 Socket 并监听客户端请求,返回响应数据;Engine 负责具体的请求处理。
|
||||
@ -54,7 +60,8 @@ Tomcat 的整体架构如下:
|
||||
|
||||
连接器的主要功能是将 Socket 的输入转换为 Request 对象,并交由容器进行处理;之后再将容器处理完成的 Response 对象写到输出流。连接器的内部组件如下:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\tomcat连接器组件.png"/> </div>
|
||||
|
||||
|
||||
### 3.1 ProtocolHandler
|
||||
|
||||
@ -79,7 +86,8 @@ EndPoint 会启动线程来监听服务器端口,并负责处理来自客户
|
||||
|
||||
ProtocolHandler 通过组合不同类型的 Endpoint 和 Processor 来实现针对具体协议的处理功能。按照不同的协议(HTTP 和 AJP)和不同的 I/O 方式(NIO,NIO2,AJP)进行组合,其有以下六个具体的实现类:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\tomcat_AbstractProtocol.png"/> </div>
|
||||
|
||||
|
||||
**4. 协议升级**
|
||||
|
||||
@ -89,7 +97,8 @@ ProtocolHandler 通过组合不同类型的 Endpoint 和 Processor 来实现针
|
||||
|
||||
Tomcat 设计者希望连接器是一个单独的组件,能够脱离 Servlet 规范而独立存在,以便增加其使用场景,因此 Process 对输入流封装后得到的 Request 不是一个 Servlet Request,该 Request 的全限定命名为:org.apache.coyote.Request 。因此在这里需要使用适配器模式(具体实现类是 CoyoteAdapter)将其转换为 org.apache.catalina.connector.Request,它才是标准的 ServletRequest 的实现:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\tomcat_request.png"/> </div>
|
||||
|
||||
|
||||
### 3.3 Mapper 和 MapperListener
|
||||
|
||||
@ -107,7 +116,8 @@ Tomcat 中的所有容器都实现了 Container 接口,它定义了容器共
|
||||
|
||||
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\tomcat_container.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -120,7 +130,8 @@ Tomcat 之所以采用分层的结构,主要是为了更好的灵活性和可
|
||||
+ **Context**:表示一个具体的 Web 应用程序,一个虚拟主机可以包含多个 Context;
|
||||
+ **Wrapper**:是 Tomcat 对 Servlet 的包装,一个 Context 中可以有多个 Wrapper。
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\tomcat_分层结构.png"/> </div>
|
||||
|
||||
|
||||
Tomcat 容器的分层结构在其 conf 目录下的 `server.xml` 配置文件中也有体现:
|
||||
|
||||
@ -139,7 +150,8 @@ Tomcat 容器的分层结构在其 conf 目录下的 `server.xml` 配置文件
|
||||
|
||||
### 4.3 Pipeline 和 Valve
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\tomcat_多层容器.jpg"/> </div>
|
||||
|
||||
|
||||
由连接器发过来的请求会最先发送到 Engine,最终逐层传递,直至我们编写的 Servlet,这种传递主要通过 Pipeline 和 Valve 来实现。每层容器都有自己的 Pipeline,Pipeline 相当于处理管道;每个 Pipeline 中有一个 Valve 链,每个 Valve 可以看做一个独立的处理单元,用于对请求进行处理。最基础的 Valve 叫做 Basic Valve,新增的 Valve 会位于已有的 Valve 之前。Pipeline 和 Valve 的接口定义如下:
|
||||
|
||||
@ -284,7 +296,8 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) throws
|
||||
|
||||
Tomcat 整体的启动流程如下图所示:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\tomcat启动流程.png"/> </div>
|
||||
|
||||
|
||||
|
||||
|
||||
@ -306,7 +319,8 @@ Catalina 通过 Digester 解析 server.xml 来创建所有的服务组件。Dige
|
||||
|
||||
Tomcat 并没有完全沿用 JVM 默认的类加载机制,为了保证 Web 应用之间的隔离性和加载的灵活性,其采用了下图所示的类加载机制:
|
||||
|
||||

|
||||
<div align="center"> <img src="..\pictures\tomcat_类加载器.jpg"/> </div>
|
||||
|
||||
|
||||
#### 1. Web App Class Loader
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user