直接内存
欢迎来到我的 ChatGPT 中转站,极具性价比,为付费不方便的朋友提供便利,有需求的可以添加左侧 QQ 二维码,另外,邀请新用户能获取余额哦!最后说一句,那啥:请自觉遵守《生成式人工智能服务管理暂行办法》。
# 直接内存概述
不是虚拟机运行时数据区的一部分,也不是《Java 虚拟机规范》中定义的内存区域。
直接内存是在 Java 堆外的、直接向系统申请的内存区间。
来源于 NIO,通过存在堆中的 DirectByteBuffer 操作 Native 内存。
通常,访问直接内存的速度会优于 Java 堆。即读写性能高。
- 因此出于性能考虑,读写频繁的场合可能会考虑使用直接内存。
- Java 的 NIO 库允许 Java 程序使用直接内存,用于数据缓冲区。
# 直接内存的简单体验
/**
* IO NIO (New IO / Non-Blocking IO)
* byte[] / char[] Buffer
* Stream Channel
*
* 查看直接内存的占用与释放
*/
public class BufferTest {
private static final int BUFFER = 1024 * 1024 * 1024;//1GB
public static void main(String[] args) {
//直接分配本地内存空间
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
System.out.println("直接内存分配完毕,请求指示!");
Scanner scanner = new Scanner(System.in);
scanner.next();
System.out.println("直接内存开始释放!");
byteBuffer = null;
System.gc();
scanner.next();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 使用本地内存读写数据的测试

public class BufferTest1 {
private static final String TO = "D:\\软件设计师.zip";
private static final int _100Mb = 1024 * 1024 * 100;
public static void main(String[] args) {
long sum = 0;
String src = "D:\\软件设计师.zip";//1.8G
for (int i = 0; i < 3; i++) {
String dest = "D:\\软件设计师_" + i + ".zip";
// 传统方式
// sum += io(src,dest);//22672
// nio 方式
sum += directBuffer(src, dest);//10155
}
System.out.println("总花费的时间为:" + sum);
}
private static long directBuffer(String src, String dest) {
long start = System.currentTimeMillis();
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
inChannel = new FileInputStream(src).getChannel();
outChannel = new FileOutputStream(dest).getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
while (inChannel.read(byteBuffer) != -1) {
byteBuffer.flip();//修改为读数据模式
outChannel.write(byteBuffer);
byteBuffer.clear();//清空
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inChannel != null) {
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outChannel != null) {
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long end = System.currentTimeMillis();
return end - start;
}
private static long io(String src, String dest) {
long start = System.currentTimeMillis();
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
byte[] buffer = new byte[_100Mb];
while (true) {
int len = fis.read(buffer);
if (len == -1) {
break;
}
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long end = System.currentTimeMillis();
return end - start;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# 直接内存的 OOM 与内存大小的设置
- 也可能导致 OutOfMemoryError 异常。
- 由于直接内存在 Java 堆外,因此它的大小不会直接受限于 - Xmx 指定的最大堆大小,但是系统内存是有限的,Java 堆和直接内存的总和依然受限于操作系统能给出的最大内存。
- 缺点
- 分配回收成本较高。
- 不受 JVM 内存回收管理。
- 直接内存大小可以通过 MaxDirectMemorySize 设置。
- 如果不指定,默认与堆的最大值 - Xmx 参数值一致。
# 实例一
/**
* 本地内存的 OOM: OutOfMemoryError: Direct buffer memory
*/
public class BufferTest2 {
private static final int BUFFER = 1024 * 1024 * 20;//20MB
public static void main(String[] args) {
ArrayList<ByteBuffer> list = new ArrayList<>();
int count = 0;
try {
while (true) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
list.add(byteBuffer);
count++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
System.out.println(count);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 实例二
/**
* -Xmx20m -XX:MaxDirectMemorySize=10m
*/
public class MaxDirectMemorySizeTest {
private static final long _1MB = 1024 * 1024;
public static void main(String[] args) throws IllegalAccessException {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while (true) {
unsafe.allocateMemory(_1MB);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 总结
简单来说:java process memory = java heap + native memory。
注意
ChatGPT:这个公式并不完全正确,因为它忽略了一些 JVM 还需要的内存区域,如元空间(Metaspace)、线程栈(Thread Stack)和代码缓存(Code Cache)等。
一个更准确的公式是:
java process memory = java heap + native memory + metaspace + thread stack + code cache + ...
其中:
- java heap 是 JVM 用来存储 Java 对象的内存区域,受到 -Xms 和 -Xmx 参数的限制。
- native memory 是 JVM 用来存储本地数据的内存区域,不受到 Java 垃圾回收器的管理,可以通过 NIO、JNI 或 Unsafe 类来分配和释放。
- metaspace 是 JVM 用来存储类的元数据的内存区域,不属于堆内存,受到 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 参数的限制。
- thread stack 是 JVM 用来存储每个线程的局部变量和方法调用的内存区域,受到 -Xss 参数的限制。
- code cache 是 JVM 用来存储 JIT 编译后的机器指令的内存区域,受到 -XX:InitialCodeCacheSize 和 -XX:ReservedCodeCacheSize 参数的限制。
上次更新: 2025/04/12, 05:37:39