首页 > 编程语言 >【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南

【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南

时间:2024-01-21 13:03:13浏览次数:30  
标签:used Java 转储 调优 线程 JVM 堆栈

专栏介绍

学习JVM需要一定的编程经验和计算机基础知识,适用于从事Java开发、系统架构设计、性能优化、研究学习等领域的专业人士和技术爱好者。

前提准备

  • 编程基础:具备良好的编程基础,理解面向对象编程(OOP)的基本概念,熟悉Java编程语言。
  • 数据结构与算法:对基本的数据结构和算法有一定了解,理解内存管理、线程操作等基本概念。

面向人群

学习本专栏以及本章内容的前提和适用人群如下:

  • Java开发人员:JVM是Java程序的核心执行引擎,因此Java开发人员需要深入了解JVM的工作原理和运行机制,以优化程序性能并解决相关问题。
  • 系统架构师和高级工程师:对系统整体性能、稳定性有较高要求的人群,有必要深入理解JVM以优化系统性能。
  • Java程序员和技术爱好者:具备一定Java编程经验,有意向深入了解JVM内部工作原理的人群。
  • 研究人员和学生:从事计算机科学相关研究或学习的人群,有兴趣深入研究JVM内部原理和优化方法。
  • JVM运维工程师:负责JVM性能优化、故障排查和调优的专业人员,需要对JVM有深入的理解。

知识脉络

每位Java开发者都了解到Java字节码是在Java运行时环境(JRE)上执行的。JRE包含了最为关键的组成部分:Java虚拟机(JVM),它负责分析和执行Java字节码。通常情况下,大多数Java开发者无需深入了解虚拟机的内部运行原理。即使对虚拟机的运行机制不甚了解,也不会对开发工作产生太多影响。然而,对JVM有一定了解的话,将更有助于深入理解Java语言,并解决一些看似困难的问题。

本专栏全面系统地剖析了特定虚拟机产品(即HotSpot,Oracle官方虚拟机)的实现,本人不仅深刻地讲解了看似深奥的原理,还提供了大量易于上手的实践案例,下面是总体的JVM相关的知识拓扑架构。

【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南_Java

tips:当然还有一些最新的JVM特性未在这张图并非展示本专栏的全部内容,另外还包含了最新的JVM特性。


分析线程转储

本文将教您如何分析JVM线程转储,并确定问题的根本原因。从我的角度来看,线程转储分析是任何参与Java EE生产支持的个人都需要掌握的最重要技能集。您可以从线程转储快照中获得的信息量通常远远超出了您的想象。

线程转储分析的介绍

在深入研究线程转储分析和问题模式之前,了解基本原理是至关重要的。

JVM和线程运行机制

"Java虚拟机是Java EE平台的基石,它为您的中间件和应用提供了运行环境。在这里,您的程序会被部署并得以运行。"

【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南_JVM_02

JVM是一种中间件软件,为Java/Java EE程序提供运行环境。它支持字节码格式的运行时,并具备多种特性,如IO设施、数据结构、线程管理、安全和监控。此外,JVM还通过垃圾收集器实现动态内存分配和管理。

JVM和中间件之间的软件交互

下面的图表显示了JVM、中间件和应用程序之间的高级交互视图。

【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南_java_03

这是一个展示JVM、中间件和应用程序之间典型而简单交互的图示。在标准的Java EE应用程序中,线程的分配主要在中间件内核和JVM之间完成(尽管在应用程序本身或某些API直接创建线程时可能会有一些例外,但这并不常见,需要非常小心地处理)。

【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南_java_04

注意,JVM本身会管理一些线程,例如垃圾收集(GC)线程,用于处理并发的垃圾收集任务。

由于大多数线程分配是由Java EE容器完成的,因此理解和识别线程堆栈跟踪,并正确地从线程转储数据中识别它们非常重要。这将帮助您快速了解Java EE容器试图执行的请求类型。

从线程转储分析的角度来看,您可以学习如何区分不同的线程池,并识别请求类型。这将帮助您更好地理解JVM中的线程池,并进行有效的分析。

JVM线程转储

