日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

java 存储数据到文件中_本机速度文件支持的“纯” Java大数据存储

發(fā)布時(shí)間:2023/12/3 java 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 存储数据到文件中_本机速度文件支持的“纯” Java大数据存储 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

java 存儲(chǔ)數(shù)據(jù)到文件中

動(dòng)機(jī)

所有這一切始于意識(shí)到我買不起足夠大的計(jì)算機(jī)。 音頻處理需要大量的內(nèi)存。 Audacity是一款出色的免費(fèi)音頻處理器,它使用文件支持的存儲(chǔ)系統(tǒng)對(duì)其進(jìn)行管理。 這是解決此類問題的常用方法,在這些問題中,我們存儲(chǔ)大量信息并希望隨機(jī)訪問。 因此,我想為Sonic Field (我的寵物音頻處理/合成項(xiàng)目)開發(fā)一個(gè)系統(tǒng),該系統(tǒng)提供了相同的基于磁盤的強(qiáng)大存儲(chǔ)方法,但使用的是純Java。

去年下半年,我開始使用此工具,并在Java Advent日歷( http://www.javaadvent.com/2014/12/a-serpentine-path-to-music.html )概述中對(duì)此進(jìn)行了簡(jiǎn)要介紹。 。 基于磁盤的內(nèi)存使Sonic Field能夠處理音頻系統(tǒng),而這在我不起眼的16 GB筆記本電腦上需要大量?jī)?nèi)存。 例如,最近的一塊創(chuàng)建了超過50 GB的內(nèi)存:

雖然這是一個(gè)突破,但效率也很低。 諸如混合之類的內(nèi)存密集型操作是該系統(tǒng)的瓶頸。 在這里,我通過實(shí)現(xiàn)相同的系統(tǒng)將Java變成了一個(gè)存儲(chǔ)動(dòng)力庫,但效率更高得多。 我懷疑我已經(jīng)接近Java不再對(duì)C ++性能不利的極限。

去年,我對(duì)該方法進(jìn)行了概述。 今年,我將深入研究執(zhí)行績(jī)效的細(xì)節(jié)。 通過這樣做,我將說明如何消除傳統(tǒng)Java內(nèi)存訪問技術(shù)的開銷,然后擴(kuò)展思想,以更通用的方法在JVM編程中共享和持久存儲(chǔ)大內(nèi)存系統(tǒng)。

什么是分段存儲(chǔ)?

我承認(rèn)這里有很多概念。 首先要弄清楚的是Java中大型內(nèi)存系統(tǒng)的常規(guī)內(nèi)存管理效率如何低下。 確實(shí),我要說的很清楚,我不是在談?wù)摾厥铡?多年的Java和C ++經(jīng)驗(yàn)告訴我,無論是收集的堆管理還是顯式的堆管理都不是有效或容易實(shí)現(xiàn)的。 我根本不在討論這個(gè)。 JVM的大型內(nèi)存系統(tǒng)管理問題是由于其邊界檢查和對(duì)象模型引起的。 使用內(nèi)存池時(shí),這成為了焦點(diǎn)。

隨著延遲或吞吐量性能變得比內(nèi)存使用更為關(guān)鍵,必須要打破內(nèi)存池。 我們擁有的是大小相同的對(duì)象池,而不是將所有內(nèi)容混合在一起的宏偉的內(nèi)存系統(tǒng)。 如果未充分使用池,或者映射到池塊中的元素小于塊本身,則與純堆相比需要更多的內(nèi)存。 但是,池的管理確實(shí)非常快。

在這篇文章中,我將討論池支持的分段存儲(chǔ)。 分段存儲(chǔ)基于池,但是允許分配比單個(gè)池塊更大的存儲(chǔ)容器。 這個(gè)想法是,一個(gè)存儲(chǔ)容器(例如1 GB)可以由一組塊(例如每個(gè)1 MB)組成。 分段存儲(chǔ)區(qū)域不一定由連續(xù)的塊組成。 確實(shí),這是其最重要的功能。 它由來自備用池的大小相等的塊組成,但是這些塊分散在虛擬地址空間中,甚至可能沒有順序。 有了這一點(diǎn),我們就具有了池的請(qǐng)求和釋放效率,但是卻接近于堆的內(nèi)存使用效率,并且無需擔(dān)心碎片。

