热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JVM学习笔记:深入解析DirectByteBuffer与堆外内存

本文将探讨Java虚拟机(JVM)中堆内存与堆外内存的区别,以及DirectByteBuffer在堆外内存管理中的作用。通过具体示例和内部机制分析,帮助读者理解Java进程中不同类型的内存使用情况。

在Java虚拟机(JVM)中,内存管理是一个复杂但至关重要的主题。本文将重点讨论堆内存与堆外内存的区别,特别是DirectByteBuffer在堆外内存管理中的应用。


堆内存与堆外内存

Java应用程序运行时,JVM为其分配了一定的内存空间,这些内存可以大致分为两类:堆内存(Heap Memory)和堆外内存(Off-Heap Memory)。

堆内存主要用于存储对象实例,是垃圾回收器的主要工作区域。而堆外内存则位于JVM堆之外,主要用于存储一些需要频繁访问的数据,如网络缓冲区等,这部分内存不受JVM垃圾回收机制的直接管理。

Java内存模型

从上图可以看出,Java进程的内存占用包括了堆内存(A1)、直接内存(B1)和其他原生代码分配的内存(B2)。其中,直接内存通常由DirectByteBuffer类管理,而其他原生代码分配的内存则可能涉及更底层的操作系统调用。


DirectByteBuffer详解

DirectByteBuffer是Java NIO包中一个非常重要的类,它允许程序直接在堆外分配内存,从而提高数据处理的效率,尤其是在网络通信和文件操作中。

使用示例

下面是一个简单的DirectByteBuffer使用示例:

public static void main(String[] args) throws Exception {
// 分配128字节的直接内存
ByteBuffer buffer = ByteBuffer.allocateDirect(128);
// 写入数据
buffer.put("写入到直接内存".getBytes(Charset.forName("utf-8")));
// 切换到读模式
buffer.flip();
// 读取数据
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
System.out.println(new String(bytes, Charset.forName("utf-8")));
System.gc(); // 建议手动触发垃圾回收,但不是必须的
}

内部机制

DirectByteBuffer的核心在于其构造函数,以下是构造函数的关键部分:

DirectByteBuffer(int cap) {
super(-1, 0, cap, cap);
boolean pa = VM.isDirectMemoryPageAligned(); // 是否开启内存页对齐
int ps = Bits.pageSize(); // 获取内存页大小
long size = Math.max(1L, (long)cap + (pa ? ps : 0)); // 计算实际分配的内存大小
Bits.reserveMemory(size, cap); // 控制直接内存的使用量
long base = 0;
try {
base = unsafe.allocateMemory(size); // 分配内存
} catch (OutOfMemoryError x) {
Bits.unreserveMemory(size, cap);
throw x;
}
unsafe.setMemory(base, size, (byte) 0); // 初始化内存
if (pa && (base % ps != 0)) {
// 如果启用了内存页对齐,则调整基地址
address = base + ps - (base & (ps - 1));
} else {
address = base;
}
cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); // 创建清理器
att = null;
}

主要步骤包括:

  1. 通过unsafe.allocateMemory(size)方法分配指定大小的内存。这是一个本地方法,通过JNI调用操作系统的内存分配函数(如malloc)。
  2. 创建Cleaner对象,用于在对象不再被引用时自动释放内存。

Cleaner类继承自PhantomReference,当对象被垃圾回收器回收时,Cleanerclean方法会被调用,进而执行Deallocatorrun方法来释放内存。

private static class Deallocator implements Runnable {
private static Unsafe unsafe = Unsafe.getUnsafe();
private long address;
private long size;
private int capacity;

private Deallocator(long address, long size, int capacity) {
assert (address != 0);
this.address = address;
this.size = size;
this.capacity = capacity;
}

public void run() {
if (address == 0) {
return;
}
unsafe.freeMemory(address); // 释放内存
address = 0;
Bits.unreserveMemory(size, capacity); // 更新统计信息
}
}

参考资料


推荐阅读
author-avatar
拥有一YY_373
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有