一、Heap 堆
- 含义:主要将
存储对象实例
到内存
中, 为 对象实例开辟内存空间。 - 作用:解决的是
数据存储问题
。(即数据怎么放、放哪儿) - 是否共享:
线程(数据)共享
,存在线程安全问题
。
二、Heap 堆内存详细结构
堆内存组成部分主要由:
Young Gen 新生代
- 主要存储
新的对象
,到该区域中,为该对象开辟空间。
eden 区
- 是新生代内存中的
最大的一块空间
,主要用于存储刚刚创建的对象
。 - 由于大部分对象在很短的时间内就会变成垃圾,Eden 区采用了
特定的垃圾回收策略
,以便更快地清理无用对象
,以节省内存空间
。
Survivor区
分为2大区域:
Survivor0区(from区)
Survivor1区(to区)
。- 作用: 用于存
放在Eden区
和另一个Survivor区
之间进行对象复制的存活对象
。
例如:
第一次 Minor GC(新生代垃圾回收) -> A: Eden区中
存活的对象
-> 会被移动 S0(from)区第二次 Minor GC(新生代垃圾回收) -> B: 将前一次GC时存放存活对象 S0(from)区 -> 复制到 S1(to)区 和 另一个Survivor区
这样来回
复制的过程
在不断重复,直到达到一定的阈值或者年龄后
-> 对象会被晋升到老年代。
Old Gen 老年代
- 主要存储
老的对象(达到垃圾回收阈值,最后存活下来对象)
到该区域中,为该对象开辟空间。
Metaspace 元空间
含义:主要存放类的信息
。
- 类的信息主要包含:
类的信息
、局部变量表
、方法信息
、常量池
。 - 线程安全问题:线程(数据)共享,存在线程安全问题。
三、Heap 堆内存-产生问题
内存泄漏(Memory Leak)
是指程序中 已动态分配的堆内存
,由于某种原因程序 未释放
或 无法释放
,造成系统 内存的浪费
,导致 程序运行速度减慢
甚至 系统崩溃
等严重后果。
例如: 在 JAVA 中创建过多的对象, 由于 GC 无法回收这些垃圾对象,导致内存浪费
,导致程序运行速度的减慢或最终导致系统崩溃
。
java
List list = new ArrayList<>(10);
for (int i = 0; i < 100 ; i++) {
HeapMemoryOutOfExample.User user = new HeapMemoryOutOfExample.User();
list.add(user);
user = null;
}
try {
System.gc();
Thread.sleep(100000);
} catch (Exception e) {
e.printStackTrace();
}
内存溢出(Memory Overflow)
- 在
无法回收的内存
或使用的内存过多
,最终使得程序运行要用到的内存
大于能提供的最大内存
。 - 此时程序就运行不了,系统会提示内存溢出,有时候会自动关闭软件,重启电脑或者软件后释放掉一部分内存又可以正常运行该软件。
- 而由
系统配置、数据流、用户代码等原因
而导致的内存溢出错误,即使用户重新执行任务
依然无法避免。
例如:在 JAVA 中程序设置最大内存为 8MB, 但由于创建对象在堆内存中超过了程序最大内存 8MB, 就会引发内存溢出。
- 如下代码:
java
/**
* 案例一: 递归深度长 -> 导致内存溢出
*/
public static void recursionDeepLong() {
List list = new ArrayList<>(0);
while (true) {
list.add(new User());
}
}
- 运行结果:
java.lang.OutOfMemoryError: Java heap space (堆内存溢出)
log
# 运行结果: java.lang.OutOfMemoryError: Java heap space (堆内存溢出)
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:267)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233)
at java.util.ArrayList.add(ArrayList.java:464)
at com.calvin.jvm.structure.example.HeapMemoryOutOfExample.main(HeapMemoryOutOfExample.java:35)
java
/**
* 用户
*
* @author calvin
* @date 2023/06/28
*/
public static class User {
/**
* 指定对象创建为1MB
*/
private byte[] bytes = new byte[1024 * 1024];
}
/**
* 案例二: 对象设置指定内存,超过设置内存 -> 导致内存溢出
*/
public static void moreThanHeapSize() {
List list = new ArrayList<>(0);
int count = 0;
try {
while (true) {
list.add(new User());
count++;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("HeapMemoryOutOfExample.moreThanHeapSize: 执行次数" + count);
}
}
- 启动程序,设置程序最大内存为
2MB
shell
$ java -Xmx2m 程序名称
- 输出结果
log
# 运行结果: java.lang.OutOfMemoryError: Java heap space (堆内存溢出)
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.calvin.jvm.structure.example.HeapMemoryOutOfExample$User.<init>(HeapMemoryOutOfExample.java:25)
at com.calvin.jvm.structure.example.HeapMemoryOutOfExample.moreThanHeapSize(HeapMemoryOutOfExample.java:63)
at com.calvin.jvm.structure.example.HeapMemoryOutOfExample.main(HeapMemoryOutOfExample.java:82)
HeapMemoryOutOfExample.moreThanHeapSize: 执行次数6
java
/**
* 案例三: 一次性查询MySQL大数据表,超过设置内存, 导致内存溢出
*/
private static void findBigDataByMySQL() {
// 伪代码,大数据
// List<User> bigDataUsers = listByUser();
// 使用该对象进行业务逻辑处理
}