记一次服务Full GC背后的内存泄漏问题,真是匪夷所思

记一次服务Full GC背后的内存泄漏问题,真是匪夷所思,第1张

最近所负责的服务略频繁地收到4xx告警

1、查业务日志,没发现相关错误的日志

2、查nginx access log,发现返回的状态码都是499,从request_uri查了一遍发现不是集中在某一个请求上,说明应该不是某个接口的问题了,有可能进程层面问题了。

通过对upstream_addr 分类,可以看到问题基本都是集中在 某一台这台机器上

3、网上资料了解到,499 是 nginx 扩展的 4xx 错误,代表客户端请求还未返回时,客户端主动断开连接。原因有几种,不过大部分原因都说到有可能服务器upstream处理过慢,导致用户提前关闭连接。那就先往这个方向排查,登录机器查看实际的access.log

发现upstream response都是10s以上。这就证明了上游服务器处理10秒还没有响应,因此nginx提前关闭链接,返回499

4、为什么进程响应如此慢,10秒太不正常了。考虑到那段时间就只有一台机器有问题,而且是进程层面的问题,首先想到的是GC,于是再次登录到机器上查看gc log。发现有Full GC,时间点和告警的时间也吻合。 惊呆的是,这次FullGC耗时长达19.07秒,由于我们的服务使用的是jdk8默认的ParallelGC,FullGC期间,整个应用Stop The World的。这是非常恐怖的一件事

由此看来,4xx告警的初步原因已经定位到,就是FullGC导致的。

那么究竟为什么会发生FullGC呢?需要深入分析一下。

借助服务治理平台的JVM监控观察了几天。期间不同机器不同时间也发生了几次FullGC。从监控发现,基本每台机器隔两天就会发生一次FullGC,每次FullGC后年老代回收的垃圾不算多,使用比例还是挺高的。

为什么年老代空间占用这么多?

继续分析上面那条full gc log,

1、发生full gc时,年老代内存已经占用了99.98%了(1048397/1048576)。看起来因为年老代满了而触发的FullGC了。

2、full gc回收了年老代大约302M的垃圾,回收后年老代占用70.4%(738282/1048576)。这占用率还是比较高的。

1、首先jmap简单打印一下所有对象的信息。发现有ClassPathList和ClassClassPath两个类的对象数量高达1000多万,并且这两个数量是一样的。仿佛嗅到了内存泄漏的味道。

2、只依靠对象统计信息,不足以定位问题,需要使用完整HeapDump,通过MAT进一步分析

jmap把完整堆heapDump下来

隔一段时间后,继续jmap,这次只取存活对象的dump(实际效果是先执行一次FULL GC)

可以看到,经过Full GC后,ClassPathList对象没有被回收,数量反而继续增加。到这里,基本可以确定,ClassPathList是存在泄漏了。

那么,ClassPathList究竟被谁引用着,导致回收不掉呢?

通过MAT的OQL过滤出老生代的ClassPathList对象,从对象的关联关系上继续深入分析。

首先需要知道老生代的地址区间,可以使用vjtools

通过vjmap的address命令,快速打印各代地址。

可以得知,oldGen的下界是0x80000000,上界是0xc0000000(注意OQL中使用时要把数值前的那串0去掉)。

执行OQL只查询年老代中的ClassPathList对象:执行OQL只查询年老代中的ClassPathList对象:

抽取其中一个对象分析,可以发现这个ClassPathList对象被一连串不同ClassPathList对象的next属性引用着。看起来是个链表的结构

再看看GCRoot,发现是被AppClassLoader也就是我们的应用类加载器引用着。除非这个加载器卸载了,否则ClassPathList对象是不会被GC掉了。

分析到这里,似乎离真相越来越近了。到底这个ClassPathList在项目中哪里使用到了?

通过前面的分析知道了ClassPathList的整体引用关系链:

AppClassLoader ->ClassPool类的defaultPool字段 ->ClassPoolTail类的source字段 ->ClassPathList类的pathList

