offset:文件偏移量 。进行映射的文件位置,从文件起始地址向后的位移量 。
下面总结一下 MappedByteBuffer 的特点和不足之处:
- MappedByteBuffer 使用是堆外的虚拟内存,因此分配(map)的内存大小不受 JVM 的 -Xmx 参数限制,但是也是有大小限制的 。
- 如果当文件超出 Integer.MAX_VALUE 字节限制时,可以通过 position 参数重新 map 文件后面的内容 。
- MappedByteBuffer 在处理大文件时性能的确很高,但也存在内存占用、文件关闭不确定等问题,被其打开的文件只有在垃圾回收的才会被关闭,而且这个时间点是不确定的 。
- MappedByteBuffer 提供了文件映射内存的 mmap() 方法,也提供了释放映射内存的 unmap() 方法 。然而 unmap() 是 FileChannelImpl 中的私有方法,无法直接显示调用 。
- public static void clean(final Object buffer) throws Exception {
- AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
- try {
- Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
- getCleanerMethod.setAccessible(true);
- Cleaner cleaner = (Cleaner) getCleanerMethod.invoke(buffer, new Object[0]);
- cleaner.clean();
- } catch(Exception e) {
- e.printStackTrace();
- }
- });
- }
DirectByteBuffer 的对象引用位于 Java 内存模型的堆里面,JVM 可以对 DirectByteBuffer 的对象进行内存分配和回收管理 。
一般使用 DirectByteBuffer 的静态方法 allocateDirect() 创建 DirectByteBuffer 实例并分配内存 。
- public static ByteBuffer allocateDirect(int capacity) {
- return new DirectByteBuffer(capacity);
- }
- 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;
- }
- 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);
- }
- }
说了这么多,那么 DirectByteBuffer 和零拷贝有什么关系?前面有提到在 MappedByteBuffer 进行内存映射时,它的 map() 方法会通过 Util.newMappedByteBuffer() 来创建一个缓冲区实例 。
初始化的代码如下:
- static MappedByteBuffer newMappedByteBuffer(int size, long addr, FileDescriptor fd,
- Runnable unmapper) {
- MappedByteBuffer dbb;
- if (directByteBufferConstructor == null)
- initDBBConstructor();
- try {
- dbb = (MappedByteBuffer)directByteBufferConstructor.newInstance(
- new Object[] { new Integer(size), new Long(addr), fd, unmapper });
- } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
推荐阅读
- 淘宝从百万到千万级并发的14次服务端架构演进之路
- 几百万扔进水?买二手房千万避开这几类房源
- 分布式、高并发、多线程,这些概念还傻傻分不清吗?
- Java 并发编程:如何保证共享变量的原子性?
- 硬核!如何模拟 5w+ 的并发用户?
- 格鲁吉亚中国茶王刘峻周诞辰150周年之际获百万赔偿
- 安徽省科技厅强化科技支撑推进精准扶贫
- PHP导出百万条数据方法
- 重金寻人 , 你是我们要找的百万英雄吗 内含福利
- 带你深入了解高并发架构
