一、JVM监控及排查分析命令详解
1、jps
JVM Process Status Tool,用于显示指定系统内所有的HotSpot虚拟机进程。
usage: jps [options] [hostid] #hostid可以省略
options:
-l 显示pid和应用程序main class的完整包名或者应用程序的jar路径
-q 只显示 pid
-m 显示pid、应用程序main class类名和传递给main方法的参数,在嵌入式jvm上可能是null
-v 显示pid、应用程序main class类名和传递给jvm的参数
-V 默认,显示pid和应用程序main class类名
2、jstat
JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译(Just In Time Compiler, 即时编译器)等运行数据。
usage: jstat [option] [-t] [-h<lines>] LVMID [interval] [count]
[interval]: 连续输出的时间间隔
[count]: 输出的次数
options:
-class 显示有关类加载器行为的统计信息
-compiler 显示有关Java HotSpot VM即时编译器行为的统计信息。
-gc 显示有关垃圾收集堆行为的统计信息。
-gccapacity 显示各个垃圾回收代容量及其对应空间的统计信息。
-gccause 显示有关垃圾收集统计信息的摘要(与 -gcutil 相同),以及最后和当前(如果适用)垃圾收集事件的原因。
-gcnew 显示新生代行为的统计信息。
-gcnewcapacity 显示有关新生代及其对应空间大小的统计信息。
-gcold 显示有关老年代行为的统计信息和元空间统计信息。
-gcoldcapacity 显示有关老年代大小的统计信息。
-gcmetacapacity 显示有关元空间大小的统计信息。
-gcutil 显示有关垃圾收集统计信息的摘要。
-printcompilation 显示Java HotSpot VM编译方法统计信息。
-t:将时间戳列显示为输出的第一列。时间戳是自目标JVM启动时间以来的时间。
-h n:每n个样本(输出行)显示一个列标题,其中n是一个正整数。默认值为0,即显示列标题的第一行数据。
-
-class
标题字段含义Loaded:加载class的数量
Bytes:加载class占用大小(KB)
Unloaded:未加载class的数量
Bytes:未加载class占用大小(KB)
Time:执行加载class和卸载class操作所花费的时间
-
-compiler标题字段含义
Compiled:执行的编译任务数
Failed:执行失败的编译任务数
Invalid:无效的编译任务数
Time:执行编译任务所花费的时间
FailedType:上次编译失败的编译类型
FailedMethod:上次失败编译的类名和方法
-
-gc标题字段含义
S0C:survivor0空间当前容量(KB)
S1C:survivor1空间当前容量(KB)
S0U:survivor0空间利用容量(KB)
S1U:survivor1空间利用容量(KB)
EC:Eden空间当前容量(KB)
EU:Eden空间利用容量(KB)
OC:Old空间当前容量(KB)
OU:Old空间利用容量(KB)
MC:元空间容量(KB)
MU:元空间利用容量(KB)
CCSC:压缩类空间容量(KB)
CCSU:压缩类空间已使用容量(KB)
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收时间
FGC:full gc次数
FGCT:full gc耗时
GCT:总垃圾回收时间
-
-gccapacity标题字段含义
NGCMN:最小new generation容量(KB),近似年轻代
NGCMX:最大new generation容量(KB),近似年轻代
NGC:new generation容量(KB),近似年轻代
OGCMN:最小老年代容量(KB)
OGCMX:最大老年代容量(KB)
OGC:老年代容量(KB)
MCMN:最小元空间容量(KB)
MCMX:最大元空间容量(KB)
CCSMN:压缩类空间最小容量(KB)
CCSMX:压缩类空间最大容量(KB)
-
-gccause标题字段含义
S0:survivor0空间利用容量占当前容量的百分比
S1:survivor1空间利用容量占当前容量的百分比
E:Eden空间利用容量占当前容量的百分比
O:Old空间利用容量占当前容量的百分比
M:元空间利用容量占当前容量的百分比
CCS:压缩类空间利用率百分比
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收时间
FGC:full gc次数
FGCT:full gc耗时
GCT:总垃圾回收时间
LGCC:上次垃圾回收的原因
GCC:当前垃圾回收的原因
-
-gcutil标题字段含义
与
-gccause
标题字段含义一致 -
-gcnew标题字段含义
TT:晋升阈值
MTT:最大晋升阈值
DSS:所需survivor空间大小(KB)
-
-gcnewcapacity标题字段含义
S0CMX:最大survivor0空间容量(KB)
S1CMX:最大survivor1空间容量(KB)
ECMX:最大Eden空间容量(KB)
-
-gcold标题字段含义
该字段含义在上文中都有
-
-gcoldcapacity标题字段含义
该字段含义在上文中都有
-
-gcmetacapacity标题字段含义
该字段含义在上文中都有
-
-printcompilation标题字段含义
Compiled:最近执行的编译方法任务数
Size:最近编译方法的字节码的数量
Type:最近编译的方法的编译类型
Method:标识最近编译的方法的类名和方法名。方法名是指定类中的方法。
3、jmap
打印进程、核心文件或远程调试服务器的共享对象内存映射或堆内存详细信息。
除此命令外,通过指定-XX:+HeapDumpOnOutOfMemoryError
参数,在发生OutOfMemoryError
时生成堆dump文件。或者使用hprof命令也可以生成。
usage: jmap [ options ] pid
jmap [ options ] executable core
jmap [ options ] [ pid ] server-id@ ] remote-hostname-or-IP
executable:生成可执行的核心转储文件
core:打印内存映射的核心文件
remote-hostname-or-IP:远程调试服务器主机名或IP地址
server-id:当多个调试服务器在同一远程主机上运行时使用的可选唯一ID
options:
<none> 如果不使用任何选项,jmap命令会打印共享对象映射。对于目标JVM中加载的每个共享对象,都会打印共享对象文件的起始地址、映射大小和完整路径
-dump:[live,] format=b, file=filename
以hprof二进制格式生成堆转储文件。live子选项是可选的,当指定时,仅转储堆中的活动对象。要浏览堆转储文件,可以使用jhat命令读取。
-finalizerinfo 打印有关等待回收的对象的信息
-heap 打印堆的概要信息,包括GC、head configuration、generation-wise heap usage等
-histo[:live] 打印堆的对象统计,包括对象数、内存大小等等
-clstats 打印堆的类加载器统计信息。
-F 强制。当pid没有反应时,将这个选项与jmap -dump或jmap -histo选项一起使用。在这种模式下不支持live子选项。
-Jflag 将flag传递给运行jmap命令的Java虚拟机。
description:
jmap命令打印指定进程、核心文件或远程调试服务器的共享对象内存图或堆内存细节。
如果指定的进程在64位Java虚拟机(JVM)上运行,那么你可能需要指定-J-d64选项,例如:jmap -J-d64 -heap pid。
-
示例
<!== 打印等待回收对象的信息 ==>
$ jmap -finalizerinfo 30809 Attaching to process ID 30809, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.251-b08 Number of objects pending for finalization: 0
<!== 打印堆概要信息 ==>
$ jmap -heap 30809 Attaching to process ID 30809, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.251-b08 using thread-local object allocation. Parallel GC with 4 thread(s) //GC方式 Heap Configuration: //堆内存初始化配置,对应jvm启动参数 MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 1073741824 (1024.0MB) NewSize = 357564416 (341.0MB) MaxNewSize = 357564416 (341.0MB) OldSize = 716177408 (683.0MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 268435456 (256.0MB) CompressedClassSpaceSize = 528482304 (504.0MB) MaxMetaspaceSize = 536870912 (512.0MB) G1HeapRegionSize = 0 (0.0MB) Heap Usage: //堆内存使用情况 PS Young Generation //年轻代使用情况 Eden Space: //Eden区内存情况 capacity = 353370112 (337.0MB) used = 198190016 (189.00872802734375MB) free = 155180096 (147.99127197265625MB) 56.08567597250556% used From Space: //其中一个Survivor区的内存情况 capacity = 2097152 (2.0MB) used = 1006928 (0.9602813720703125MB) free = 1090224 (1.0397186279296875MB) 48.014068603515625% used To Space: //其中一个Survivor区的内存情况 capacity = 2097152 (2.0MB) used = 0 (0.0MB) free = 2097152 (2.0MB) 0.0% used PS Old Generation //老年代内存情况 capacity = 716177408 (683.0MB) used = 62305128 (59.418800354003906MB) free = 653872280 (623.5811996459961MB) 8.699677943485199% used 37015 interned Strings occupying 4020168 bytes.
<!== 打印堆的对象统计 ==>
$ jmap -F -histo 30809 Attaching to process ID 30809, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.251-b08 Iterating over heap. This may take a while... Object Histogram: num #instances #bytes Class description -------------------------------------------------------------------------- 1: 183560 22797768 char[] 2: 1395 4765360 long[] 3: 15811 4545944 byte[] 4: 181047 4345128 java.lang.String 5: 41490 3651120 java.lang.reflect.Method 6: 23943 3640280 int[] 7: 107354 3435328 java.util.concurrent.ConcurrentHashMap$Node 8: 49566 3015984 java.lang.Object[] 9: 18664 2068768 java.lang.Class 10: 34349 1373960 java.util.LinkedHashMap$Entry ... Total : 1176270 67035968 Heap traversal took 47.908 seconds.
<!== 创建堆dump文件 ==>
$ jmap -dump:format=b,file=/tmp/cus-operation-server.hprof 30809 Attaching to process ID 30809, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.251-b08 Dumping heap to /tmp/cus-operation-server.hprof ... Heap dump file created
<!== 使用jhat命令分析堆dump文件 ==>
$ jhat -J-Xmx2048m cus-operation-server.hprof Reading from cus-operation-server.hprof... Dump file created Fri Apr 22 11:34:22 CST 2022 Snapshot read, resolving... Resolving 5178821 objects... WARNING: Class c31ed980 not found, adding fake class! WARNING: Class c1feb088 not found, adding fake class! WARNING: Class c03bda70 not found, adding fake class! WARNING: Class c03bdaf0 not found, adding fake class! WARNING: Class c0847af0 not found, adding fake class! WARNING: Class c324d1b8 not found, adding fake class! WARNING: Class c03bc090 not found, adding fake class! WARNING: Class c03bc110 not found, adding fake class! WARNING: Class c03bc190 not found, adding fake class! WARNING: Failed to resolve object id 0xc08089f0 for field clazz (signature L) WARNING: Failed to resolve object id 0xc0808750 for field clazz (signature L) WARNING: Failed to resolve object id 0xc08083c8 for field clazz (signature L) WARNING: Failed to resolve object id 0xc0808128 for field clazz (signature L) WARNING: Failed to resolve object id 0xc0807e90 for field clazz (signature L) Chasing references, expect 1035 dots........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... Eliminating duplicate references........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... Snapshot resolved. Started HTTP server on port 7000 Server is ready.
登录web页面使用OQL进行查询分析
4、jstack
打印 Java 进程、核心文件或远程调试服务器的 Java 线程堆栈跟踪。
usage: jstack [ options ] pid
jstack [ options ] executable core
jstack [ options ] [ server-id@ ] remote-hostname-or-IP
executable:生成可执行的核心转储文件
core:打印内存映射的核心文件
remote-hostname-or-IP:远程调试服务器主机名或IP地址
server-id:当多个调试服务器在同一远程主机上运行时使用的可选唯一ID
options:
-F 当jstack [-l] pid没有反应时,强制进行堆栈转储
-l 打印关于锁的额外信息
-m 打印一个混合模式的堆栈跟踪,其中有Java和本地C/C++框架,该选项不适用于远程调试服务器
description:
jstack命令打印指定的Java进程、核心文件或远程调试服务器的Java线程的堆栈痕迹。对于每个Java框架,如果有的话,会打印完整的类名、方法名、字节码索引(BCI)和行号。当指定的进程在64位Java虚拟机上运行时,你可能需要指定-J-d64选项,例如:jstack -J-d64 -m pid。
-
示例
cpu异常问题排查步骤
-
使用top命令查看程序cpu占用率过高的线程
$ top -Hp <pid> #结果保存截图
-
打印线程堆栈信息到文件
$ jstack -l <pid> > <filename>
-
将步骤1中的占用cpu过高的TID转换成16进制
$ printf "%x\n" TID
在堆栈文件中搜索对应TID
-
5、jinfo
生成配置信息,实时查看和调整虚拟机运行参数。
usage: jinfo [ option ] pid
jinfo [ option ] executable core
jinfo [ option ] [ servier-id ] remote-hostname-or-IP
executable:生成可执行的核心转储文件
core:打印内存映射的核心文件
remote-hostname-or-IP:远程调试服务器主机名或IP地址
server-id:当多个调试服务器在同一远程主机上运行时使用的可选唯一ID
options:
<none> 打印命令行参数和系统属性名称-值对
-flag name 打印指定命令行参数的名称和值
-flag [+|-]name 启用或禁用指定的布尔值命令行参数
-flag name=value 将指定的命令行参数设置为指定的值
-flags 打印传递给JVM的命令行参数
-sysprops 将Java系统属性打印为名称-值对
description:
jinfo命令打印指定Java进程或核心文件或远程调试服务器的Java配置信息。配置信息包括Java系统属性和JVM命令行标志。如果指定的进程在64位JVM上运行,那么您可能需要指定-J-d64选项,例如:jinfo -J-d64 -sysprops pid。
-
示例
<!== 打印命令行参数和系统属性 ==>
$ jinfo 30809 Attaching to process ID 30809, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.251-b08 Java System Properties: com.sun.management.jmxremote.authenticate = false java.runtime.name = Java(TM) SE Runtime Environment java.vm.version = 25.251-b08 sun.boot.library.path = /data/svr/jdk1.8.0_251/jre/lib/amd64 java.protocol.handler.pkgs = org.springframework.boot.loader apollo.cluster = dev java.vendor.url = http://java.oracle.com/ java.vm.vendor = Oracle Corporation path.separator = : java.rmi.server.randomIDs = true file.encoding.pkg = sun.io java.vm.name = Java HotSpot(TM) 64-Bit Server VM sun.os.patch.level = unknown sun.java.launcher = SUN_STANDARD user.country = US ... VM Flags: Non-default VM flags: -XX:AutoBoxCacheMax=20000 -XX:CICompilerCount=3 -XX:CompressedClassSpaceSize=528482304 -XX:GCLogFileSize=1048576 -XX:+HeapDumpOnOutOfMemoryError -XX:InitialHeapSize=1073741824 -XX:+ManagementServer -XX:MaxHeapSize=1073741824 -XX:MaxMetaspaceSize=536870912 -XX:MaxNewSize=357564416 -XX:MetaspaceSize=268435456 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=357564416 -XX:NumberOfGCLogFiles=10 -XX:OldSize=716177408 -XX:+PrintGC -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseGCLogFileRotation -XX:+UseParallelGC Command line: -Xms1024m -Xmx1024m -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M -XX:+HeapDumpOnOutOfMemoryError -XX:AutoBoxCacheMax=20000 -Xloggc:/dev/shm/gc-fbg-il8n-server.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=1M -Djava.rmi.server.hostname=10.81.48.30 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=50606 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dapp.name=fbg-il8n-server -Dapp.repo=/data/local/apps/fbg-il8n-server/lib -Dapp.home=/data/local/apps/fbg-il8n-server -Dbasedir=/data/local/apps/fbg-il8n-server
<!== 打印指定命令行参数信息 ==>
#需要以程序运行用户执行 $ jinfo -flag MaxMetaspaceSize 30809 -XX:MaxMetaspaceSize=536870912
<!== 动态修改命令行参数 ==>
$ jinfo -flag MaxHeapFreeRatio=65 30809 $ jinfo -flags 30809 Attaching to process ID 30809, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.251-b08 Non-default VM flags: -XX:AutoBoxCacheMax=20000 -XX:CICompilerCount=3 -XX:CompressedClassSpaceSize=528482304 -XX:GCLogFileSize=1048576 -XX:+HeapDumpOnOutOfMemoryError -XX:InitialHeapSize=1073741824 -XX:+ManagementServer -XX:MaxHeapFreeRatio=65 -XX:MaxHeapSize=1073741824 -XX:MaxMetaspaceSize=536870912 -XX:MaxNewSize=357564416 -XX:MetaspaceSize=268435456 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=357564416 -XX:NumberOfGCLogFiles=10 -XX:OldSize=716177408 -XX:+PrintGC -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseGCLogFileRotation -XX:+UseParallelGC Command line: -Xms1024m -Xmx1024m -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=512M -XX:+HeapDumpOnOutOfMemoryError -XX:AutoBoxCacheMax=20000 -Xloggc:/dev/shm/gc-fbg-il8n-server.log -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=1M -Djava.rmi.server.hostname=10.81.48.30 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=50606 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dapp.name=fbg-il8n-server -Dapp.repo=/data/local/apps/fbg-il8n-server/lib -Dapp.home=/data/local/apps/fbg-il8n-server -Dbasedir=/data/local/apps/fbg-il8n-server #支持动态修改的参数 $ java -XX:+PrintFlagsInitial | grep manageable intx CMSAbortablePrecleanWaitMillis = 100 {manageable} intx CMSTriggerInterval = -1 {manageable} intx CMSWaitDuration = 2000 {manageable} bool HeapDumpAfterFullGC = false {manageable} bool HeapDumpBeforeFullGC = false {manageable} bool HeapDumpOnOutOfMemoryError = false {manageable} ccstr HeapDumpPath = {manageable} uintx MaxHeapFreeRatio = 70 {manageable} uintx MinHeapFreeRatio = 40 {manageable} bool PrintClassHistogram = false {manageable} bool PrintClassHistogramAfterFullGC = false {manageable} bool PrintClassHistogramBeforeFullGC = false {manageable} bool PrintConcurrentLocks = false {manageable} bool PrintGC = false {manageable} bool PrintGCDateStamps = false {manageable} bool PrintGCDetails = false {manageable} bool PrintGCID = false {manageable} bool PrintGCTimeStamps = false {manageable}
二、常见JVM内存错误问题排查
1、Java heap space
当堆内存(Heap Space)没有足够空间存放新创建的对象时,就会抛出java.lang.OutOfMemoryError: Java heap space
错误。
-
原因分析
请求创建一个超大对象
超出预期的访问量/数据量,通常是上游系统请求流量飙升,常见于各类促销/秒杀活动,可以结合业务流量指标排查是否有尖状峰值。
过度使用终结器(Finalizer),该对象没有立即被GC.
内存泄漏(Memory Leak),大量对象引用没有释放,JVM无法对其自动回收,常见于使用了File等资源没有回收。
-
解决方案
首先使用
jmap -dump:format=b,file=FILE_WITH_PATH PID
命令生成dump文件已变后续分析, 再通过-Xmx
参数调高 JVM 堆内存空间重启服务(临时解决)。如果是超大对象,可以检查其合理性,比如是否一次性查询了数据库全部结果,而没有做结果数限制。如果是业务峰值压力,可以考虑添加机器资源,或者做限流降级。如果是内存泄漏,需要找到持有的对象,修改代码设计,比如关闭没有释放的连接。
2、GC overhead limit exceeded
当Java进程花费98%以上的时间执行GC,但只恢复了不到2%的内存,且该动作连续重复了5次,就会抛出java.lang.OutOfMemoryError: GC overhead limit exceeded
错误。简单地说,就是应用程序已经基本耗尽了所有可用内存,GC也无法回收。
此类问题的原因与解决方案跟 Java heap space 非常类似,可以参考上文。
3、Permgen space
java.lang.OutOfMemoryError: Permgen space
该错误表示永久代(Permanent Generation)已用满。
-
原因分析
通常是因为加载的class数目太多或体积太大。PermGen的使用量与加载到内存的class的数量/大小成正相关。
-
解决方案
根据 Permgen space 报错的时机,可以采用不同的解决方案:
程序启动报错,修改
-XX:MaxPermSize
启动参数,调大永久代空间。应用重新部署时报错,很可能是没有应用没有重启,导致加载了多份 class 信息,只需重启 JVM 即可解决。
运行时报错,应用程序可能会动态创建大量class,而这些class的生命周期很短暂,但是JVM默认不会卸载class,可以设置
-XX:+CMSClassUnloadingEnabled
和-XX:+UseConcMarkSweepGC
这两个参数允许JVM卸载class。
上述方法无法解决,可以通过
jmap -dump:format=b,file=FILE_WITH_PATH PID
命令生成dump文件,然后利用Eclipse MAT软件功能逐一分析开销最大的classloader和重复class。
4、Metaspace
JDK 1.8使用Metaspace(元空间)替换了永久代(Permanent Generation),java.lang.OutOfMemoryError: Metaspace
该错误表示Metaspace已被用满。
此类问题的原因与解决方法跟Permgen space非常类似,可以参考上文。需要特别注意的是调整Metaspace空间大小的启动参数为 -XX:MaxMetaspaceSize
。
5、Unable to create new native thread
每个Java线程都需要占用一定的内存空间,当JVM向底层操作系统请求创建一个新的native线程时,如果没有足够的资源分配就会报此类错误java.lang.OutOfMemoryError: Unable to create new native thread
-
原因分析
线程数超过操作系统最大线程数ulimit限制。
线程数超过kernel.pid_max(只能重启)。
本机内存不足。
-
解决方案
升级服务器内存资源
降低 Java Heap Space 大小
修复应用程序的线程泄漏问题
限制线程池大小
使用
-Xss
参数减少线程栈的大小调高OS层面的线程最大数:使用
ulimit -u xxx
命令调整最大线程数限制。
6、Out of swap space?
java.lang.OutOfMemoryError: Out of swap space?
该错误表示所有可用的虚拟内存已被耗尽。虚拟内存(Virtual Memory)由物理内存(Physical Memory)和交换空间(Swap Space)两部分组成。当运行时程序请求的虚拟内存溢出时就会报 Out of swap space? 错误。
-
原因分析
主机虚拟内存耗尽
应用程序的本地内存泄漏(native leak)
-
解决方案
升级服务器内存资源
7、Direct buffer memory
DirectByteBuffer顾名思义直接缓冲,我们可以使用它进行堆外内存的分配/使用/回收。DirectByteBuffer晋升到老年代,必须要等到full gc 才有可能被回收。当DirectByteBuffer使用较多且存活时间过长的情况时,则可能造成堆外内存OOM出现java.lang.OutOfMemoryError: Direct buffer memory
错误
-
原因分析
通过 ByteBuffer.allocateDirect 方法使用Direct ByteBuffer,该方法是分配堆外内存,不属于GC管辖范围,由于不需要内存拷贝所以速度相对较快。但如果不断分配堆外内存,堆内存很少使用,那么JVM就不需要进行GC,DirectByteBuffer对象们就不会被回收。这个时候堆内存充足,但堆外内存可能已经耗尽。
Direct ByteBuffer使用值超过最大上限
-
解决方案
通过启动参数
-XX:MaxDirectMemorySize
调整 Direct ByteBuffer 的上限值。去掉启动参数
-XX:+DisableExplicitGC
,该参数会使 System.gc() 失效。检查堆外内存使用代码,确认是否存在内存泄漏;或者通过反射调用 sun.misc.Cleaner 的 clean() 方法来主动释放被 Direct ByteBuffer 持有的内存空间。