可以看到,ClassPathList有两个属性,一个是next,结合之前MAT的分析,ClassPathList的确就是一个链表的结构。随着时间的增长,ClassPathList不断新增,链表也随之变得越来越大,最后内存占用逐渐上升。

另一个path字段属于ClassPath类型,ClassPath是个接口,查看它的实现类,发现一个似曾相识的名称ClassClassPath,之前分析对象统计信息时,还有一个类的对象数量是和ClassPathList一样的,正是这个ClassClassPath。每新增一个ClassPathList,都会伴随着新增对应的ClassPath对象,这也解释了为什么两者数量是一致的了。

通过注释知道,这个ClassClassPath的作用大概就是,利用一个叫ClassPool的对象,可以调用其insertClassPath方法来新增一个ClassClassPath对象,insertClassPath方法内部通过头插法将ClassClassPath添加到ClassPathList链表,从而形成一个search-path,然后通过这个search-path能够获取到某一个Class类的信息。

于是尝试着搜了一下,看看项目中有没有调用到insertClassPath方法的地方。意外发现一个类,

这不就是我们项目用来打印方法入参、执行耗时、上报metrics的@AutoLog的实现类吗。

可以看到getParams方法中调用了insertClassPath,注解@AutoLog的printParams默认为true,也就是每次调用都需要打印方法入参,每次打印前都要调用getParams先获取参数名称。因此每次都会insertClassPath,从而导致ClassPathList链表越来越大。

至此,内存泄漏的元凶已经找到。解决方法也就简单了。

因为目标只是想得到方法的参数名称,通过JoinPoint其实能直接获取到,因此可以改成JoinPoint获取的方式。

为了进行对比,分别在修改前后各进行一次压测。压测JVM参数大致与线上一致,为了尽快看到效果,只是调小了heap的大小。-Xms200m -Xmx200m

ClassPathList数量不断增长

年老代每次能回收的垃圾越来越少,每次回收过后的剩余空间也越来越小。最终整个年老代被撑满

虽然还没触发OOM,但是CPU负载飙高,从基本都在处于频繁的FULLGC状态

ClassPathList已经被消灭掉了

FullGC也趋于规律化了。每次回收的垃圾大致都相同

第一种方式是在启动参数增加 -XX:+PrintHeapAtGC,每次GC都打印地址

第二种方式是使用vjmap的命令,在-old, -sur, -address 中,都会打印出该区间的地址

第三种方式,使用vjmap的address命令,快速打印各代地址,不会造成过长时间停顿。

附: 我们服务的JVM参数

对于每一个java进程来说都有自己的内存池和使用空间,而这也就意味着会出现内存使用错误等问题,而这时候我们就需要对java内存进行诊断分析,今天辽宁java培训http://www.kmbdqn.cn/就一起来了就一下,在进行内存诊断上都有哪些软件可以使用。

Java堆:分析诊断数据堆转储分析堆转储可以使用如下的工具进行分析:EclipseMAT(内存分析工具,MemoryAnalyzerTool)是一个社区开发的分析堆转储的工具。

它提供了一些很棒的特性,包括:可疑的泄漏点:它能探测堆转储中可疑的泄露点,报告持续占有大量内存的对象直方图:列出每个类的对象数量、浅大小(shallow)以及这些对象所持有的堆。

直方图中的对象可以很容易地使用正则表达式进行排序和过滤。

这样有助于放大并集中我们怀疑存在泄露的对象。

它还能够对比两个堆转储的直方图,展示每个类在实例数量方面的差异。

这样能够帮助我们查找Java堆中增长快的对象,并进一步探查确定在堆中持有这些对象的根不可达的对象:MAT有一个非常棒的功能,那就是它允许在它的工作集对象中包含或排除不可达/死对象。

