日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
java內(nèi)存池代碼實現(xiàn) java 內(nèi)存池

怎樣設(shè)計一個內(nèi)存池,減少內(nèi)存碎片?

一般工程里不推薦你寫,因為你費力寫一個出來99%可能性沒有內(nèi)置的好,且內(nèi)存出bug難調(diào)試

創(chuàng)新互聯(lián)建站專注于網(wǎng)站建設(shè)|網(wǎng)站建設(shè)維護(hù)|優(yōu)化|托管以及網(wǎng)絡(luò)推廣,積累了大量的網(wǎng)站設(shè)計與制作經(jīng)驗,為許多企業(yè)提供了網(wǎng)站定制設(shè)計服務(wù),案例作品覆蓋建筑動畫等行業(yè)。能根據(jù)企業(yè)所處的行業(yè)與銷售的產(chǎn)品,結(jié)合品牌形象的塑造,量身設(shè)計品質(zhì)網(wǎng)站。

一定要設(shè)計的話,你也可以當(dāng)個玩具寫寫玩玩:

1. 實現(xiàn)教科書上的內(nèi)存分配器:

做一個鏈表指向空閑內(nèi)存,分配就是取出一塊來,改寫鏈表,返回,釋放就是放回到鏈表里面,并做好歸并。注意做好標(biāo)記和保護(hù),避免二次釋放,還可以花點力氣在如何查找最適合大小的內(nèi)存快的搜索上,減少內(nèi)存碎片,有空你了還可以把鏈表換成伙伴算法,寫著玩嘛。

2. 實現(xiàn)固定內(nèi)存分配器:

即實現(xiàn)一個 FreeList,每個 FreeList 用于分配固定大小的內(nèi)存塊,比如用于分配 32字節(jié)對象的固定內(nèi)存分配器,之類的。每個固定內(nèi)存分配器里面有兩個鏈表,OpenList 用于存儲未分配的空閑對象,CloseList用于存儲已分配的內(nèi)存對象,那么所謂的分配就是從 OpenList 中取出一個對象放到 CloseList 里并且返回給用戶,釋放又是從 CloseList 移回到 OpenList。分配時如果不夠,那么就需要增長 OpenList:申請一個大一點的內(nèi)存塊,切割成比如 64 個相同大小的對象添加到 OpenList中。這個固定內(nèi)存分配器回收的時候,統(tǒng)一把先前向系統(tǒng)申請的內(nèi)存塊全部還給系統(tǒng)。

3. 實現(xiàn) FreeList 池:

在你實現(xiàn)了 FreeList的基礎(chǔ)上,按照不同對象大?。?字節(jié),16字節(jié),32,64,128,256,512,1K。。。64K),構(gòu)造十多個固定內(nèi)存分配器,分配內(nèi)存時根據(jù)內(nèi)存大小查表,決定到底由哪個分配器負(fù)責(zé),分配后要在頭部的 header 處(ptr[-sizeof(char*)]處)寫上 cookie,表示又哪個分配器分配的,這樣釋放時候你才能正確歸還。如果大于64K,則直接用系統(tǒng)的 malloc作為分配,如此以浪費內(nèi)存為代價你得到了一個分配時間近似O(1)的內(nèi)存分配器,差不多實現(xiàn)了一個 memcached 的 slab 內(nèi)存管理器了,但是先別得意。此 slab 非彼 slab(sunos/solaris/linux kernel 的 slab)。這說白了還是一個弱智的 freelist 無法歸還內(nèi)存給操作系統(tǒng),某個 FreeList 如果高峰期占用了大量內(nèi)存即使后面不用,也無法支援到其他內(nèi)存不夠的 FreeList,所以我們做的這個和 memcached 類似的分配器其實是比較殘缺的,你還需要往下繼續(xù)優(yōu)化。

4. 實現(xiàn)正統(tǒng)的 slab (非memcached的偽 slab)代替 FreeList:

這時候你需要閱讀一下論文了,現(xiàn)代內(nèi)存分配技術(shù)的基礎(chǔ),如何管理 slab 上的對象,如何進(jìn)行地址管理,如何管理不同 slab 的生命周期,如何將內(nèi)存回收給系統(tǒng)。然后開始實現(xiàn)一個類似的東西,文章上傳統(tǒng)的 slab 的各種基礎(chǔ)概念雖然今天沒有改變,但是所用到的數(shù)據(jù)結(jié)構(gòu)和控制方法其實已經(jīng)有很多更好的方法了,你可以邊實現(xiàn)邊思考下,實在不行還可以參考 kernel 源碼嘛。但是有很多事情應(yīng)用程序做不了,有很多實現(xiàn)你是不能照搬的,比如頁面提供器,可以提供連續(xù)線性地址的頁面,再比如說 kernel 本身記錄著每個頁面對應(yīng)的 slab,你查找 slab 時,系統(tǒng)其實是根據(jù)線性地址移位得到頁面編號,然后查表得到的,而你應(yīng)用程序不可能這么干,你還得做一些額外的體系來解決這些問題,還需要寫一些額外的 cookie 來做標(biāo)記。做好內(nèi)存收縮工作,內(nèi)存不夠時先收縮所有分配器的 slab,再嘗試重新分配。再做好內(nèi)存回收工作,多余的內(nèi)存,一段時間不使用可以還給操作系統(tǒng)。

5. 實現(xiàn)混合分配策略:

你實現(xiàn)了上面很多常見的算法后,該具體閱讀各種內(nèi)存分配器的代碼了,這些都是經(jīng)過實踐檢驗的,比如 libc 的內(nèi)存分配器,或者參考有自帶內(nèi)存管理的各種開源項目,比如 python 源碼,做點實驗對比他們的優(yōu)劣,然后根據(jù)分配對象的大小采用不同的分配策略,區(qū)別對待各種情況。試驗的差不多了就得引入多線程支持了,將你的鎖改小。注意很多系統(tǒng)層的線程安全策略你是沒法弄的,比如操作系統(tǒng)可以關(guān)中斷,短時間內(nèi)禁止本cpu發(fā)生任務(wù)切換,這點應(yīng)用程序就很麻煩了,還得用更小的鎖來代替。當(dāng)鎖已經(jīng)小到不能再小,也可以選擇引入 STM 來代替各種鏈表的鎖。

6. 實現(xiàn) Per-CPU Cache:

現(xiàn)代內(nèi)存分配器,在多核下的一個重要優(yōu)化就是給多核增加 cache,為了進(jìn)一步避免多線程鎖競爭,需要引入 Per-CPU Cache 了。分配內(nèi)存先找到對應(yīng)線程所在的cpu,從該cpu上對應(yīng)的 cache 里分配,cache 不夠了就一次性從你底層的內(nèi)存分配器里多分配幾個對象進(jìn)來填充 cache,釋放時也是先放回 cache,cache里面如果對象太多,就做一次收縮,把內(nèi)存換個底層分配器,讓其他 cpu 的cache有機會利用。這樣針對很多短生命周期的頻繁的分配、釋放,其實都是在 cache 里完成的,沒有鎖競爭,同時cache分配邏輯簡單,速度更快。操作系統(tǒng)里面的代碼經(jīng)常是直接讀取當(dāng)前的cpu是哪個,而應(yīng)用層實現(xiàn)你可以用 thread local storage 來代替,目前這些東西在 crt的 malloc 里還暫時支持不到位(不排除未來版本會增加),可以更多參考 tc/jemalloc。

7. 實現(xiàn)地址著色:

現(xiàn)代內(nèi)存分配器必須多考慮總線壓力,在很多機型上,如果內(nèi)存訪問集中在某條 cache line相同的偏移上,會給總線帶來額外的負(fù)擔(dān)和壓力。比如你經(jīng)常要分配一個 FILE 對象,而每個 FILE對象使用時會比較集中的訪問 int FILE::flag; 這個成員變量,如果你的頁面提供器提供的頁面地址是按照 4K對齊的,那么很可能多個 FILE對象的 flag 成員所處的 cache line 偏移地址是相同的,大量訪問這些相同的偏移地址會給總線帶來很大負(fù)擔(dān),這時候你需要給每個對象額外增加一些偏移,讓他們能夠均勻的分布在線性地址對應(yīng)的cache line 偏移上,消減總線沖突的開銷。

8. 優(yōu)化緩存競爭:

多核時代,很多單核時代的代碼都需要針對性的優(yōu)化改寫,最基本的一條就是 cache 競爭,這是比前面鎖競爭更惡劣的情況:如果兩個cpu同時訪問相同的 cache-line 或者物理頁面,那么 cpu 之間為了保證內(nèi)存一致性會做很多的通信工作,比如那個cpu0需要用到這段內(nèi)存,發(fā)現(xiàn)cpu1也在用,那么需要通知cpu1,將cpu1 L1-L2緩存里面的數(shù)據(jù)寫回該物理內(nèi)存,并且釋放控制權(quán),這時cpu0取得了控制權(quán)才能繼續(xù)操作,期間cpu0-cpu1之間的通信協(xié)議是比較復(fù)雜的,代價也是比較大的,cache競爭比鎖競爭惡劣不少。為了避免 cache 競爭,需要比先前Per-CPU cache 更徹底的 Per-CPU Page 機制來解決,直接讓不同的cpu使用不同的頁面進(jìn)行二次分配,徹底避免 cache 競爭。具體應(yīng)用層的做法也是利用線性地址來判斷所屬頁面(因為物理頁面映射到進(jìn)程地址也是4k對齊的),同時繼續(xù)使用 thread local storage 或者用系統(tǒng)提供的 api 讀取當(dāng)前屬于哪個 cpu 來實現(xiàn)。為了避免核太多每個核占據(jù)大量的頁面帶來的不必要的浪費,你可以參考下 Linux 最新的 slub 內(nèi)存分配算法,但是 slub 也有未盡之處,好幾個 linux 發(fā)行版在實踐中發(fā)現(xiàn) slub 還是存在一些問題的(非bug,而是機制),所以大部分發(fā)行版默認(rèn)都是關(guān)閉 slub 的,雖然,你還是可以借鑒測試一下。

9. 調(diào)試和折騰:

繼續(xù)參考各種現(xiàn)代內(nèi)存分配器,取長補短,然后給你的分配器添加一些便于調(diào)試的機制,方便診斷各種問題。在你借鑒了很多開源項目,自己也做了一些所謂的優(yōu)化,折騰了那么久以后,你或許以為你的分配器可以同各種開源分配器一戰(zhàn)了,測試效果好像也挺好的,先別急,繼續(xù)觀察內(nèi)存利用率,向操作系統(tǒng)申請/歸還內(nèi)存的頻率等一系列容易被人忽視的指標(biāo)是否相同。同時更換你的測試用例,看看更多的情況下,是否結(jié)果還和先前一樣?這些都差不多的時候,你發(fā)現(xiàn)沒有個一兩年的大規(guī)模持續(xù)使用,你很難發(fā)現(xiàn)一些潛在的隱患和bug,可能你覺得沒問題的代碼,跑了兩年后都會繼續(xù)報bug,這很正常,多點耐心,興許第三年以后就比較穩(wěn)定了呢?

如何用java代碼來監(jiān)控系統(tǒng)內(nèi)存·cpu·線程占用情況,并生成日志

可以學(xué)習(xí)軟件包 java.lang.management

提供管理接口,用于監(jiān)視和管理 Java 虛擬機以及 Java 虛擬機在其上運行的操作系統(tǒng)。

ClassLoadingMXBean

用于 Java 虛擬機的類加載系統(tǒng)的管理接口。

CompilationMXBean

用于 Java 虛擬機的編譯系統(tǒng)的管理接口。

GarbageCollectorMXBean

用于 Java 虛擬機的垃圾回收的管理接口。

MemoryManagerMXBean

內(nèi)存管理器的管理接口。

MemoryMXBean

Java 虛擬機內(nèi)存系統(tǒng)的管理接口。

MemoryPoolMXBean

內(nèi)存池的管理接口。

OperatingSystemMXBean

用于操作系統(tǒng)的管理接口,Java 虛擬機在此操作系統(tǒng)上運行。

RuntimeMXBean

Java 虛擬機的運行時系統(tǒng)的管理接口。

ThreadMXBean

Java 虛擬機線程系統(tǒng)的管理接口。

更多請訪問(bug315)

【Java基礎(chǔ)】線程池的原理是什么?

什么是線程池?

總歸為:池化技術(shù) ---》數(shù)據(jù)庫連接池 緩存架構(gòu) 緩存池 線程池 內(nèi)存池,連接池,這種思想演變成緩存架構(gòu)技術(shù)--- JDK設(shè)計思想有千絲萬縷的聯(lián)系

首先我們從最核心的ThreadPoolExecutor類中的方法講起,然后再講述它的實現(xiàn)原理,接著給出了它的使用示例,最后討論了一下如何合理配置線程池的大小。

Java 中的 ThreadPoolExecutor 類

java.uitl.concurrent.ThreadPoolExecutor 類是線程池中最核心的一個類,因此如果要透徹地了解Java 中的線程池,必須先了解這個類。下面我們來看一下 ThreadPoolExecutor 類的具體實現(xiàn)源碼。

在 ThreadPoolExecutor 類中提供了四個構(gòu)造方法:

從上面的代碼可以得知,ThreadPoolExecutor 繼承了 AbstractExecutorService 類,并提供了四個構(gòu)造器,事實上,通過觀察每個構(gòu)造器的源碼具體實現(xiàn),發(fā)現(xiàn)前面三個構(gòu)造器都是調(diào)用的第四個構(gòu)造器進(jìn)行的初始化工作。

下面解釋下一下構(gòu)造器中各個參數(shù)的含義:

corePoolSize:核心池的大小,這個參數(shù)跟后面講述的線程池的實現(xiàn)原理有非常大的關(guān)系。在創(chuàng)建了線程池后,默認(rèn)情況下,線程池中并沒有任何線程,而是等待有任務(wù)到來才創(chuàng)建線程去執(zhí)行任務(wù),除非調(diào)用了prestartAllCoreThreads() 或者 prestartCoreThread()方法,從這 2 個方法的名字就可以看出,是預(yù)創(chuàng)建線程的意思,即在沒有任務(wù)到來之前就創(chuàng)建 corePoolSize 個線程或者一個線程。默認(rèn)情況下,在創(chuàng)建了線程池后,線程池中的線程數(shù)為0,當(dāng)有任務(wù)來之后,就會創(chuàng)建一個線程去執(zhí)行任務(wù),當(dāng)線程池中的線程數(shù)目達(dá)到 corePoolSize 后,就會把到達(dá)的任務(wù)放到緩存隊列當(dāng)中;

maximumPoolSize:線程池最大線程數(shù),這個參數(shù)也是一個非常重要的參數(shù),它表示在線程池中最多能創(chuàng)建多少個線程;

keepAliveTime:表示線程沒有任務(wù)執(zhí)行時最多保持多久時間會終止。默認(rèn)情況下,只有當(dāng)線程池中的線程數(shù)大于 corePoolSize 時,keepAliveTime 才會起作用,直到線程池中的線程數(shù)不大于 corePoolSize,即當(dāng)線程池中的線程數(shù)大于 corePoolSize 時,如果一個線程空閑的時間達(dá)到 keepAliveTime,則會終止,直到線程池中的線程數(shù)不超過 corePoolSize。但是如果調(diào)用了 allowCoreThreadTimeOut(boolean) 方法,在線程池中的線程數(shù)不大于 corePoolSize 時,keepAliveTime 參數(shù)也會起作用,直到線程池中的線程數(shù)為0;

unit:參數(shù) keepAliveTime 的時間單位,有 7 種取值,在 TimeUnit 類中有 7 種靜態(tài)屬性:

workQueue:一個阻塞隊列,用來存儲等待執(zhí)行的任務(wù),這個參數(shù)的選擇也很重要,會對線程池的運行過程產(chǎn)生重大影響,一般來說,這里的阻塞隊列有以下幾種選擇:

ArrayBlockingQueue 和 PriorityBlockingQueue 使用較少,一般使用 LinkedBlockingQueue 和 Synchronous。線程池的排隊策略與 BlockingQueue 有關(guān)。

threadFactory:線程工廠,主要用來創(chuàng)建線程;

handler:表示當(dāng)拒絕處理任務(wù)時的策略,有以下四種取值:

具體參數(shù)的配置與線程池的關(guān)系將在下一節(jié)講述。

從上面給出的 ThreadPoolExecutor 類的代碼可以知道,ThreadPoolExecutor 繼承了AbstractExecutorService,我們來看一下 AbstractExecutorService 的實現(xiàn):

AbstractExecutorService 是一個抽象類,它實現(xiàn)了 ExecutorService 接口。

我們接著看 ExecutorService 接口的實現(xiàn):

而 ExecutorService 又是繼承了 Executor 接口,我們看一下 Executor 接口的實現(xiàn):

菜鳥:剛學(xué)java,堆區(qū),棧區(qū),靜態(tài)區(qū),代碼區(qū),暈了?。。。?!

程序運行時,我們最好對數(shù)據(jù)保存到什么地方做到心中有數(shù)。特別要注意的是內(nèi)在的分配,有六個地方都可以保存數(shù)據(jù):

1、 寄存器。這是最快的保存區(qū)域,因為它位于和其他所有保存方式不同的地方:處理器內(nèi)部。然而,寄存器的數(shù)量十分有限,所以寄存器是根據(jù)需要由編譯器分配。我們對此沒有直接的控制權(quán),也不可能在自己的程序里找到寄存器存在的任何蹤跡。

