1、错误
执行算法性能测试时,出现Exception in thread "pool-5-thread-4" java.lang.OutOfMemoryError: Java heap space
2、分析
java.lang.OutOfMemoryError: Java heap space
在JVM中内存一共有3种:Heap(堆内存),Non-Heap(非堆内存) [3]和Native(本地内存)。
上面这个是Heap(堆内存)OutOfMemoryError。堆内存是运行时分配所有类实例和数组的一块内存区域。
网上资料参考:
Java.lang.OutOfMemoryError: Java heap space
原因:Heap内存溢出,意味着Young和Old generation的内存不够。
解决:解决方案有两种,一是优化应用,找到消耗大量内存的地方,然后优化代码或者算法。这种方式比较推荐,但是难度比较大,尤其是在产品环境中出现这种问题,开发人员不能很好的重现问题。第二种方案是提升Java heap size,这种方式虽然感觉有点治标不治本,但是可行性非常高,操作简单。
“JVM 堆空间溢出(java.lang.OutOfMemoryError: Java heap space)”错误是JVM 堆空间不足,此时只需要调整-Xms 和-Xmx 这两个参数即可。
堆内存溢出时,首先判断当前最大内存是多少(参数:-Xmx 或 -XX:MaxHeapSize=),可以通过命令 jinfo -flag MaxHeapSize 查看运行中的JVM的配置,如果该值已经较大则应通过 mat 之类的工具查找问题,或 jmap -histo查找哪个或哪些类占用了比较多的内存。参数-verbose:gc(-XX:+PrintGC) -XX:+PrintGCDetails可以打印GC相关的一些数据。如果问题比较难排查也可以通过参数-XX:+HeapDumpOnOutOfMemoryError在OOM之前Dump内存数据再进行分析。此问题也可以通过histodiff打印多次内存histogram之前的差值,有助于查看哪些类过多被实例化,如果过多被实例化的类被定位到后可以通过btrace再跟踪。
下面代码可再现该异常:
List<String> list = new ArrayList<String>();
while(true) list.add(new String("Consume more memory!"));
2.2、查看Java版本
[root@localhost ]# java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
3、这次的解决办法:
3.1、解决办法
从两个方面入手来解决:一是排查程序是否有BUG导致内存泄漏;二是调整JVM启动参数增大内存。
因为使用的数据大小是100M,所以我这里需要执行该算法时,用的是调整JVM启动参数增大内存
执行时加上:java -Xms20480m -Xmx20480m 就可以了。
3.2、-Xms和-Xmx参数说明
尽量把这两个参数的值设置称一样的,因为在运行中弹性扩容或减少容量都会带来性能损耗。
-Xms与Xmx分别是设置jvm的堆内存的初始大小和量大大小。
-Xmx2048m:等价于-xx:MaxHeapSize,设置JVM的最大堆内存为2028M。
-Xms512m:等价于-xx:InitialHeapSize,设置JVM初始堆内存为512M。
适当的调整JVM的内存大小,可以充分利用服务器资源,让程序跑得更快。
4、其他信息
4.1、查看java进程得方式
1、通过ps -ef|grep java 命令来查看
2、通过jps命令来查看所有java进程
13424 是java进程ID
17632是jps本身得进程ID
4.2、jps -l查询完整的包名
[root@localhost 1772455281197334529]# jps -l
9437 sdkPerfTest-1.0.8_2.3.8.9_algorithm20240326.jar
23356 sun.tools.jps.Jps
4.3、 jinfo -flags pid: 查看曾经赋过值的参数值
jinfo(Configuration Info for Java) 查看虚拟机配置参数信思,也可用于调整虚拟机的配置参数。
$jinfo -flags 9437
[root@localhost sdkPerTest]# jinfo -flags 9437
Attaching to process ID 10006, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.221-b11
Non-default VM flags: -XX:CICompilerCount=12 -XX:InitialHeapSize=1054867456 -XX:MaxHeapSize=16861102080 -XX:MaxNewSize=5620367360 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=351272960 -XX:OldSize=703594496 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
Command line: -Djava.ext.dirs=/opt/casb/lib/:/home/jdk/jre/lib/ext:/home/jdk/lib/ext
4.4、jinfo -flag <具体参数> pid: 查看具体参数的值
[root@localhost sdkPerTest]# jinfo -flag MaxHeapSize 9437
-XX:MaxHeapSize=16861102080
参考:jvm 性能调优工具之 jinfo命令详解-CSDN博客
参考:JVM知识梳理之四_JVM运维与调试工具 - 知乎 (zhihu.com)
4.5、jinfo -sysprops pid :查看该进程的全部配置信息
4.6、top看资源利用率
#13424是进程ID
top |grep 9437
4.7、jstat命令查看GC
jstat是用于监视JVM各种运行时的状态信息的命令行工具,包括类加载、内存、垃圾收集、即时编译等运行时数据。
$jstat -gc 2187487 500 10
# 使用jstat查看目标进程2187487 的垃圾收集状况,每500ms查看一次,共查看 10 次
# 如果不加后面两个参数,则表示只查看一次
# -gc 表示查看的是垃圾收集状况
上述-gc
表示查看垃圾收集状况,其中各列的含义如下(内存空间单位:KB,时间单位:秒):
- S0C :幸存者0区容量
- S1C :幸存者1区容量
- S0U :幸存者0区已使用大小
- S1U :幸存者1区已使用大小
- EC :eden区容量
- EU :eden区已使用大小
- OC :老年代容量
- OU :老年代已使用大小
- MC :元数据空间容量
- MU :元数据空间已使用大小
- CCSC :压缩类空间容量
- CCSU :压缩类空间已使用大小
- YGC :年轻代GC次数
- YGCT :年轻代GC耗时合计
- FGC :整堆GC次数
- FGCT :整堆GC耗时合计
- GCT :所有GC耗时合计
其中,CCS
是压缩类空间,用于对象指针与类指针压缩,属于MetaSpace元数据空间的一部分,通过-XX:+UseCompressedClassPointers
和-XX:+UseCompressedOops
开启,默认开启。
-gc
是参数选项,可以替换为以下其他选项:
4.8、jstack pid查看线程信息
jstack用于生成虚拟机当前时刻的线程快照。生成线程快照主要是为了定位长时间停顿的线程,比如线程间死锁、死循环、请求外部资源超时等等。通过jstack可以查看到各个线程的调用堆栈信息,就可以知道线程目前运行在哪一句代码,在做什么事情或者等待什么资源。
$jstack 2205220
备注:
学习JDK自带的那些工具比如jps
、jinfo
、jstat
、jstack
等。
参考:JVM知识梳理之四_JVM运维与调试工具 - 知乎 (zhihu.com)
参考:java heap space 解决方法_Java中的OutOfMemoryError的各种情况及解决和JVM内存结构