如果你不想查看不可达的对象,也就是那些会在下一次GC周期中收集掉的对象,只关心可达的对象,那么这个特性是非常便利的重复的类:展现由多个类加载器所加载的重复的类到GC根的路径:能够展示到GC根(JVM本身保持存活的对象)的引用链,这些GC根负责持有堆中的对象OQL:我们可以使用对象查询语言(ObjectQueryLanguage)来探查堆转储中的对象。

它丰富了OQL的基础设施,能够编写复杂的查询,帮助我们深入了解转储的内部。

JavaVisualVM:监控、分析和排查Java语言的一站式工具。

它可以作为JDK工具的一部分来使用,也可以从GitHub上下载。

它所提供的特性之一就是堆转储分析。

它能够为正在监控的应用创建堆转储,也可以加载和解析它们。

从堆转储中,它可以展现类的直方图、类的实例,也能查找特定实例的GC根jhat命令工具(在/bin文件夹中)提供了堆转储分析的功能,它能够在任意的浏览器中展现堆转储中的对象。

默认情况下,Web服务器会在7000端口启动。

jhat支持范围广泛的预定义查询和对象查询语言,以便于探查堆转储中的对象Java任务控制(JavaMissionControl)的JOverflow插件:这是一个实验性的插件,能够让Java任务控制执行简单的堆转储分析并报告哪里可能存在内存浪费Yourkit是一个商业的Javaprofiler,它有一个堆转储分析器,具备其他工具所提供的几乎所有特性。

除此之外,YourKit还提供了:可达性的范围(reachabilityscope):它不仅能够列出可达和不可达的对象,还能按照它们的可达性范围显示它们的分布,也就是,强可达、弱/软可达或不可达内存探查:YourKit内置了一组全面的查询,而不是使用ad-hoc查询功能,YourKit的查询能够探查内存,查找反模式并为常见的内存问题分析产生原因和提供解决方案。

JConsole

JConsole 图形用户界面是一种符合 Java 管理扩展(JMX)规范的监视工具。JConsole 使用 Java 虚拟机 (Java VM) 的广泛检测来提供有关在 Java 平台上运行的应用程序的性能和资源消耗的信息。

使用方法 本地

使用jconsole命令:监视本地运行的所有 Java 应用程序,JConsole 可以连接到这些应用程序。

使用jconsole PID命令:监视指定PID的Java应用程序。

获取java PID的方法:通过任务管理器查看、通过Java提供的jps命令查看。远程

使用jsconsole hostName:portNum命令:hostName是运行应用程序的系统的名称,portNum是您在启动Java VM时启用 JMX 代理时指定的端口号。

使用service:jmx::命令:使用 JMX 服务 URL 进行连接。

内容分析

将 JConsole 连接到应用程序后,JConsole 由六个选项卡组成。

概述:显示有关 Java VM 和受监视值的概述信息。

内存:显示有关内存使用的信息。

线程:显示有关线程使用的信息。

类:显示有关类加载的信息。

VM:显示有关 Java VM 的信息。

MBeans:显示有关 MBeans 的信息。

组成部分 概览

显示有关 CPU 使用情况、内存使用情况、线程计数和在Java VM中加载的类的图形监视信息。

提供执行GC的操作,可以随时点击按钮进行垃圾回收

伊甸园空间(堆):最初为大多数对象分配内存的池。

幸存者空间(堆):包含在伊甸园空间垃圾回收中幸存下来的物体的池。

终身代(堆):包含在幸存者空间中存在一段时间的对象的池。

永久生成(非堆):包含虚拟机本身的所有反射数据的池,如类和方法对象。使用类数据共享的 Java VM,这一代分为只读和读写区域。

代码缓存(非堆):HotSpotJava VM 还包括一个代码缓存,其中包含用于编译和存储本机代码的内存。

堆和非堆内存

Java VM管理两种类型的内存:堆内存和非堆内存,这两种内存都是在 Java VM 启动时创建的。

堆内存是Java VM为所有类实例和数组分配内存的运行时数据区域。堆的大小可能是固定的或可变的。垃圾回收器是一个自动内存管理系统,用于回收对象的堆内存。