2、 堆棧。駐留于常規(guī)RAM(隨機訪問存儲器)區(qū)域。但可通過它的“堆棧指針”獲得處理的直接支持。堆棧指針若向下移,會創(chuàng)建新的內(nèi)存;若向上移,則會釋放那些內(nèi)存。這是一種特別快、特別有效的數(shù)據(jù)保存方式,僅次于寄存器。創(chuàng)建程序時,java編譯器必須準(zhǔn)確地知道堆棧內(nèi)保存的所有數(shù)據(jù)的“長度”以及“存在時間”。這是由于它必須生成相應(yīng)的代碼,以便向上和向下移動指針。這一限制無疑影響了程序的靈活性,所以盡管有些java數(shù)據(jù)要保存在堆棧里——特別是對象句柄,但java對象并不放到其中。

3、 堆。一種常規(guī)用途的內(nèi)存池(也在RAM區(qū)域),其中保存了java對象。和堆棧不同:“內(nèi)存堆”或“堆”最吸引人的地方在于編譯器不必知道要從堆里分配多少存儲空間,也不必知道存儲的數(shù)據(jù)要在堆里停留多長的時間。因此,用堆保存數(shù)據(jù)時會得到更大的靈活性。要求創(chuàng)建一個對象時,只需用new命令編制相碰的代碼即可。執(zhí)行這些代碼時,會在堆里自動進(jìn)行數(shù)據(jù)的保存。當(dāng)然,為達(dá)到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間

4、 靜態(tài)存儲。這兒的“靜態(tài)”是指“位于固定位置”。程序運行期間,靜態(tài)存儲的數(shù)據(jù)將隨時等候調(diào)用??捎胹tatic關(guān)鍵字指出一個對象的特定元素是靜態(tài)的。但java對象本身永遠(yuǎn)都不會置入靜態(tài)存儲空間。

5、 常數(shù)存儲。常數(shù)值通常直接置于程序代碼內(nèi)部。這樣做是安全的。因為它們永遠(yuǎn)都不會改變,有的常數(shù)需要嚴(yán)格地保護(hù),所以可考慮將它們置入只讀存儲器(ROM)。

6、 非RAM存儲。若數(shù)據(jù)完全獨立于一個程序之外,則程序不運行時仍可存在,并在程序的控制范圍之外。其中兩個最主要的例子便是“流式對象”和“固定對象”。對于流式對象,對象會變成字節(jié)流,通常會發(fā)給另一臺機器,而對于固定對象,對象保存在磁盤中。即使程序中止運行,它們?nèi)钥杀3肿约旱臓顟B(tài)不變。對于這些類型的數(shù)據(jù)存儲,一個特別有用的技藝就是它們能存在于其他媒體中,一旦需要,甚至能將它們恢復(fù)成普通的、基于RAM的對象。

java 內(nèi)存池和堆內(nèi)存什么關(guān)系啊

兩者是完全不同的兩個概念

內(nèi)存池:

在真正使用內(nèi)存之前,先申請分配一定數(shù)量的、大小相等(一般情況下)的內(nèi)存塊留作備用。當(dāng)有新的內(nèi)存需求時,就從內(nèi)存池中分出一部分內(nèi)存塊,若內(nèi)存塊不夠再繼續(xù)申請新的內(nèi)存。這樣做的一個顯著優(yōu)點是,使得內(nèi)存分配效率得到提升。

一個程序會隨著長時間的運行和內(nèi)存的申請釋放而變得越來越慢,內(nèi)存也會隨著時間逐漸碎片化。特別是高頻率的進(jìn)行小內(nèi)存申請釋放,此問題變得尤其嚴(yán)重。

內(nèi)存池最大的優(yōu)勢在于:

1、極少的(甚至沒有)堆碎片整理

2、較之普通內(nèi)存分配(如malloc,new),有著更快的速度

額外的,你還將獲得如下好處:

1、檢測任意的指針是否指向內(nèi)存池內(nèi)

2、生成"heap-dump"

3、各種 內(nèi)存泄漏 檢測:當(dāng)你沒有釋放之前申請的內(nèi)存,內(nèi)存池將拋出斷言

堆內(nèi)存:

是一塊內(nèi)存區(qū)域,區(qū)別于棧區(qū)、全局?jǐn)?shù)據(jù)區(qū)和代碼區(qū)的另一個內(nèi)存區(qū)域。堆內(nèi)存允許程序在運行時動態(tài)地申請某個大小的內(nèi)存空間。堆內(nèi)存是由new分配的內(nèi)存,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便


分享文章:java內(nèi)存池代碼實現(xiàn) java 內(nèi)存池
當(dāng)前地址:http://www.dlmjj.cn/article/dooihod.html