Netty 零拷贝(一)NIO 对零拷贝的支持
Netty 零拷贝(二)NIO 对零拷贝的支持
Netty 之美系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html)
相关文章:

- 非直接缓冲区(HeapByteBuffer):在 JVM 内存上分配一个字节数组 byte[] hb
- 直接缓冲区(DirectByteBuffer):保存一个指向系统内核的地址 long address
一、非直接缓冲区和直接缓冲区
(1) Buffer 分配
// 分配非直接缓冲区
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}
// 分配直接缓冲区
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
(2) ByteBuffer 内存存储
public abstract class Buffer {
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
// 直接缓冲区指向系统内核的一个地址,之所以放到父类中是为了加快 JNI 的访问速度
long address;
}
public abstract class ByteBuffer extends Buffer {
// 非直接缓冲区在 JVM 内存上分配一个字节数组
final byte[] hb;
}
(3) 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
}
public byte get() {
return ((unsafe.getByte(ix(nextGetIndex()))));
}
public ByteBuffer put(byte x) {
unsafe.putByte(ix(nextPutIndex()), ((x)));
return this;
}
DirectByteBuffer 对直接缓冲区的操作都委托给了类 sun.misc.Unsafe,Unsafe 都是一些本地方法 native。
public final class Unsafe {
public native long allocateMemory(long bytes);
public native byte getByte(long address);
public native void putByte(long address, byte x);
}
二、直接缓冲区应用
使用直接缓冲区可以避免用户空间和系统空间之间的拷贝过程,即零拷贝。
FileChannel inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("3.png"),
StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
// 方式一:内存映射文件,直接缓冲区
MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
//只有 READ_WRITE,没有 WRITE,因此 outChannel 也要加上 READ
MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
byte[] bytes = new byte[inMappedBuf.limit()];
inMappedBuf.get(bytes);
outMappedBuf.put(bytes);
// 方式二:transferTo 也是使用直接缓冲区
//inChannel.transferTo(0, inChannel.size(), outChannel);
//outChannel.transferFrom(inChannel, 0, inChannel.size());