非堆内存包括所有线程之间共享的方法区域和Java VM的内部处理或优化所需的内存。它存储每类结构,如运行时常量池、字段和方法数据,以及方法和构造函数的代码。方法区域在逻辑上是堆的一部分,但是,根据实现,Java VM 可能不会对它进行垃圾回收或压缩。与堆内存一样,方法区域可能为固定大小或可变大小。方法区域的内存不需要连续。

内存池和内存管理器

内存池和内存管理器是Java VM内存系统的关键方面。

内存池表示Java VM管理的内存区域。Java VM至少有一个内存池,它可能会在执行期间创建或删除内存池。内存池可以属于堆内存或非堆内存。

内存管理器管理一个或多个内存池。垃圾回收器是一种内存管理器,负责回收不可到达的对象使用的内存。Java VM可能具有一个或多个内存管理器。它可以在执行期间添加或删除内存管理器。内存池可以由多个内存管理器管理。

垃圾回收

垃圾回收 (GC) 是Java VM释放不再引用的对象占用的内存的方式。通常认为具有活动引用为"活动"且未引用(或无法访问)对象的对象为"已死"。垃圾回收是释放死对象使用的内存的过程。GC 使用的算法和参数对性能有显著影响。

Java hotspot VM垃圾回收器使用代数 GC。生成 GC 利用大多数程序符合以下概括的观察。

它们创建许多寿命较短的对象,例如迭代器和局部变量。

它们创建一些寿命很长的对象,例如高级持久对象。

线程

提供有关线程使用的信息。

查找监视器死锁线程:检测对象监视器锁上是否有任何线程死锁。此操作返回死锁线程指示的数组。

getThreadInfo:返回线程信息。这包括线程当前被阻止的名称、堆栈跟踪和监视器锁(如果有)以及持有该锁的线程以及线程争用统计信息。

获取ThreadCpu时间:返回给定线程消耗的 CPU 时间

显示有关类加载的信息。

提供有关Java VM的信息。

以通用方式显示有关在平台 MBean 服务器注册的所有 MBeans 的信息。MBeans 选项卡允许您访问平台 MXBean 检测的完整集,包括在其他选项卡中不可见的仪器。此外,您还可以使用 MBeans 选项卡监视和管理应用程序的 MBeans。

列出目标系统上已检测的 Java 虚拟机 (JVM)。

监视 Java 虚拟机 (JVM) 统计信息。

对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控。

命令格式

jstat [-option] [PID]

option参数

class:显示有关类加载器行为的统计信息。

compiler:显示有关Java HotSpot VM实时编译器行为的统计信息。

gc:显示有关垃圾回收堆行为的统计信息。

gccapacity:显示有关几代人容量及其相应空间的统计信息。

gccause:显示有关垃圾回收统计信息(与 相同)的摘要,以及最后和当前(如果适用)垃圾回收事件的原因。-gcutil

gcnew:显示新一代行为的统计信息。

gcnewcapacity:显示有关新一代大小及其相应空间的统计信息。

gcold:显示有关旧一代和元空间统计信息行为的统计信息。

gcoldcapacity:显示有关旧一代大小的统计信息。

gcmetacapacity:显示有关元空间大小的统计信息。

gcutil:显示有关垃圾回收统计信息的摘要。

printcompilation:显示 Java 热点 VM 编译方法统计信息。

1.jstat –class: 显示加载class的数量,及所占空间等信息。

2.jstat -compiler显示VM实时编译的数量等信息。

3.jstat -gc: 可以显示gc的信息,查看gc的次数,及时间。

4.jstat -gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小

5.jstat -gcutil:统计gc信息

6.jstat -gcnew:年轻代对象的信息。

7.jstat -gcnewcapacity: 年轻代对象的信息及其占用量。

8.jstat -gcold:old代对象的信息。