首先讓我們看一下泳池的外觀。 然后我們可以返回細(xì)分。

在此討論中,池包括以下部分:

  • 大小相等的內(nèi)存塊的池(不一定全部在一個(gè)數(shù)據(jù)結(jié)構(gòu)中)。
  • 一個(gè)或多個(gè)已用塊的列表。
  • 一份免費(fèi)的大塊清單。
  • 要從池中創(chuàng)建分段的內(nèi)存分配,我們有一個(gè)循環(huán):

  • 創(chuàng)建一個(gè)存儲(chǔ)塊(數(shù)組或類似的東西)的容器。 將該段稱為分配的段列表。
  • 從空閑列表中取出一部分內(nèi)存,并將其添加到段列表中。
  • 查看段列表包含的總內(nèi)存是否等于或大于所需的總內(nèi)存。
  • 如果不是,請(qǐng)從2開始重復(fù)。
  • 現(xiàn)在,我們有了一個(gè)分配段列表,其中至少有足夠的內(nèi)存來滿足需求。 當(dāng)我們釋放該內(nèi)存時(shí),我們只需將這些塊放回到空閑列表中。 從中我們可以看到,很快空閑列表上的塊將不再是有序的,即使我們按地址對(duì)它們進(jìn)行排序,它們也不會(huì)是連續(xù)的。 因此,任何分配將具有足夠的內(nèi)存,但沒有任何連續(xù)的順序。

    這是一個(gè)可行的例子

    我們將考慮10個(gè)1兆字節(jié)的塊,我們可以將它們稱為1,2…10,它們是按順序排列的。

    Start:Free List: 1 2 3 4 5 6 7 8 9 10Allocate a 2.5 megabyte store:Free List: 1 2 3 4 5 6 7Allocated Store A: 8 9 10Allocate a 6 megabyte store:Free List: 1 Allocated Store A: 8 9 10Allocated Store A: 7 6 5 4 3 2Free Allocated Store A:Free List: 10 9 8 1Allocated Store A: 7 6 5 4 3 2Allocate a 3.1 megabyte store:Free List: Allocated Store A: 7 6 5 4 3 2Allocated Store C:10 9 8 1

    可以注意到,這種方法對(duì)于某些情況(例如64位C ++)是好的,但對(duì)于Java來說,它的真正威力是。 在當(dāng)前的JVM中,最大可尋址數(shù)組或ByteBuffer僅包含2 ** 31個(gè)元素,分段存儲(chǔ)提供了一種有效的方式來處理大量?jī)?nèi)存,并在需要時(shí)使用內(nèi)存映射文件支持該內(nèi)存。考慮到我們需要200億雙,無法將它們分配到數(shù)組或ByteBuffer中; 但是我們可以使用分段內(nèi)存來實(shí)現(xiàn)目標(biāo)。

    在Java中將匿名虛擬內(nèi)存用于很大的內(nèi)存對(duì)象可能效率不高。 在某些情況下,我們要處理的內(nèi)存比計(jì)算機(jī)上的RAM大得多,與使用匿名映射空間相比,最好使用內(nèi)存映射文件。 這意味著JVM不會(huì)(在一定程度上)與其他程序爭(zhēng)奪交換空間,但更重要的是,垃圾收集的內(nèi)存會(huì)分配對(duì)象訪問權(quán)限,這對(duì)于匿名虛擬內(nèi)存來說尤其糟糕。 我們希望集中訪問時(shí)域中的特定頁面,以便我們吸引盡可能少的硬頁面錯(cuò)誤。 我在這里討論了該領(lǐng)域的其他概念: https : //jaxenter.com/high-speed-multi-threaded-virtual-memory-in-java-105629.html 。

    鑒于這種。 如果將內(nèi)存映射文件的要求縮小到200億雙,那么我們甚至無法在sun.misc.Unsafe(請(qǐng)參閱下文)中使用magic來提供幫助。 沒有JNI,我們可以用Java管理的最大的內(nèi)存映射文件“塊”僅為2 ^ 31字節(jié)。 正是由于對(duì)內(nèi)存映射文件的需求以及分段存儲(chǔ)方法固有的分配/釋放效率,才使我將其用于Sonic Field(我經(jīng)常需要在16G機(jī)器上管理100G以上的內(nèi)存)。

    深入實(shí)施

    現(xiàn)在,我們有一套清晰的想法可以實(shí)施。 我們需要映射的字節(jié)緩沖區(qū)。 每個(gè)緩沖區(qū)都是空閑塊池中的一個(gè)塊。 當(dāng)我們想要分配一個(gè)存儲(chǔ)容器時(shí),我們需要將這些映射的字節(jié)緩沖區(qū)塊中的一些從空閑池中取出并放入我們的容器中。 釋放容器后,我們將塊返回到空閑池。 簡(jiǎn)單,高效,清潔。

    另外,重要的一點(diǎn)是,映射的字節(jié)緩沖區(qū)實(shí)際上是具有文件后備內(nèi)存的java.nio.DirectByteBuffer對(duì)象。 我們將在以后使用這個(gè)概念。 現(xiàn)在我們可以將它們視為ByteBuffers。

    在Sonic Field上(這是我使用映射的字節(jié)緩沖區(qū)開發(fā)分段存儲(chǔ)技術(shù)的代碼。請(qǐng)參閱https://github.com/nerds-central/SonicFieldRepo )。 在該代碼庫中,我定義了以下內(nèi)容:

    private static final long ?CHUNK_LEN ???????= 1024 * 1024;

    為了獲得樣本,我們可以將每個(gè)塊視為CHUNK_LEN ByteBuffer。 在我的加速工作之前,用于從分配的內(nèi)存塊訪問元素的代碼是:

    private static final long ?CHUNK_SHIFT ?????= 20;private static final long ?CHUNK_MASK ??????= CHUNK_LEN - 1; ...public final double getSample(int index){long bytePos = index << 3;long pos = bytePos & CHUNK_MASK;long bufPos = (bytePos - pos) >> CHUNK_SHIFT;return chunks[(int) bufPos].getDouble((int) pos);}

    因此,在這種情況下,分配的段列表是ByteBuffers的數(shù)組:

  • 通過將所需索引除以塊大小來找到列表中的索引(使用shift以獲得效率)。
  • 通過取模數(shù)找到找到的塊中的索引(使用二進(jìn)制和以提高效率)。
  • 使用getDouble內(nèi)在方法查找實(shí)際值(看起來像一個(gè)方法,但編譯器對(duì)此有所了解,而方法調(diào)用除外)。
  • 所有這些看起來都不錯(cuò),但是效果卻不盡如人意,因?yàn)镴ava在內(nèi)存中布置對(duì)象的方式存在一些根本性的問題,這些問題妨礙了對(duì)分段訪問的正確優(yōu)化。 從表面上看,訪問分段的內(nèi)存區(qū)域應(yīng)該是一些非常快的移位和邏輯操作以及間接查找,但這對(duì)于Java而言并不可行。 所有的問題都發(fā)生在這一行:

    return chunks[(int) bufPos].getDouble((int) pos);

    這是此行必須執(zhí)行的操作:

  • 從其句柄中查找塊對(duì)象。
  • 邊界檢查。
  • 從其數(shù)據(jù)區(qū)域獲取數(shù)據(jù)。
  • 從該對(duì)象的ByteBuffer句柄中查找實(shí)際對(duì)象。
  • 動(dòng)態(tài)查找其長(zhǎng)度(它可以更改,因此這是安全點(diǎn)和對(duì)象字段查找)。
  • 邊界檢查。
  • 檢索數(shù)據(jù)。
  • 真? 是的,JVM完成了所有痛苦的工作。 它不僅需要大量指令,而且還需要在內(nèi)存中跳轉(zhuǎn),從而導(dǎo)致所有隨后的緩存行刷新和內(nèi)存暫停。

    我們?nèi)绾螌?duì)此進(jìn)行改進(jìn)? 請(qǐng)記住,我們的ByteBuffers是DirectByteBuffers,這意味著它們的數(shù)據(jù)未存儲(chǔ)在Java堆中; 在整個(gè)對(duì)象生命周期中,它位于相同的虛擬地址位置。 我敢打賭,您已經(jīng)猜到這里的關(guān)鍵是使用sun.misc.Unsafe。 是的; 我們可以通過使用堆內(nèi)存來繞過所有此對(duì)象查找。 這樣做意味著彎曲一些Java和JVM規(guī)則,但是值得這樣做。

    從現(xiàn)在開始,我討論的所有內(nèi)容都與Java 1.8 x86_64有關(guān)。 將來的版本可能會(huì)破壞此方法,因?yàn)樗环蠘?biāo)準(zhǔn)。

    考慮一下:

    private static class ByteBufferWrapper{public long ??????address;public ByteBuffer buffer;public ByteBufferWrapper(ByteBuffer b) throwsNoSuchMethodException,SecurityException,IllegalAccessException,IllegalArgumentException,InvocationTargetException{Method addM = b.getClass().getMethod("address");addM.setAccessible(true);address = (long) addM.invoke(b);buffer = b;}}

    我們正在做的是獲取DirectByteBuffer中存儲(chǔ)的數(shù)據(jù)在內(nèi)存中的地址。 為此,我使用反射,因?yàn)镈irectByteBuffer是包私有的。 DirectByteBuffer上有一個(gè)稱為address()的方法,該方法返回long。 在x86_64上,地址的大小(64位)與長(zhǎng)度相同。 雖然long的值是有符號(hào)的,但我們只能將long用作二進(jìn)制數(shù)據(jù),而忽略其數(shù)值。 因此,從address()返回的long實(shí)際上是緩沖區(qū)存儲(chǔ)區(qū)起始位置的虛擬地址。

    與“普通” JVM存儲(chǔ)(例如,數(shù)組)不同,DirectByteBuffer的存儲(chǔ)是“堆外”。 它是虛擬內(nèi)存,與其他任何虛擬內(nèi)存一樣,但不屬于垃圾收集器,不能被垃圾收集器移動(dòng); 這對(duì)我們?cè)L問它的速度和方式有很大的不同。 請(qǐng)記住,對(duì)于給定的DirectByteBuffer對(duì)象,由address()返回的地址永遠(yuǎn)不會(huì)更改。 因此,我們可以“永遠(yuǎn)”使用該地址,并避免對(duì)象查找。

    介紹sun.misc.Unsafe

    相信在DirectByteBuffer上調(diào)用getDouble(int)是超高效的,雖然看起來很可愛,但事實(shí)并非如此。 盡管方法是固有的,但邊界檢查會(huì)減慢速度(JVM JIT編譯器知道并可以用機(jī)器代碼代替魔術(shù)方法而不是按常規(guī)方式編譯的魔術(shù)函數(shù))。 但是,使用我們的地址,我們現(xiàn)在可以使用sun.misc.Unsafe來訪問存儲(chǔ)。

    而不是:

    b.getDouble(pos);

    我們可以:

    unsafe.getDouble(address+pos);

    不安全的版本也是固有的,可以編譯成與C編譯器(如gcc)生成的機(jī)器代碼幾乎相同的機(jī)器代碼。 換句話說,它盡可能快。 沒有對(duì)象取消引用或邊界檢查,它只是從地址加載雙精度型。

    商店等效項(xiàng)是:

    unsafe.putDouble(address+pos,value);

    這是什么“不安全”的東西? 我們通過另一個(gè)反思技巧來解決這個(gè)問題:

    private static Unsafe getUnsafe(){try{Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);return (Unsafe) f.get(null);}catch (Exception e){throw new RuntimeException(e);}}private static final Unsafe unsafe = getUnsafe();

    將不安全的單例加載到最終的靜態(tài)字段中很重要。 這使編譯器可以假設(shè)對(duì)象引用永不更改,因此將生成最佳代碼。

    現(xiàn)在,我們可以非常快速地從DirectByteBuffer中獲取數(shù)據(jù),但是我們有分段存儲(chǔ)模型,因此我們需要非常快速地獲取正確字節(jié)緩沖區(qū)的地址。 如果將它們存儲(chǔ)在數(shù)組中,則會(huì)冒著數(shù)組邊界檢查和數(shù)組對(duì)象取消引用步驟的風(fēng)險(xiǎn)。 我們可以通過進(jìn)一步使用不安全和亂堆的內(nèi)存來擺脫它們。

    private final long ?chunkIndex; ...try{// Allocate the memory for the index - final so do it herelong size = (1 + ((l << 3) >> CHUNK_SHIFT)) << 3;allocked = chunkIndex = unsafe.allocateMemory(size);if (allocked == 0){throw new RuntimeException("Out of memory allocating " + size);}makeMap(l << 3l);}catch (Exception e){throw new RuntimeException(e);}

    再次,我們使用“最終”技巧使編譯器進(jìn)行最佳優(yōu)化。 這里的決賽很長(zhǎng),只是一個(gè)地址。 我們可以使用不安全的方法直接分配堆內(nèi)存。 想象中的實(shí)現(xiàn)此功能的函數(shù)是allocateMemory(long)。 這將返回一個(gè)存儲(chǔ)在chunkIndex中的long。 實(shí)際上,allocateMemory(long)分配字節(jié),但是我們要存儲(chǔ)有效的long數(shù)組(地址); 這就是位旋轉(zhuǎn)邏輯在計(jì)算大小時(shí)所執(zhí)行的操作。

    現(xiàn)在我們有了大塊的堆外內(nèi)存,足以為存儲(chǔ)容器存儲(chǔ)DirectByteBuffer段的地址,我們可以放入地址并使用不安全的地址進(jìn)行檢索。

    在存儲(chǔ)構(gòu)建過程中,我們:

    // now we have the chunks we get the address of the underlying memory// of each and place that in the off heap lookup so we no longer// reference them via objects but purely as raw memorylong offSet = 0;for (ByteBufferWrapper chunk : chunks){unsafe.putAddress(chunkIndex + offSet, chunk.address);offSet += 8;}

    這意味著我們獲取和設(shè)置數(shù)據(jù)的新代碼確實(shí)可以非常簡(jiǎn)單:

    private long getAddress(long index){long bytePos = index << 3;long pos = bytePos & CHUNK_MASK;long bufPos = (bytePos - pos) >> CHUNK_SHIFT;long address = chunkIndex + (bufPos << 3);return unsafe.getAddress(address) + pos;}/* (non-Javadoc)* @see com.nerdscentral.audio.SFSignal#getSample(int)*/@Overridepublic final double getSample(int index){return unsafe.getDouble(getAddress(index));}/* (non-Javadoc)* @see com.nerdscentral.audio.SFSignal#setSample(int, double)*/@Overridepublic final double setSample(int index, double value){unsafe.putDouble(getAddress(index), value);return value;}

    這樣做的妙處在于完全沒有對(duì)象操作或邊界檢查。 好的,如果有人要求提供超出范圍的樣本,JVM將崩潰。 那可能不是一件好事。 這種編程對(duì)于許多Java程序員來說是非常陌生的,我們需要非常認(rèn)真地對(duì)待它的危險(xiǎn)。 但是,與原始版本相比,它確實(shí)相當(dāng)快。

    在實(shí)驗(yàn)中,我發(fā)現(xiàn)默認(rèn)的JVM內(nèi)聯(lián)設(shè)置有些保守,無法充分利用這種方法。 通過以下命令行調(diào)整,我已經(jīng)看到了大幅度的加速(性能提高了兩倍)。

    -XX:MaxInlineSize=128 -XX:InlineSmallCode=1024

    這些使JVM可以更好地利用可利用的額外性能,而不必強(qiáng)制執(zhí)行邊界檢查和對(duì)象查找。 通常,我不建議您去擺弄JVM內(nèi)聯(lián)設(shè)置,但是在這種情況下,我有真正的基準(zhǔn)測(cè)試經(jīng)驗(yàn),可以顯示出對(duì)復(fù)雜的堆外訪問工作的好處。

    測(cè)試–快多少?

    我編寫了以下Jython進(jìn)行測(cè)試:

    import math from java.lang import Systemsf.SetSampleRate(192000) count=1000 ncount=100def test():t1=System.nanoTime()for i in range(1,ncount):signal=sf.Mix(+signal1,+signal2)signal=sf.Realise(signal)-signalt2=System.nanoTime()d=(t2-t1)/1000000.0print "Done: " + str(d)return dsignal1=sf.Realise(sf.WhiteNoise(count)) signal2=sf.Realise(sf.WhiteNoise(count)) print "WARM" for i in range(1,100):test()print "Real" total=0.0 for i in range(1,10):total+=test()print "Mean " + str(total/9.0)-signal1 -signal2

    這樣做是創(chuàng)建一些存儲(chǔ)的雙打,然后創(chuàng)建新的雙打,并一遍又一遍地從舊讀入。 請(qǐng)記住,我們正在使用由池支持的分段存儲(chǔ)。 因此,我們僅在最初真正分配存儲(chǔ)空間,然后才對(duì)“塊”進(jìn)行回收。 這種體系結(jié)構(gòu)意味著我們的執(zhí)行時(shí)間主要由執(zhí)行g(shù)etSample和setSample而不是分配或任何其他工具決定。

    我們的堆外系統(tǒng)快多少? 在裝有Java 1.8.0的Macbook Pro Retina I7機(jī)器上,我得到了“實(shí)際”(即預(yù)熱后)操作的數(shù)字(越小越好):

    對(duì)于不安全的內(nèi)存模型:

    • 完成:187.124
    • 完成:175.007
    • 完成:181.124
    • 完成:175.384
    • 完成:180.497
    • 完成:180.688
    • 完成:183.309
    • 完成:178.901
    • 完成:181.746
    • 均值180.42

    對(duì)于傳統(tǒng)的內(nèi)存模型:

    • 完成:303.008
    • 完成:328.763
    • 完成:299.701
    • 完成:315.083
    • 完成:306.809
    • 完成:302.515
    • 完成:304.606
    • 完成:300.291
    • 完成:342.436
    • 均值311.468

    因此,我們的不安全內(nèi)存模型比傳統(tǒng)的Java方法快1.73倍 !

    為什么快1.73倍

    我們可以明白為什么。

    如果我們回顧一下從傳統(tǒng)的DirectByteBuffer和數(shù)組方法中讀取雙精度數(shù)據(jù)所需的事項(xiàng)列表:

  • 從其句柄中查找塊對(duì)象。
  • 邊界檢查。
  • 從其數(shù)據(jù)區(qū)域獲取數(shù)據(jù)。
  • 從該對(duì)象的ByteBuffer句柄中查找實(shí)際對(duì)象。
  • 動(dòng)態(tài)查找其長(zhǎng)度(它可以更改,因此這是安全點(diǎn)和對(duì)象字段查找)。
  • 邊界檢查。
  • 檢索數(shù)據(jù)。
  • 使用新方法,我們可以:

  • 檢索塊的地址
  • 從該塊中檢索數(shù)據(jù)
  • 不僅發(fā)出的機(jī)器指令數(shù)量減少了很多,而且存儲(chǔ)器訪問的位置更加本地化,??幾乎可以肯定的是,它可以提高數(shù)據(jù)處理期間的緩存使用率。

    如此處所述,用于存儲(chǔ)系統(tǒng)快速版本的源代碼為: https : //github.com/nerds-central/SonicFieldRepo/blob/cf6a1b67fb8dd07126b0b1274978bd850ba76931/SonicField/src/com/nerdscentral/audio/SFData.java

    我希望您(讀者)發(fā)現(xiàn)一個(gè)我沒有解決的大問題! 每當(dāng)我的代碼創(chuàng)建分段存儲(chǔ)容器時(shí),我的代碼都會(huì)分配堆內(nèi)存。 但是,垃圾回收器不會(huì)釋放此內(nèi)存。 我們可以嘗試使用終結(jié)器來釋放代碼,但是有很多原因使它不是一個(gè)好主意。

    我的解決方案是使用顯式資源管理。 Sonic Field使用try資源,通過引用計(jì)數(shù)來管理其內(nèi)存。 當(dāng)特定存儲(chǔ)容器的引用計(jì)數(shù)達(dá)到零時(shí),該容器將被釋放,從而將其存儲(chǔ)塊放回空閑列表中,并使用不安全的方法來釋放地址查找內(nèi)存。

    其他用途和新思路

    大約一年前,我發(fā)布了“ 保持相關(guān)性的Java Power功能 ”。 我猜這是一個(gè)有爭(zhēng)議的帖子,并不是我所談?wù)摰拿總€(gè)人都對(duì)我的想法感到滿意(至少可以這樣說)。 盡管如此,我仍然認(rèn)為JVM面臨著挑戰(zhàn)。 Java和JVM本身的復(fù)雜多線程模型不一定代表人們認(rèn)為它應(yīng)該在多核計(jì)算領(lǐng)域中所具有的巨大利益。 使用多個(gè)通過共享內(nèi)存或套接字進(jìn)行通信的小進(jìn)程仍然引起了很多興趣。 隨著基于RDMA的網(wǎng)絡(luò)的緩慢但不可避免的增長(zhǎng),這些方法對(duì)于人們來說將越來越自然。

    Java和JVM語言似乎設(shè)法使自己獨(dú)特地?zé)o法利用這些思維上的轉(zhuǎn)變。 通過開發(fā)“圍墻花園”方法,JVM在內(nèi)部工作方面變得非常高效,但在處理其他進(jìn)程方面卻表現(xiàn)不佳。 這既是性能問題,也是穩(wěn)定性問題; 無論我們?nèi)绾闻?#xff0c;總是有機(jī)會(huì)JVM崩潰或進(jìn)入不穩(wěn)定狀態(tài)(有人OutOfMemoryError嗎?)。 在生產(chǎn)系統(tǒng)中,這通常需要幾個(gè)小型JVM實(shí)例一起工作,因此,如果一個(gè)實(shí)例消失了,生產(chǎn)系統(tǒng)就會(huì)停下來。 內(nèi)存映射文件是幫助持久保存數(shù)據(jù)的好方法,即使JVM進(jìn)程消失了。

    所有這些問題使我對(duì)另一個(gè)原因感到非常感興趣,因?yàn)槲覍?duì)JVM的高效偏移,映射文件體系結(jié)構(gòu)非常感興趣。 這項(xiàng)技術(shù)位于共享內(nèi)存和映射文件技術(shù)的重疊處,這些技術(shù)現(xiàn)在正在推動(dòng)高速,穩(wěn)定的生產(chǎn)環(huán)境。 雖然我在這里討論的系統(tǒng)是針對(duì)單個(gè)JVM的,但使用了堆原子(請(qǐng)參閱此處: http ://nerds-central.blogspot.co.uk/2015/05/synchronising-sunmiscunsafe-with-c.html),我們可以空閑列表亂用,并在進(jìn)程之間共享。 然后,共享內(nèi)存隊(duì)列還可以對(duì)分段存儲(chǔ)分配和利用進(jìn)行進(jìn)程間仲裁。 突然地,分段存儲(chǔ)模型成為JVM和其他技術(shù)(Python,C ++等)的多個(gè)進(jìn)程共享大型文件持久存儲(chǔ)系統(tǒng)的有效方法。

    現(xiàn)在有一些問題。 其中最大的一點(diǎn)是,盡管Java通過內(nèi)存映射文件支持共享內(nèi)存,但不通過純共享內(nèi)存支持。 如果我們對(duì)大內(nèi)存區(qū)域感興趣(如本例所示),則文件映射是一個(gè)優(yōu)勢(shì),但對(duì)于不需要持久性的快速變化的小內(nèi)存區(qū)域,這是不必要的性能問題。 我想在JDK中看到一個(gè)真正的共享內(nèi)存庫; 這不太可能很快發(fā)生(請(qǐng)參閱我關(guān)于圍墻花園的觀點(diǎn))。 JNI提供了一條路線,但是JNI有很多不利之處,我們對(duì)此深有體會(huì)。 也許巴拿馬項(xiàng)目將提供所需的功能,并最終打破JVM的壁壘。

    綜上所述,我想嘗試的下一個(gè)技巧是將文件映射到ramdisk(在此處有一個(gè)有趣的文章: http : //www.jamescoyle.net/knowledge/951-the-difference-between-a -tmpfs和Ramfs-RAM磁盤 這在Linux上應(yīng)該非常容易,并且可以讓我們?cè)诓皇褂肑NI的情況下將進(jìn)程間隊(duì)列放置在純RAM共享內(nèi)存區(qū)域中。 完成此部分后,將可以洞悉純Java高速進(jìn)程間共享內(nèi)存模型。 也許那將不得不等待明年的日歷?

    翻譯自: https://www.javacodegeeks.com/2015/12/native-speed-file-backed-large-data-storage-pure-java.html

    java 存儲(chǔ)數(shù)據(jù)到文件中

    總結(jié)

    以上是生活随笔為你收集整理的java 存储数据到文件中_本机速度文件支持的“纯” Java大数据存储的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。