java heap buffer direct buffer_java NIO - DirectBuffer 和 HeapBuffer
問(wèn)題 :
DirectBuffer 屬于堆外存,那應(yīng)該還是屬于用戶內(nèi)存,而不是內(nèi)核內(nèi)存?
FileChannel 的read(ByteBuffer dst)函數(shù),write(ByteBuffer src)函數(shù)中,如果傳入的參數(shù)是HeapBuffer類(lèi)型,則會(huì)臨時(shí)申請(qǐng)一塊DirectBuffer,進(jìn)行數(shù)據(jù)拷貝,而不是直接進(jìn)行數(shù)據(jù)傳輸,這是出于什么原因?
DirectBuffer
Java | native
|
DirectByteBuffer | malloc'd
[ address ] -+-> [ data ]
|
DirectByteBuffer 自身是一個(gè)Java對(duì)象,在Java堆中;而這個(gè)對(duì)象中有個(gè)long類(lèi)型字段address,記錄著一塊調(diào)用 malloc() 申請(qǐng)到的native memory。DirectByteBuffer 自身是(Java)堆內(nèi)的,它背后真正承載數(shù)據(jù)的buffer是在(Java)堆外——native memory中的。這是 malloc() 分配出來(lái)的內(nèi)存,是用戶態(tài)的。(來(lái)自參考文章R大的回答)
DirectBuffer 和 HeapBuffer
兩個(gè)都是Buffer ,不同的是前者使用的是堆外內(nèi)存,后者時(shí)候的是 JVM 堆內(nèi)內(nèi)存。在使用 FileChannel 讀寫(xiě)的時(shí)候內(nèi)部實(shí)現(xiàn)就有點(diǎn)不同了。以下是FileChannel使用代碼
public static void main(String[] args) throws Exception{
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel channel = aFile.getChannel();
String newData = "New String to write to file..." + System.currentTimeMillis();
// HeapByteBuffer
ByteBuffer buf = ByteBuffer.allocate(48);
// DirectByteBuffer
ByteBuffer dirctBuf = ByteBuffer.allocateDirect(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
}
//讀取地址
FileInputStream fis = new FileInputStream("C:\\CloudMusic\\Circadian Eyes - Ferris Wheel.mp3");
//寫(xiě)出地址
FileOutputStream fos = new FileOutputStream("D:\\etc\\cas\\logs\\cas_audit.log");
FileChannel fc = fis.getChannel();
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
//回刷回磁盤(pán)
mbb.flip();
fos.flush();
fc.close();
fis.close();
如果上面的代碼channel.write傳入的參數(shù)是HeapBuffer類(lèi)型,則會(huì)臨時(shí)申請(qǐng)一塊DirectBuffer,將HeapBuffer中的數(shù)據(jù)進(jìn)行數(shù)據(jù)拷貝到堆外內(nèi)存,然后剩下就是對(duì)DirectBuffer進(jìn)行IO操作,為什么直接使用HeapBuffer拷貝數(shù)據(jù)到內(nèi)核中,然后進(jìn)行IO操作呢?這是因?yàn)槿绻岩粋€(gè)Java里的 byte[] 對(duì)象的引用傳給native代碼,讓native代碼直接訪問(wèn)數(shù)組的內(nèi)容的話,就必須要保證native代碼在訪問(wèn)的時(shí)候這個(gè) byte[] 對(duì)象不能被移動(dòng),也就是要被“pin”(釘)住。而虛擬機(jī)的GC 算法會(huì)移動(dòng)對(duì)象,導(dǎo)致地址會(huì)變化,那么后續(xù)就會(huì)產(chǎn)生錯(cuò)誤。詳細(xì)的見(jiàn)參考資料R大的回答。 ?OpenJDK的 sun.nio.ch.IOUtil.write(FileDescriptor fd, ByteBuffer src, long position, NativeDispatcher nd) 的實(shí)現(xiàn)。
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);
}
}
MappedByteBuffer
MappedByteBuffer 是 DirectBuffer 的父類(lèi),它的讀寫(xiě)性能比HeapByteBuffer要高(不然FileChannel 內(nèi)部實(shí)現(xiàn)中也不會(huì)用DirectByteBuffer進(jìn)行操作)。MappedByteBuffer 內(nèi)部原理主要和操作系統(tǒng)的虛擬存儲(chǔ)有關(guān),更加直接的聯(lián)系就是頁(yè)表相關(guān)的知識(shí),先閱讀以下這篇文章。
補(bǔ)充
關(guān)于 Heap memory 和 Native memory的解釋,來(lái)自stackoverflow
Heap memory: memory within the JVM process that is managed by the JVM to represent Java objects
Native memory/Off-heap: is memory allocated within the processes address space that is not within the heap.
Direct memory: is similar to native, but also implies that an underlying buffer within the hardware is being shared. For example buffer within the network adapter or graphics display. The goal here is to reduce the number of times the same bytes is being copied about in memory.
Finally, depending upon the OS then extra native allocations (assigning of the memory address space) can be carried out via Unsafe alloc and/or by memory mapping a file. Memory mapping a file is especially interesting as it can easily allocate more memory than the machine currently has as physical ram. Also note, that the total address space limit is restricted by the size of a pointer being used, a 32bit pointer cannot go outside of 4GB. Period.
參考資料
https://www.zhihu.com/question/57374068 (推薦一看)
總結(jié)
以上是生活随笔為你收集整理的java heap buffer direct buffer_java NIO - DirectBuffer 和 HeapBuffer的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: failed to open log f
- 下一篇: 恋与制作人 服务器错误,恋与制作人安装失