9.jstat -gcoldcapacity: old代对象的信息及其占用量。

10.jstat -gcpermcapacity: perm对象的信息及其占用量。

11.jstat -printcompilation:当前VM执行的信息。

监视 Java 虚拟机 (JVM),并使远程监视工具能够连接到 JVM

命令格式

jstatd -[option]

option

-nr当找不到现有的RMI注册表时,不尝试使用jstatd进程创建一个内部的RMI注册表。

-p port在指定的端口查找RMI注册表。如果没有找到,并且没有指定-nr选项,则在该端口自行创建一个内部的RMI注册表。

-n rminameRMI注册表中绑定的RMI远程对象的名称。默认的名称为JStatRemoteHost。如果多个jstatd服务器在同一主机上运行,你可以通过指定该选项来让每个服务器导出的RMI对象具有唯一的名称。不管如何,这样做需要将唯一的服务器名称包含进监控客户端的hostid和vmid字符串中。

-Joption将选项参数传递给被javac调用的java启动程序。例如,-J-Xms48m设置启动内存为48 MB。使用-J将选项参数传递给执行Java应用程序的底层虚拟机,这是一种常见惯例。

使用方法

1.在jdk的bin目录下创建文件jstatd.all.policy

2.写入下面的安全配置

grant codebase "file:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.191.b12-1.el7_6.x86_64/lib/tools.jar" {

permission java.security.AllPermission

#此处写绝对路径,主要是防止路径错误问题,排查问题,应该写成相对路径

3.启动jstatd

./jstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=x.x.x.x &

4.使用jvisualvm工具远程连接,进行监控

jvisualvm

VisualVM,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来的).

同时他还提供很多插件可以自己安装,是一款不错的监控分析工具。

故障排除工具 JInfo

可以用来查看正在运行的 java 应用程序的扩展参数,包括Java System属性和JVM命令行参数;也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息

命令格式

参数说明

pid对应jvm的进程id

executable core产生core dump文件

[server-id@]remote server IP or hostname远程的ip或者hostname,server-id标记服务的唯一性id

option

no option输出全部的参数和系统属性

-flag name输出对应名称的参数

-flag [+|-]name开启或者关闭对应名称的参数

-flag name=value设定对应名称的参数

-flags输出全部的参数

-sysprops输出系统属性

Javacore 概述

Javacore,也可以称为“threaddump”或是“javadump”,它是 Java 提供的一种诊断特性,能够提供一份可读的当前运行的 JVM 中线程使用情况的快照。即在某个特定时刻,JVM 中有哪些线程在运行,每个线程执行到哪一个类,哪一个方法。应用程序如果出现不可恢复的错误或是内存泄露,就会自动触发 Javacore 的生成。

使用方法

1.jinfo pid:输出当前 jvm 进程的全部参数和系统属性

2.jinfo -flag name pid:输出对应名称的参数使用该命令,可以查看指定的 jvm 参数的值。如:查看当前 jvm 进程是否开启打印 GC 日志。

3.jinfo -flag [+|-]name pid:开启或者关闭对应名称的参数

使用 jinfo 可以在不重启虚拟机的情况下,可以动态的修改 jvm 的参数。尤其在线上的环境特别有用。

4.jinfo -flag name=value pid:修改指定参数的值。

注意:jinfo虽然可以在java程序运行时动态地修改虚拟机参数,但并不是所有的参数都支持动态修改

5.jinfo -flags pid:输出全部的参数

6.jinfo -sysprops pid:输出当前 jvm 进行的全部的系统属性

jhat

主要是用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询语言。

1.使用jmap命令导出堆文件jmap -dump:live,file=a.log pid

也可以使用下面方式导出堆文件

1、使用jconsole选项通过HotSpotDiagnosticMXBean从运行时获得堆转储(生成dump文件)、

2、虚拟机启动时如果指定了-XX:+HeapDumpOnOutOfMemoryError选项, 则在抛出OutOfMemoryError时, 会自动执行堆转储。

