JAVA NIO知识点总结(2)——直接缓冲区和非直接缓冲区
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
? 前面我們一直說NIO能夠提高性能,那么到底如何提高效率。本篇就接著上一篇文章的緩沖區(qū),來看看直接緩沖區(qū)和非直接緩沖區(qū)。
非直接緩沖區(qū)
首先看看非直接緩沖區(qū)。我們之前說過NIO通過通道連接磁盤文件與應(yīng)用程序,通過緩沖區(qū)存取數(shù)據(jù)進(jìn)行雙向的數(shù)據(jù)傳輸。物理磁盤的存取是操作系統(tǒng)進(jìn)行管理的,與物理磁盤的數(shù)據(jù)操作需要經(jīng)過內(nèi)核地址空間;而我們的Java應(yīng)用程序是通過JVM分配的緩沖空間。有點(diǎn)雷同于一個(gè)屬于核心態(tài),一個(gè)屬于應(yīng)用態(tài)的意思,而數(shù)據(jù)需要在內(nèi)核地址空間和用戶地址空間,在操作系統(tǒng)和JVM之間進(jìn)行數(shù)據(jù)的來回拷貝,無形中增加的中間環(huán)節(jié)使得效率與后面要提的之間緩沖區(qū)相比偏低。
直接緩沖區(qū)
直接緩沖區(qū)則不再通過內(nèi)核地址空間和用戶地址空間的緩存數(shù)據(jù)的復(fù)制傳遞,而是在物理內(nèi)存中申請(qǐng)了一塊空間,這塊空間映射到內(nèi)核地址空間和用戶地址空間,應(yīng)用程序與磁盤之間的數(shù)據(jù)存取之間通過這塊直接申請(qǐng)的物理內(nèi)存進(jìn)行。
直接與非直接緩沖區(qū)的要點(diǎn)
? 字節(jié)緩沖區(qū)要么是直接的,要么是非直接的。如果為直接字節(jié)緩沖區(qū),則 Java 虛擬機(jī)會(huì)盡最大努力直接在此緩沖區(qū)上執(zhí)行本機(jī) I/O 操作。也就是說,在每次調(diào)用操作系統(tǒng)基礎(chǔ)的一個(gè)本機(jī) I/O 操作之前(或之后),虛擬機(jī)都會(huì)盡量避免將緩沖區(qū)的內(nèi)容復(fù)制到中間緩沖區(qū)中(或從中間緩沖區(qū)中復(fù)制內(nèi)容)。
? 直接字節(jié)緩沖區(qū)可以通過調(diào)用此類的 allocateDirect() 工廠方法來創(chuàng)建。此方法返回的緩沖區(qū)進(jìn)行分配和取消分配所需成本通常高于非直接緩沖區(qū)。直接緩沖區(qū)的內(nèi)容可以駐留在常規(guī)的垃圾回收堆之外,因此,它們對(duì)應(yīng)用程序的內(nèi)存需求量造成的影響可能并不明顯。所以,建議將直接緩沖區(qū)主要分配給那些易受基礎(chǔ)系統(tǒng)的本機(jī) I/O 操作影響的大型、持久的緩沖區(qū)。一般情況下,最好僅在 直接緩沖區(qū)能在程序性能方面帶來明顯好處時(shí) 分配它們。
? 直接字節(jié)緩沖區(qū)還可以通過 FileChannel 的 map() 方法 將文件區(qū)域直接映射到內(nèi)存中來創(chuàng)建。該方法返回MappedByteBuffer 。 Java 平臺(tái)的實(shí)現(xiàn)有助于通過 JNI 從本機(jī)代碼創(chuàng)建直接字節(jié)緩沖區(qū)。如果以上這些緩沖區(qū)中的某個(gè)緩沖區(qū)實(shí)例指的是不可訪問的內(nèi)存區(qū)域,則試圖訪問該區(qū)域不會(huì)更改該緩沖區(qū)的內(nèi)容,并且將會(huì)在訪問期間或稍后的某個(gè)時(shí)間導(dǎo)致拋出不確定的異常。
? 字節(jié)緩沖區(qū)是直接緩沖區(qū)還是非直接緩沖區(qū)可通過調(diào)用其 isDirect() 方法來確定。提供此方法是為了能夠在性能關(guān)鍵型代碼中執(zhí)行顯式緩沖區(qū)管理
(本文出自oschina博主happyBKs的博文:https://my.oschina.net/happyBKs/blog/1592329)
那么既然直接緩沖區(qū)的性能更高、效率更快,為什么還要存在兩種緩沖區(qū)呢?因?yàn)橹苯泳彌_區(qū)也存在著一些缺點(diǎn):
(1)不安全
(2)消耗更多,因?yàn)樗皇窃贘VM中直接開辟空間。這部分內(nèi)存的回收只能依賴于垃圾回收機(jī)制,垃圾什么時(shí)候回收不受我們控制。
(3)數(shù)據(jù)寫入物理內(nèi)存緩沖區(qū)中,程序就喪失了對(duì)這些數(shù)據(jù)的管理,即什么時(shí)候這些數(shù)據(jù)被最終寫入從磁盤只能由操作系統(tǒng)來決定,應(yīng)用程序無法再干涉。
?
選擇方法
直接緩沖區(qū)適合與數(shù)據(jù)長時(shí)間存在于內(nèi)存,或者大數(shù)據(jù)量的操作時(shí)更加適合
?
?
操作方法
@Testpublic void test3(){ByteBuffer dirBuf = ByteBuffer.allocateDirect(1024);if(dirBuf.isDirect()){System.out.println("dirBuf 是直接緩沖區(qū)");}else{System.out.println("dirBuf 是非直接緩沖區(qū)");}}這是第一種建立直接緩沖區(qū)的方法。
之后我們介紹了通道之后,我們就有了第二種建立直接緩沖區(qū)的方式——建立內(nèi)存映射文件,來建立直接緩沖區(qū)。
?
源碼簡(jiǎn)析
我們可以看看直接緩沖區(qū)申請(qǐng)的源碼:
/*** Allocates a new direct byte buffer.** <p> The new buffer's position will be zero, its limit will be its* capacity, its mark will be undefined, and each of its elements will be* initialized to zero. Whether or not it has a* {@link #hasArray backing array} is unspecified.** @param capacity* The new buffer's capacity, in bytes** @return The new byte buffer** @throws IllegalArgumentException* If the <tt>capacity</tt> is a negative integer*/public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);}我們?cè)倏碊irectByteBuffer的構(gòu)造實(shí)現(xiàn):
// Primary constructor//DirectByteBuffer(int cap) { // package-privatesuper(-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)) {// Round up to page boundaryaddress = base + ps - (base & (ps - 1));} else {address = base;}cleaner = Cleaner.create(this, new Deallocator(base, size, cap));att = null;}可以看到unsafe.allocateMemory(size);已經(jīng)不能進(jìn)入源碼了,它已經(jīng)是操作系統(tǒng)層面的jni調(diào)用了。
我們?cè)倏纯捶侵苯泳彌_區(qū)的申請(qǐng)?jiān)创a:
它申請(qǐng)的是堆空間,即在JVM上的操作。
/*** Allocates a new byte buffer.** <p> The new buffer's position will be zero, its limit will be its* capacity, its mark will be undefined, and each of its elements will be* initialized to zero. It will have a {@link #array backing array},* and its {@link #arrayOffset array offset} will be zero.** @param capacity* The new buffer's capacity, in bytes** @return The new byte buffer** @throws IllegalArgumentException* If the <tt>capacity</tt> is a negative integer*/public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw new IllegalArgumentException();return new HeapByteBuffer(capacity, capacity);}再進(jìn)一層,我們已經(jīng)可以看到byte數(shù)組了。
HeapByteBuffer(int cap, int lim) { // package-privatesuper(-1, 0, lim, cap, new byte[cap], 0);/*hb = new byte[cap];offset = 0;*/}?
?
轉(zhuǎn)載于:https://my.oschina.net/happyBKs/blog/1592329
總結(jié)
以上是生活随笔為你收集整理的JAVA NIO知识点总结(2)——直接缓冲区和非直接缓冲区的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用Sqoop将MySQL海量测试数据导
- 下一篇: LNMP部署(分享十七)