JVM线程转储器是在特定时间生成的一个快照,它提供了所有已创建的Java线程的完整列表。这个转储器可以帮助您获取关于线程的详细信息,以便进行分析和调试。

Java快照的基本信息

找到的每个单独的Java线程都为您提供以下信息:

【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南_java_05

  • 线程名称:通常被中间件供应商用于识别线程Id及其关联的线程池名称和状态(正在运行、被卡住等)。
  • 线程类型和优先级等:守护进程prio=3 中间件软件通常创建它们的线程作为守护进程,这意味着它们的线程在后台运行;为其用户提供服务,例如您的Java EE应用程序
  • Java线程ID ex: tid=0x000000011e52a800 这是通过java.lang获得的Java线程ID。Thread.getId(),通常实现为一个自动增量的长1..n
  • 本地线程ID ex: nid=0x251c关键信息,因为这个本地线程ID允许您相关,例如哪些线程从操作系统的角度使用最多的CPU在您的JVM等。
  • Java线程状态和细节等:等待监视器输入[0x fffffff ea5afb000]java.lang.thread。状态:已阻塞(在对象监视器上)允许快速了解线程状态及其潜在的当前阻塞条件
  • Java线程堆栈跟踪:这是迄今为止您将从线程转储文件中找到的最重要的数据。这也是您将花费大部分分析时间的地方,因为Java堆栈跟踪为您提供了90%的信息,以便查明许多问题模式类型的根本原因。
  • Java堆分解:从HotSpot VM 1.6开始,您还将在线程转储快照的底部发现HotSpot内存空间利用率的细分,如Java堆(YoungGen,OldGen)和PermGen/metaspace。当怀疑过多的GC是可能的根本原因时,这一点非常有用,因此您可以对已找到的线程数据/模式进行开箱即用的相关性。
内存回收日志
Heap
PSYoungGen total 466944K, used 178734K [0xffffffff45c00000, 0xffffffff70800000,0xffffffff70800000)
eden space 233472K, 76% used 
[0xffffffff45c00000,0xffffffff50ab7c50,0xffffffff54000000)
from space 233472K, 0% used 
[0xffffffff62400000,0xffffffff62400000,0xffffffff70800000)
to space 233472K, 0% used 
[0xffffffff54000000,0xffffffff54000000,0xffffffff62400000)
PSOldGen total 1400832K, used 1400831K [0xfffffffef0400000, 
0xffffffff45c00000, 0xffffffff45c00000)
object space 1400832K, 99% used 
[0xfffffffef0400000,0xffffffff45bfffb8,0xffffffff45c00000)
PSPermGen total 262144K, used 248475K [0xfffffffed0400000, 
0xfffffffee0400000, 0xfffffffef0400000)
object space 262144K, 94% used 
[0xfffffffed0400000,0xfffffffedf6a6f08,0xfffffffee0400000)
线程转储分解概述

为了让你更好地理解,找到下面的图表,显示一个HotSpot VM线程转储及其常见的线程池的可视化分解发现:

【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南_Java_06

您可以从HotSpot VM线程转储文件中找到一些信息。根据您的问题模式,其中的一些将比其他的更重要,现在,根据我们的示例Hotspot线程转储,在下面找到每个线程转储部分的详细说明。

全线程转储标识符

