您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Java堆棧內(nèi)存、堆外內(nèi)存、零拷貝的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
堆棧內(nèi)存,顧名思義,指的是堆內(nèi)存以及棧內(nèi)存,其中,堆內(nèi)存是由Java GC進(jìn)行管理的內(nèi)存區(qū)域,而棧內(nèi)存則是線程內(nèi)存。關(guān)于棧內(nèi)存,這里不去細(xì)說。以Hotspot為例,堆內(nèi)存的簡要結(jié)構(gòu)如下圖所示:
而堆棧的關(guān)系,我們可以通過一行簡單的代碼來理解:
public static void main(String[] args) { Object o = new Object(); }
上述代碼主要完成了兩件事,new Object( ) 在堆上開辟了一塊內(nèi)存,也就是說,new Object( )是分配在堆上的;而變量o,則是在線程main的棧上面的,它指向了new Object( ) 開辟的堆內(nèi)存地址。簡單來說,程序中創(chuàng)建的對象,都存儲在堆內(nèi)存中,棧內(nèi)存包含對它的引用。
簡單來說,除了堆棧內(nèi)存,剩下的就都是堆外內(nèi)存了(當(dāng)然,這是從Java運(yùn)行時(shí)內(nèi)存的角度來看),堆外內(nèi)存直接受操作系統(tǒng)管理,而不是虛擬機(jī)。而使用堆外內(nèi)存的原因,主要有幾點(diǎn):
一定程度上減少了GC,堆外內(nèi)存是直接受操作系統(tǒng)管理的,而不是JVM,因此使用堆外內(nèi)存的話,就可以保持一個(gè)比較小的堆內(nèi)內(nèi)存,減少垃圾回收對程序性能的影響。這一塊,在Kafka中就應(yīng)用得很好,感興趣的同學(xué)可以去了解一下;
還有一個(gè)更大的優(yōu)點(diǎn),就是提高IO操作的效率!這里就涉及用戶態(tài)與內(nèi)核態(tài),以及內(nèi)核緩沖區(qū)的概念,具體可以看筆者之前的一篇文章Java隨筆記 - 內(nèi)核緩沖區(qū)與進(jìn)程緩沖區(qū)。其中,堆內(nèi)內(nèi)存其實(shí)就是用戶進(jìn)程的進(jìn)程緩沖區(qū),屬于用戶態(tài),而堆外內(nèi)存由操作系統(tǒng)管理,屬于內(nèi)核態(tài)。如果從堆內(nèi)向磁盤寫數(shù)據(jù),數(shù)據(jù)會被先復(fù)制到堆外內(nèi)存,即內(nèi)核緩沖區(qū),然后再由OS寫入磁盤,但使用堆外內(nèi)存的話則可以避免這個(gè)復(fù)制操作。
總結(jié)上述內(nèi)容中對堆棧內(nèi)存與堆外內(nèi)存的說明,主要解決了兩個(gè)疑問:“零拷貝”是從哪拷貝到哪?“零拷貝”是怎么優(yōu)化掉這一拷貝操作的?
用戶進(jìn)程需要像磁盤寫數(shù)據(jù)時(shí),需要將用戶緩沖區(qū)(堆內(nèi)內(nèi)存)中的內(nèi)容拷貝到內(nèi)核緩沖區(qū)(堆外內(nèi)存)中,操作系統(tǒng)再將內(nèi)核緩沖區(qū)中的內(nèi)容寫進(jìn)磁盤中;
通過在用戶進(jìn)程中,直接申請堆外內(nèi)存,存儲其需要寫進(jìn)磁盤的數(shù)據(jù),就能夠省掉上述拷貝操作。
在Java中,提供了一些使用堆外內(nèi)存以及DMA的方法,能夠在很大程度上優(yōu)化用戶進(jìn)程的IO效率。這里,給出一份拷貝文件的代碼,分別使用BIO、NIO和使用堆外內(nèi)存的NIO進(jìn)行文件復(fù)制,簡單對比其耗時(shí)。
這里我使用一個(gè)200MB左右的pdf文件進(jìn)行拷貝操作,你可以另外指定更大的文件,文件越大對比越明顯。這里我運(yùn)行出來的延時(shí),BIO的平均耗時(shí)1500ms上下,NIO耗時(shí)120ms左右, 使用堆外內(nèi)存的NIO耗時(shí)100ms上下。
package top.jiangnanmax.nio; import java.io.*; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; /** * @author jiangnanmax * @email jiangnanmax@gmail.com * @description CopyCompare * @date 2021/5/7 **/ public class CopyCompare { public static void main(String[] args) throws Exception { String inputFile = "/tmp/nio/input/HyperLedger.pdf"; String outputFile = "/tmp/nio/output/HyperLedger.pdf"; long start = System.currentTimeMillis(); nioCopyByDirectMem(inputFile, outputFile); long end = System.currentTimeMillis(); System.out.println("cost time: " + (end - start) + " ms"); deleteFile(outputFile); } /** * 使用傳統(tǒng)IO進(jìn)行文件復(fù)制 * * 平均耗時(shí) 15** ms * * @param sourcePath * @param destPath */ private static void bioCopy(String sourcePath, String destPath) throws Exception { File sourceFile = new File(sourcePath); File destFile = new File(destPath); if (!destFile.exists()) { destFile.createNewFile(); } FileInputStream inputStream = new FileInputStream(sourceFile); FileOutputStream outputStream = new FileOutputStream(destFile); byte[] buffer = new byte[512]; int lenRead; while ((lenRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, lenRead); } inputStream.close(); outputStream.close(); } /** * 使用NIO進(jìn)行文件復(fù)制,但不使用堆外內(nèi)存 * * 平均耗時(shí) 1** ms, 比BIO直接快了一個(gè)數(shù)量級??? * * @param sourcePath * @param destPath */ private static void nioCopy(String sourcePath, String destPath) throws Exception { File sourceFile = new File(sourcePath); File destFile = new File(destPath); if (!destFile.exists()) { destFile.createNewFile(); } FileInputStream inputStream = new FileInputStream(sourceFile); FileOutputStream outputStream = new FileOutputStream(destFile); FileChannel inputChannel = inputStream.getChannel(); FileChannel outputChannel = outputStream.getChannel(); // transferFrom底層調(diào)用的應(yīng)該是sendfile // 直接在兩個(gè)文件描述符之間進(jìn)行了數(shù)據(jù)傳輸 // DMA outputChannel.transferFrom(inputChannel, 0, inputChannel.size()); inputChannel.close(); outputChannel.close(); inputStream.close(); outputStream.close(); } /** * 使用NIO進(jìn)行文件復(fù)制,并使用堆外內(nèi)存 * * 平均耗時(shí)100ms上下,比沒使用堆外內(nèi)存的NIO快一點(diǎn) * * @param sourcePath * @param destPath */ private static void nioCopyByDirectMem(String sourcePath, String destPath) throws Exception { File sourceFile = new File(sourcePath); File destFile = new File(destPath); if (!destFile.exists()) { destFile.createNewFile(); } FileInputStream inputStream = new FileInputStream(sourceFile); FileOutputStream outputStream = new FileOutputStream(destFile); FileChannel inputChannel = inputStream.getChannel(); FileChannel outputChannel = outputStream.getChannel(); MappedByteBuffer buffer = inputChannel.map(FileChannel.MapMode.READ_ONLY, 0, inputChannel.size()); outputChannel.write(buffer); inputChannel.close(); outputChannel.close(); inputStream.close(); outputStream.close(); } /** * 刪除目標(biāo)文件 * * @param target */ private static void deleteFile(String target) { File file = new File(target); file.delete(); } }
Java的基本數(shù)據(jù)類型分為:1、整數(shù)類型,用來表示整數(shù)的數(shù)據(jù)類型。2、浮點(diǎn)類型,用來表示小數(shù)的數(shù)據(jù)類型。3、字符類型,字符類型的關(guān)鍵字是“char”。4、布爾類型,是表示邏輯值的基本數(shù)據(jù)類型。
關(guān)于“Java堆棧內(nèi)存、堆外內(nèi)存、零拷貝的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯(cuò),請把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。