java heap buffer direct buffer_java NIO - DirectBuffer 和 HeapBuffer
問題 :
DirectBuffer 屬于堆外存,那應該還是屬于用戶內存,而不是內核內存?
FileChannel 的read(ByteBuffer dst)函數,write(ByteBuffer src)函數中,如果傳入的參數是HeapBuffer類型,則會臨時申請一塊DirectBuffer,進行數據拷貝,而不是直接進行數據傳輸,這是出于什么原因?
DirectBuffer
Java | native
|
DirectByteBuffer | malloc'd
[ address ] -+-> [ data ]
|
DirectByteBuffer 自身是一個Java對象,在Java堆中;而這個對象中有個long類型字段address,記錄著一塊調用 malloc() 申請到的native memory。DirectByteBuffer 自身是(Java)堆內的,它背后真正承載數據的buffer是在(Java)堆外——native memory中的。這是 malloc() 分配出來的內存,是用戶態的。(來自參考文章R大的回答)
DirectBuffer 和 HeapBuffer
兩個都是Buffer ,不同的是前者使用的是堆外內存,后者時候的是 JVM 堆內內存。在使用 FileChannel 讀寫的時候內部實現就有點不同了。以下是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");
//寫出地址
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());
//回刷回磁盤
mbb.flip();
fos.flush();
fc.close();
fis.close();
如果上面的代碼channel.write傳入的參數是HeapBuffer類型,則會臨時申請一塊DirectBuffer,將HeapBuffer中的數據進行數據拷貝到堆外內存,然后剩下就是對DirectBuffer進行IO操作,為什么直接使用HeapBuffer拷貝數據到內核中,然后進行IO操作呢?這是因為如果要把一個Java里的 byte[] 對象的引用傳給native代碼,讓native代碼直接訪問數組的內容的話,就必須要保證native代碼在訪問的時候這個 byte[] 對象不能被移動,也就是要被“pin”(釘)住。而虛擬機的GC 算法會移動對象,導致地址會變化,那么后續就會產生錯誤。詳細的見參考資料R大的回答。 ?OpenJDK的 sun.nio.ch.IOUtil.write(FileDescriptor fd, ByteBuffer src, long position, NativeDispatcher nd) 的實現。
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 的父類,它的讀寫性能比HeapByteBuffer要高(不然FileChannel 內部實現中也不會用DirectByteBuffer進行操作)。MappedByteBuffer 內部原理主要和操作系統的虛擬存儲有關,更加直接的聯系就是頁表相關的知識,先閱讀以下這篇文章。
補充
關于 Heap memory 和 Native memory的解釋,來自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 (推薦一看)
總結
以上是生活随笔為你收集整理的java heap buffer direct buffer_java NIO - DirectBuffer 和 HeapBuffer的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: failed to open log f
- 下一篇: 恋与制作人 服务器错误,恋与制作人安装失