共计 2798 个字符,预计需要花费 7 分钟才能阅读完成。
一、说明
当虚拟机申请不到内存空间的时候,会报堆内存溢出: OutOfMemoryError:java heap space。
常见的原因:http://outofmemory.cn/c/java-…
我测试到时候,运行在 16G 内存的机器上。JVM 堆内存 默认为物理内存的 1 /4,即 16 * 1/4 = 4G
JDK 8 的 JVM 在 JDK 7 的基础上从堆内存中移除了 永久代(Perm Generation),替换为了堆内存之外的 元空间(Metaspace),元空间是堆外直接内存,不受堆内存的限制,只受物理内存的限制,可以提供更大的空间。
二、原因及解决办法
OutOfMemoryError 异常的常见原因:
- 加载的数据过大。如:加载的文件或者图片过大、一次从数据库取出过多数据
- 代码存在死循环或循环产生过多的对象
解决方法
- 增加 jvm 的内存大小,使用 -Xmx 和 -Xms 来设置
- 检查代码中是否有死循环或递归调用。
- 检查是否有大循环重复产生新对象实体。
- 检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
- 检查 List、Map 等集合对象是否有使用完后,未清除的问题。List、MAP 等集合对象会始终存有对对象的引用,使得这些对象不能被 GC 回收。
三、代码示例
/**java 堆溢出实例
* 原理:java 的堆是用来存放对象实例的,所以我们只要做到以下三点就可以使堆溢出:* 1、限制堆的大小,不可扩展
* 2、不断新建对象
* 3、保持对象存活不被回收
* 对应的,我们需要:* 1、改变 JVM 的启动参数,将堆的最小值和最大值设成一样,这样就可以避免堆自动扩展(其实不一样也可以)* 2、不断产生对象
* 3、使用一个 List 来保存对象,保持对象存活
*
* JVM 配置参数:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*
*/
public class HeapOom {public static void main(String[] args) {
// 此 list 实例会存放在堆内存中
List<byte[]> list = new ArrayList<>();
int i = 0;
boolean flag = true;
while (flag) {
try {
i++;
// 每次增加一个 1M 大小的数组对象
list.add(new byte[1024 * 1024]);
} catch (Throwable e) { // catch 捕获的是 Throwable,而不是 Exception。因为 OutOfMemoryError 不属于 Exception 的子类。e.printStackTrace();
flag = false;
// 记录次数
System.out.println("count=" + i);
}
}
// 不让进程结束,便于使用分析工具来查看内存情况
try {Thread.sleep(24 * 60 * 60 * 1000);
} catch (InterruptedException e) {e.printStackTrace();
}
}
}
使用的 java 1.8.0_171 版本:
$ java -version
java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)
报错信息:
count=3316
java.lang.OutOfMemoryError: Java heap space
at com.song.HeapOom.main(HeapOom.java:21)
运行结果表明,运行到 3316 次时,出现了 堆内存溢出。由于每次增加 1M 内存。粗略估计:程序大约使用 3G 内存时,出现了内存溢出情况。
通过 jmap 或 VisualVM 者工具可以查看内存情况:最后可以看出,老年代内存使用情况,大约使用了 2709MB,使用率近 100%。从而导致了 OutOfMemoryError
TIPS:下面查看内存时,由于使用工具查看内存的时间不是同一时间,所以内存使用量有细微差别
1. 使用 jmap 查看的内存信息
$ jmap -heap 3428
Attaching to process ID 3428, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.66-b18
using thread-local object allocation.
Parallel GC with 4 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 4261412864 (4064.0MB)
NewSize = 88604672 (84.5MB)
MaxNewSize = 1420296192 (1354.5MB)
OldSize = 177733632 (169.5MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 637009920 (607.5MB)
used = 637009920 (607.5MB)
free = 0 (0.0MB)
100.0% used
From Space:
capacity = 11010048 (10.5MB)
used = 0 (0.0MB)
free = 11010048 (10.5MB)
0.0% used
To Space:
capacity = 11010048 (10.5MB)
used = 0 (0.0MB)
free = 11010048 (10.5MB)
0.0% used
PS Old Generation // 老年代内存使用情况,大约使用了 2709MB,使用率近 100%(导致 OutOfMemoryError)capacity = 2841116672 (2709.5MB)
used = 2840991320 (2709.38045501709MB)
free = 125352 (0.11954498291015625MB)
99.99558793198338% used
4955 interned Strings occupying 422328 bytes.
2. 使用 VisualVM 查看的内存信息
正文完