三、DirectByteBuffer
Java NIO中的 direct buffer(主要是 DirectByteBuffer)其实是分两部分的:
Java | native
|
DirectByteBuffer | malloc'd
[ address ] -+-> [ data ]
其中 DirectByteBuffer 自身是一个 Java 对象,在 Java 堆中;而这个对象中有个 long 类型字段 address,记录着一块调用 malloc() 申请到的 native memory。
FileChannel 的 read(ByteBuffer dst) 函数,write(ByteBuffer src) 函数中,如果传入的参数是 HeapBuffer 类型,则会临时申请一块 DirectBuffer,进行数据拷贝,而不是直接进行数据传输,这是出于什么原因?
// OpenJDK 的 sun.nio.ch.IOUtil
static int write(FileDescriptor fd, ByteBuffer src, long position,
NativeDispatcher nd) throws IOException {
if (src instanceof DirectBuffer)
return writeFromNativeBuffer(fd, src, position, nd);
// Substitute a native buffer
int pos = src.position();
int lim = src.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
try {
bb.put(src);
bb.flip();
// Do not update src until we see how many bytes were written
src.position(pos);
int n = writeFromNativeBuffer(fd, bb, position, nd);
if (n > 0) {
// now update src
src.position(pos + n);
}
return n;
} finally {
Util.offerFirstTemporaryDirectBuffer(bb);
}
}
这里其实是在迁就 OpenJDK 里的 HotSpot VM 的一点实现细节。
HotSpot VM 里的 GC 除了 CMS 之外都是要移动对象的,是所谓 “compacting GC”。
如果要把一个 Java 里的 byte[] 对象的引用传给 native 代码,让 native 代码直接访问数组的内容的话,就必须要保证 native 代码在访问的时候这个 byte[] 对象不能被移动,也就是要被“pin”(钉)住。
可惜 HotSpot VM 出于一些取舍而决定不实现单个对象层面的 object pinning,要 pin 的话就得暂时禁用 GC ——也就等于把整个 Java 堆都给 pin 住。HotSpot VM 对 JNI 的 Critical 系 API 就是这样实现的。这用起来就不那么顺手。
所以 Oracle/Sun JDK / OpenJDK 的这个地方就用了点绕弯的做法。 它假设把 HeapByteBuffer 背后的 byte[] 里的内容拷贝一次是一个时间开销可以接受的操作,同时假设真正的 I/O 可能是一个很慢的操作。
于是它就先把 HeapByteBuffer 背后的 byte[] 的内容拷贝到一个 DirectByteBuffer 背后的 native memory 去,这个拷贝会涉及 sun.misc.Unsafe.copyMemory() 的调用,背后是类似 memcpy() 的实现。这个操作本质上是会在整个拷贝过程中暂时不允许发生 GC 的,虽然实现方式跟 JNI 的 Critical 系 API 不太一样。(具体来说是 Unsafe.copyMemory() 是 HotSpot VM 的一个 intrinsic 方法,中间没有 safepoint 所以 GC 无法发生)。
然后数据被拷贝到 native memory 之后就好办了,就去做真正的 I/O,把 DirectByteBuffer 背后的 native memory 地址传给真正做 I/O 的函数。这边就不需要再去访问 Java 对象去读写要做 I/O 的数据了。
参考:
- 《Java NIO中,关于DirectBuffer,HeapBuffer的疑问?》:https://www.zhihu.com/question/57374068/answer/152691891
每天用心记录一点点。内容也许不重要,但习惯很重要!
Netty 零拷贝(一)NIO 对零拷贝的支持的更多相关文章
- Netty 零拷贝(三)Netty 对零拷贝的改进
Netty 零拷贝(三)Netty 对零拷贝的改进 Netty 系列目录 (https://www.cnblogs.com/binarylei/p/10117436.html) Netty 的&quo ...
- Netty 零拷贝(一)Linux 零拷贝
Netty 零拷贝(一)Linux 零拷贝 本文探讨 Linux 中主要的几种零拷贝技术以及零拷贝技术适用的场景. 一.几个重要的概念 1.1 用户空间与内核空间 操作系统的核心是内核,独立于普通的应 ...
- Netty学习笔记(一)——nio基础
Netty简单认识: 1) Netty 是由JBOSS 提供的一个Java 开源框架. 2) Netty 是一个异步的.基于事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络I0 程序. 3) ...
- c++的默认构造函数 VS 深拷贝(值拷贝) 与 浅拷贝(位拷贝)
C++默认为类生成了四个缺省函数: A(void); // 缺省的无参数构造函数 A(const A &a); // 缺省的拷贝构造函数 ~A(void); // 缺省的析构函数 A & ...
- C++ //构造函数调用规则 //1.创建一个类,C++编译器会给每个类添加至少3个函数 //默认构造(空实现) //析构函数(空实现) //拷贝函数(值拷贝) //2.如果我们写了有参构造函数 编译器就不会提供默认构造函数 但是会提供拷贝构造函数 //3.如果我们写了拷贝函数 编译器就不再提供 默认 有参 构造函数
//构造函数调用规则 #include <iostream> using namespace std; //1.创建一个类,C++编译器会给每个类添加至少3个函数 //默认构造(空实现) ...
- NIO学习笔记,从Linux IO演化模型到Netty—— Netty零拷贝
Netty的中零拷贝与上述零拷贝是不一样的,它并不是系统层面上的零拷贝,只是相对于ByteBuf而言的,更多的是偏向于数据操作优化这样的概念. Netty中的零拷贝: 1.CompositeByteB ...
- Java后端进阶-网络编程(Netty零拷贝机制)
package com.study.hc.net.netty.demo; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled ...
- java IO Nio 文件拷贝工具类Files
public static void main(String[] args) throws Exception { Files.copy(Paths.get("file/text.txt&q ...
- 文件拷贝io nio比较
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedRead ...
随机推荐
- wzben的QQ空间
实习之后没有动过博客了,后续慢慢补.
- leetcode942
public class Solution { public int[] DiStringMatch(string S) { var len = S.Length; ; var max = len; ...
- leetcode292
public class Solution { public bool CanWinNim(int n) { //如果要赢,最后3个必须是自己来拿 //也就是最后剩1,2,3是胜利, //如果剩4枚, ...
- Windows 域用户
Windows 2000 组及说明 分类: Windows 2000 的组分为Security 和 Distribution 两种. Security 类型是Windows 2000 唯一用于赋予权限 ...
- 可视化库-seaborn-布局风格设置(第五天)
1. sns.set_style() 进行风格设置, sns.set() 进行设置的重置, 五种风格 # 1.darkgrid# 2.whitegrid# 3.dark# 4.white# 5 tic ...
- python 1 面向对象基础知识
1.编码范式 编程 是程序员用特定的 语法+数据结构+算法 组成的代码来告诉计算机如何执行任务的过程 如果把编程比作习武,编程方式就是武林中的各种流派,而在编程的世界里面最常见的两大流派是:面向过程 ...
- VBox 安装 macOS 10.12
安装步骤⑴ 下载及解压 macOS 10.12 Sierra Final by TechReviews.rar ⑵ 下载及双击安装 VirtualBox-5.1.6-110634-Win.exe ,默 ...
- Hystrix-超时设置
由于客户端请求服务端方法时,服务端方法响应超过1秒将会触发降级,所以我们可以配置Hystrix默认的超时配置 如果我们没有配置默认的超时时间,Hystrix将取default_executionTim ...
- SQL2008R2 express版本不支持维护计划
SQL2008R2 express版本不支持维护计划
- (转)游戏引擎中三大及时光照渲染方法介绍(以unity3d为例)
重要:在目前市面上常见的游戏引擎中,主要采用以下三种灯光实现方式: 顶点照明渲染路径细节 Vertex Lit Rendering Path Details 正向渲染路径细节 Forward Rend ...