3、使用hprof命令

2.使用jhat分析堆文件jhat -J-Xmx512M a1.log

3.查看分析的html页面

http://ip:7000/jhat中的OQL(对象查询语言)

如果需要根据某些条件来过滤或查询堆的对象,这是可能的,可以在jhat的html页面中执行OQL,来查询符合条件的对象

基本语法:

select

[from [instanceof] ]

[where ]

解释:

(1)class name是java类的完全限定名,如:java.lang.String,java.util.ArrayList, C是char数组,java.io.File是java.io.File[]

(2)类的完全限定名不足以唯一的辨识一个类,因为不同的ClassLoader载入的相同的类,它们在jvm中是不同类型的

(3)instanceof表示也查询某一个类的子类,如果不明确instanceof,则只精确查询class name指定的类

(4)from和where子句都是可选的

(5)java域表示:obj.field_name;java数组表示:array[index]

举例:

(1)查询长度大于100的字符串

select s from java.lang.String s where s.count >100

(2)查询长度大于256的数组

select a from [I a where a.length >256

(3)显示匹配某一正则表达式的字符串

select a.value.toString() from java.lang.String s where /java/(s.value.toString())

(4)显示所有文件对象的文件路径

select file.path.value.toString() from java.io.File file

(5)显示所有ClassLoader的类名

select classof(cl).name from instanceof java.lang.ClassLoader cl

(6)通过引用查询对象

select o from instanceof 0xd404d404 o

built-in对象 -- heap

(1)heap.findClass(class name) -- 找到类

select heap.findClass("java.lang.String").superclass

(2)heap.findObject(object id) -- 找到对象

select heap.findObject("0xd404d404")

(3)heap.classes -- 所有类的枚举

select heap.classes

(4)heap.objects -- 所有对象的枚举

select heap.objects("java.lang.String")

(5)heap.finalizables -- 等待垃圾收集的java对象的枚举

(6)heap.livepaths -- 某一对象存活路径

select heaplivepaths(s) from java.lang.String s

(7)heap.roots -- 堆根集的枚举

辨识对象的函数

(1)classof(class name) -- 返回java对象的类对象

select classof(cl).name from instanceof java.lang.ClassLoader cl

(2)identical(object1,object2) -- 返回是否两个对象是同一个实例

select identical(heap.findClass("java.lang.String").name, heap.findClass("java.lang.String").name)

(3)objectid(object) -- 返回对象的id

select objectid(s) from java.lang.String s

(4)reachables -- 返回可从对象可到达的对象

select reachables(p) from java.util.Properties p -- 查询从Properties对象可到达的对象

select reachables(u, "java.net.URL.handler") from java.net.URL u -- 查询从URL对象可到达的对象,但不包括从URL.handler可到达的对象

(5)referrers(object) -- 返回引用某一对象的对象

select referrers(s) from java.lang.String s where s.count >100

(6)referees(object) -- 返回某一对象引用的对象

select referees(s) from java.lang.String s where s.count >100

(7)refers(object1,object2) -- 返回是否第一个对象引用第二个对象

select refers(heap.findObject("0xd4d4d4d4"),heap.findObject("0xe4e4e4e4"))

(8)root(object) -- 返回是否对象是根集的成员

select root(heap.findObject("0xd4d4d4d4"))

(9)sizeof(object) -- 返回对象的大小

select sizeof(o) from [I o

(10)toHtml(object) -- 返回对象的html格式

select "+ toHtml(o) + "" from java.lang.Object o

(11)选择多值

select {name:t.name?t.name.toString():"null",thread:t} from instanceof java.lang.Thread t

数组、迭代器等函数

(1)concat(enumeration1,enumeration2) -- 将数组或枚举进行连接

select concat(referrers(p),referrers(p)) from java.util.Properties p

(2)contains(array, expression) -- 数组中元素是否满足某表达式

select p from java.util.Properties where contains(referres(p), "classof(it).name == 'java.lang.Class'")

返回由java.lang.Class引用的java.util.Properties对象

built-in变量

it -- 当前的迭代元素

index -- 当前迭代元素的索引

array -- 被迭代的数组

(3)count(array, expression) -- 满足某一条件的元素的数量

select count(heap.classes(), "/java.io./(it.name)")

(4)filter(array, expression) -- 过滤出满足某一条件的元素

select filter(heap.classes(), "/java.io./(it.name)")

(5)length(array) -- 返回数组长度

select length(heap.classes())

(6)map(array,expression) -- 根据表达式对数组中的元素进行转换映射

select map(heap.classes(),"index + '-->' + toHtml(it)")

(7)max(array,expression) -- 最大值, min(array,expression)

select max(heap.objects("java.lang.String"),"lhs.count>rhs.count")

built-in变量

lhs -- 左边元素

rhs -- 右边元素

(8)sort(array,expression) -- 排序

select sort(heap.objects('[C'),'sizeof(lhs)-sizeof(rhs)')

(9)sum(array,expression) -- 求和

select sum(heap.objects('[C'),'sizeof(it)')

(10)toArray(array) -- 返回数组

(11)unique(array) -- 唯一化数组

jmap

打印进程、核心文件或远程调试服务器的共享对象内存映射或堆内存详细信息。

jmap [option]

(to connect to running process) 连接到正在运行的进程

jmap [option]

(to connect to a core file) 连接到核心文件

jmap [option] [server_id@]

(to connect to remote debug server) 连接到远程调试服务

option

pid:目标进程的PID,进程编号,可以采用ps -ef | grep java查看java进程的PID

executable:产生core dump的java可执行程序

core:将被打印信息的core dump文件

remote-hostname-or-IP:远程debug服务的主机名或ip

server-id:唯一id,假如一台主机上多个远程debug服务

使用方法

jmap -dump:[live,]format=b,file= PID:使用hprof二进制形式,输出jvm的heap内容到文件

jmap -finalizerinfo PID:打印正等候回收的对象的信息

jmap -heap PID:打印heap的概要信息,GC使用的算法,heap(堆)的配置及JVM堆内存的使用情况。

jmap -histo:live PID:打印每个class的实例数目,内存占用,类全名信息。VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量.

jmap -permstat PID:打印classload和jvm heap长久层的信息. 包含每个classloader的名字、活泼性、地址、父classloader和加载的class数量。另外,内部String的数量和占用内存数也会打印出来。

-F强迫.在pid没有相应的时候使用-dump或者-histo参数。在这个模式下,live子参数无效。

-h | -help打印辅助信息

-J传递参数给jmap启动的jvm.

jstack

jstack命令主要用于调试java程序运行过程中的线程堆栈信息,可以用于检测死锁,进程耗用cpu过高报警问题的排查。jstack命令会打印出所有的线程,包括用户自己启动的线程和jvm后台线程。

命令格式

jstack -[option] pid

option

-F强制dump线程堆栈信息. 用于进程hung住,jstack命令没有响应的情况

-m同时打印java和本地(native)线程栈信息,m是mixed mode的简写

-l打印锁的额外信

作者:楚瑞涛 https://blog.csdn.net/cong____cong/article/details/106349866

公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

最近有很多人问,有没有读者交流群!加入方式很简单,公众号Java精选,回复“加群”,即可入群!

(微信小程序):3000+道面试题,包含Java基础、并发、JVM、线程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架构设计等,在线随时刷题!

------ 特别推荐 ------

特别推荐:专注分享最前沿的技术与资讯,为弯道超车做好准备及各种开源项目与高效率软件的公众号,「大咖笔记」,专注挖掘好东西,非常值得大家关注。点击下方公众号卡片关注。

文章有帮助的话,在看,转发吧!


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/467330.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-06-05
下一篇2023-06-05

发表评论

登录后才能评论

评论列表(0条)

    保存