这基本上是唯一的关键字,一旦你生成线程转储(例如:通过UNIX,你可以在<PID>中找到。这是线程转储快照数据的开头。

Full thread dump Java HotSpot(TM) 64-Bit Server VM (20.0-b11 mixed mode):
Java EE中间件,第三方和自定义应用程序线程

这部分是线程转储的核心,您通常将在这里花费大部分的分析时间。找到的线程的数量将取决于您使用的中间件软件、第三方库(可能有自己的线程)和您的应用程序(如果创建任何自定义线程,这通常不是最佳实践)。

在我们的示例线程转储中,Weblogic是所使用的中间件。从Weblogic9.2开始,使用一个自调优线程池和唯一标识符“Weblogic.内核”。默认值(自调)。

"[STANDBY] ExecuteThread: '414' for queue: 'weblogic.kernel.Default (selftuning)'" daemon prio=3 tid=0x000000010916a800 nid=0x2613 in Object.wait() 
[0xfffffffe9edff000]
 java.lang.Thread.State: WAITING (on object monitor)
 at java.lang.Object.wait(Native Method)
 - waiting on <0xffffffff27d44de0> (a weblogic.work.ExecuteThread)
 at java.lang.Object.wait(Object.java:485)
 at weblogic.work.ExecuteThread.waitForRequest(ExecuteThread.java:160)
 - locked <0xffffffff27d44de0> (a weblogic.work.ExecuteThread)
 at weblogic.work.ExecuteThread.run(ExecuteThread.java:181)
HotSpot VM Thread

这是一个由Hotspot管理的内部线程,以便执行内部本机操作。通常,您不应该担心这个问题,除非您看到高CPU(通过线程转储和prstat /本地线程id相关性)。

"VM Periodic Task Thread" prio=3 tid=0x0000000101238800 nid=0x19 waiting on condition
HotSpot GC Thread

当使用HotSpot并行GC时(现在在使用多物理核硬件时很常见),HotSpot VM会默认创建或根据JVM调优特定#的GC线程。这些GC线程允许VM以并行的方式执行其定期的GC清理,从而导致GC时间的总体减少;以增加CPU利用率为代价。

"GC task thread#0 (ParallelGC)" prio=3 tid=0x0000000100120000 nid=0x3 runnable 
"GC task thread#1 (ParallelGC)" prio=3 tid=0x0000000100131000 nid=0x4 runnable

这也是至关重要的数据,因为当遇到与GC相关的问题时,如GC过多、内存泄漏过多等,您将能够使用本地id值(nid=0x3)将从OS / Java进程中观察到的任何高CPU与这些线程关联起来。

JNI全局引用计数

JNI(Java本机接口)全局引用基本上是从本地代码到由Java垃圾收集器管理的Java对象的对象引用。它的作用是防止收集本机代码仍在使用的对象,但在技术上讲,在Java代码中没有“实时”引用。

为了检测与JNI相关的泄漏,密切关注JNI的参考文献也很重要。如果程序直接使用JNI或使用第三方工具,如监控工具,容易导致本机内存泄漏,就会发生这种情况

JNI global references: 1925
Java堆利用率视图

这些数据被添加到JDK中,并为您提供了Hotspot堆的一个简短和快速的视图。我发现它很有用当故障排除GC相关问题以及高CPU因为你得到线程转储和Java堆在一个快照允许你确定(或排除)任何压力点在一个特定的Java堆内存空间以及当前线程计算目前正在完成。正如您在我们的示例线程转储中看到的,Java堆OldGen已经用爆了!

Heap
PSYoungGen total 466944K, used 178734K [0xffffffff45c00000, 
0xffffffff70800000, 0xffffffff70800000)
 eden space 233472K, 76% used 
[0xffffffff45c00000,0xffffffff50ab7c50,0xffffffff54000000)
 from space 233472K, 0% used 
[0xffffffff62400000,0xffffffff62400000,0xffffffff70800000)
 to space 233472K, 0% used 
[0xffffffff54000000,0xffffffff54000000,0xffffffff62400000)
PSOldGen total 1400832K, used 1400831K [0xfffffffef0400000, 
0xffffffff45c00000, 0xffffffff45c00000)
 object space 1400832K, 99% used
[0xfffffffef0400000,0xffffffff45bfffb8,0xffffffff45c00000)
PSPermGen total 262144K, used 248475K [0xfffffffed0400000, 
0xfffffffee0400000, 0xfffffffef0400000)
 object space 262144K, 94% used 
[0xfffffffed0400000,0xfffffffedf6a6f08,0xfffffffee0400000)

为了让您快速从线程转储中识别出问题模式,您首先需要了解如何读取线程堆栈跟踪以及如何正确地获取“故事”。这意味着,如果我让你告诉我线程#38在做什么;你应该能够精确地回答;包括线程堆栈跟踪是否显示了一个健康(正常)和挂起条件。

Java堆栈跟踪重新访问

你们大多数人都熟悉Java堆栈跟踪。这是我们在抛出Java异常时从服务器和应用程序日志文件中找到的典型数据。在此上下文中,Java堆栈跟踪为我们提供了触发Java异常的线程的代码执行路径,例如,一个java.lang.NoClassDefFound错误,java.lang.Nullpointer异常等。这样的代码执行路径使我们能够看到最终导致Java异常的不同代码层。

Java堆栈跟踪必须始终从自下而上读取
  • 底部的一行将公开请求的发起者,例如Java / Java EE容器线程。
  • 堆栈跟踪顶部的第一行将显示最后一个异常被触发的Java类。

让我们通过一个简单的例子来介绍一下这个过程。我们创建了一个示例Java程序,简单地执行一些类方法调用并抛出一个异常。所生成的程序输出如下所述:

JavaStrackTraceSimulator
Author: Pierre-Hugues Charbonneau
http://javaeesupportpatterns.blogspot.com
Exception in thread "main" java.lang.IllegalArgumentException: 
 at org.ph.javaee.training.td.Class2.call(Class2.java:12)
 at org.ph.javaee.training.td.Class1.call(Class1.java:14)
 at org.ph.javaee.training.td.JavaSTSimulator.main(JavaSTSimulator.java:20)
  • Java program JavaSTSimulator is invoked (via the "main" Thread)
  • The simulator then invokes method call() from Class1
  • Class1 method call() then invokes Class2 method call()
  • Class2 method call()throws a Java Exception: java.lang.IllegalArgumentException
  • The Java Exception is then displayed in the log / standard output

如您所示,导致此异常的代码执行路径总是从下向上显示。上面的分析过程对于任何Java程序员来说都应该是非常熟悉的。接下来您将看到的是,线程转储线程堆栈跟踪分析过程与上面的Java堆栈跟踪分析非常相似。

线程转储:线程堆栈跟踪分析

从JVM生成的线程转储为您提供了整个JVM进程中所有“已创建的”线程的代码级执行快照。已创建的线程并不意味着所有这些线程实际上都在做一些事情。在从Java EE容器JVM生成的典型线程转储快照中:

  • 一些线程可以执行原始的计算任务,如XML解析、IO /磁盘访问等。
  • 一些线程可能正在等待一些阻塞的IO调用,比如远程Web服务调用,DB/JDBC查询等。
  • 一些线程可能涉及到垃圾收集,例如GC线程。
  • 一些线程将等待一些工作要做(不做任何工作的线程通常处于等待状态)
  • 一些线程可能正在等待其他线程的完成工作,例如,线程等待获取某些对象上的监视器锁(同步块{})。

线程堆栈跟踪为您提供了其当前执行情况的快照。第一行通常包括线程的本机信息,如其名称、状态、地址等。必须从自下而上开始读取当前的执行堆栈跟踪。请遵循下面的分析过程。你使用线程转储分析的经验越多,你就能越快地阅读和识别每个线程所执行的工作:

  1. 开始从底部读取线程堆栈跟踪
  2. 首先,识别发起者(Java EE容器线程、自定义线程、GC线程、JVM内部线程、独立的Java程序“主”线程等)。
  3. 下一步是确定线程正在执行的请求的类型(WebApp、Web服务、JMS、Remote EJB(RMI)、内部Java EE容器等)。
  4. 下一步是从执行堆栈中跟踪您的应用程序模块涉及线程尝试执行的实际核心工作。分析的复杂性将取决于中间件环境和应用程序的抽象层。
  5. 下一步是查看在第一行之前的最后一个~10-20行。识别线程所涉及的协议或工作,例如HTTP调用、套接字通信、JDBC或原始计算任务,如磁盘访问、类加载等。
  6. 下一步是看第一行。第一行通常告诉线程状态上的LOT,因为它是在您拍摄快照时执行的当前代码片段。
  7. 最后两个步骤的结合将为您提供信息的核心,以总结线程所涉及的工作和/或挂起条件。

现在,使用从JBoss生产环境捕获的线程转储线程堆栈跟踪的真实示例,找到上述步骤的可视化细分。在本例中,许多线程在创建JAX-WS服务实例的新实例时都显示了类似的过度IO问题模式。

【深入浅出JVM原理及调优】「搭建理论知识框架」全方位带你深度剖析Java线程转储分析的开发指南_Java_07

正如您所看到的,最后10行和第一行将告诉我们线程涉及什么挂起或慢状态,如果有的话。从底部开始的行将给我们提供发起者和请求类型的详细信息。

标签:used,Java,转储,调优,线程,JVM,堆栈
From: https://blog.51cto.com/alex4dream/9354735

相关文章

  • Java如何过滤掉一段字符串中出现重复的字母或数字?
    可以使用Java中的HashSet来去除一段字符串中出现重复的字母或数字。HashSet是一个不允许有重复元素的集合,因此可以利用它的特性来去除重复的字符或数字。示例代码如下:importjava.util.HashSet;publicclassRemoveDuplicates{publicstaticvoidmain(String[]args){......
  • JAVA 工具大全
    JAVA常用工具大全 一、commons-lang3引入依赖StringUtils字符串工具类StringEscapeUtils转义字符串工具类NumberUtils数字工具类ArrayUtils数组工具类RandomUtils随机数工具类RandomStringUtils随机字符串工具类Date......
  • 《Java 核心技术·卷 II(原书第 11 版):高级特性》PDF
    内容简介本书针对Java11进行了修订,涵盖了完整的对高级UI特性、企业编程、网络、安全和Java强大的模块系统等内容的讨论。书中对Java复杂的新特性进行了深入而全面的研究,展示了如何使用它们来构建具有专业品质的应用程序,作者所设计的经过全面完整测试的示例反映了当今的Ja......
  • 一文搞清楚Java中的包、类、接口
    写在开头包、类、接口、方法、变量、参数、代码块,这些都是构成Java程序的核心部分,即便最简单的一段代码里都至少要包含里面的三四个内容,这两天花点时间梳理了一下,理解又深刻了几分。Java中的包Java定义了一种名字空间,称之为包:package。一个类总是属于某个包,类名(比如Person)只是......
  • Java学习日记 Day6
    JavaSE:------------IO流------------①File类:就是待读取或写入的文件需要把他包装成一个file类②IO流:形象地理解是文件内容的输入输出通过两根管子,我们把这两个管子包装成对象,这两根管子就是IO流。③字符流:Fileread和FileWrite④字节流:一般都是读图片才使用⑤缓冲字节流(处理......
  • 在JavaScript中减去一个日期时间字符串的两分钟
    例如:js将2024-01-2003:18:38减两分钟的到:2024-01-2003:16:38 functionsubtractTwoMinutes(dateString){//解析日期时间字符串为Date对象constdate=newDate(dateString);//减去两分钟date.setMinutes(date.getMinutes()-2);......
  • 二进制免安装的方式,部署java1.8开发环境
    (1)配置Java环境#1.下载二进制压缩文件[root@servertools]#wgethttps://download.oracle.com/java/18/latest/jdk-18_linux-x64_bin.tar.gz#2.解压Java二进制文件[root@servertools]#tar-xvfjdk-18_linux-x64_bin.tar.gz#3.编写Java代码[root@server~]#catH......
  • Day2--Java学习
    计算机1.1什么是计算机Compute:电子计算机,俗称电脑。能够按照程序运行,自动、高速处理海量数据的现代化智能电子设备。由硬件和软件所组成常见的形式:台式计算机、笔记本计算机、大型计算机等。广泛应用在:科学计算、数据处理、自动控制、计算机辅助设计、......
  • Java接口
    一、什么是接口生活中的接口:一种公共的规范标准;Java中的接口:多个类的公共规范;是一种引用数据类型,其中最重要的是:抽象方法二、接口中的抽象方法 (一)定义接口:publicinterface接口名称{}编译生成的字节码文件仍然是.java->.classJava7:常量+抽象方法Java8新增默认方法+静......
  • JavaScript数组常用方法
    1、带回调函数的方法当涉及到数组操作时,JavaScript提供了许多有用的方法。下面是对一些常见的数组方法的讲解:forEach:forEach方法用于遍历数组中的每个元素,并对每个元素执行一个回调函数。它没有返回值,仅用于执行操作。例如:constnumbers=[1,2,3,4,5];numbers.forEa......