性能優(yōu)化是使用Ruby開發(fā)web應(yīng)用中一個(gè)比較頭痛的問題,因?yàn)镽uby本身的執(zhí)行性能并不高,而且重量級(jí)應(yīng)用Rails使用ruby的方式又是極其消耗內(nèi)存的雷蹂。我們知道內(nèi)存消耗過大對(duì)性能有影響姆怪,那么內(nèi)存的消耗是如何影響性能,這就需要對(duì)Ruby使用內(nèi)存的結(jié)構(gòu)和GC的工作方式有比較深入的了解宫屠。
內(nèi)存結(jié)構(gòu)
1.Ruby管理的 棧和堆
在Ruby管理的 椓辛疲空間中存放的是 當(dāng)前運(yùn)行棧的RVALUE指針和值,而堆空間存放的是浪蹂,RVALUE值抵栈,其中值的大小為固定的40bytes ,其結(jié)果是 heap 默認(rèn)包含24個(gè)heap page heap page 默認(rèn)包括 408個(gè) slot 一個(gè)slot 存放一個(gè)對(duì)象坤次,集RVALUE(Robject RArray..) (按照Ruby默認(rèn)初始化10_000個(gè)slots 計(jì)算)古劲。其中Ruby 2.1 中 默認(rèn)會(huì)多初始化一個(gè)heap page 也就是25個(gè)heap page,并且堆空間的heap page 是按照 eden 和tomb 兩個(gè)分區(qū)存放的缰猴。在運(yùn)行GC的時(shí)候产艾,Ruby會(huì)從eden中清空不在使用的heap page,然后移動(dòng)到 tomb中標(biāo)記為free滑绒。
GC: x: 需要掃描回收的對(duì)象數(shù)量
y = x/HEAP_OBJ_LIMIT (GC需要掃描的heap page數(shù))
z = 0.8 * (80% of total heap slot count |GC_HEAP_INIT_SLOTS)
2.系統(tǒng)管理的堆
系統(tǒng)管理的堆空間存放的是 數(shù)據(jù)闷堡,即RVALUE中無法存放的的數(shù)據(jù),比如一個(gè)1K的字符串疑故,RVALUE只能存放引用和元信息和23個(gè)字符杠览,剩下真正的數(shù)據(jù)是存放在系統(tǒng)管理的堆空間的,數(shù)據(jù)用完后會(huì)被GC釋放焰扳。
GC觸發(fā)條件
1.僅剩余20%的free slots時(shí)執(zhí)行GC (RUBY_GC_HEAP_FREE_SLOTS) 4096
2.mallocate 分配16MB以上內(nèi)存時(shí)執(zhí)行GC ( RUBY_GC_MALLOC_LIMIT ~ RUBY_GC_MALLOC_LIMIT_MAX 32MBMB區(qū)間個(gè)字節(jié) 具體數(shù)字是Ruby根據(jù)內(nèi)存使用情況動(dòng)態(tài)確定的)
問題和優(yōu)化
Ruby的內(nèi)存使用方式造就了它在多次GC和malloc之后 堆內(nèi)存中會(huì)出現(xiàn)很多的碎片倦零,這些部分的內(nèi)存是部分被利用上的,然后就會(huì)導(dǎo)致堆內(nèi)存不足需要繼續(xù)分配內(nèi)存吨悍,然后再產(chǎn)生更多碎片一尺類推扫茅。
Ruby 2.1之后內(nèi)存heap分為 eden 和 tomb 兩個(gè)部分,分別存放新生對(duì)象和死去的對(duì)象育瓜,當(dāng)你創(chuàng)建對(duì)象的時(shí)候葫隙,ruby會(huì)從eden space 分配內(nèi)存,如果eden中的內(nèi)存不足的話躏仇,再?gòu)膖omb空間中分配內(nèi)存使用恋脚。這么做的原因有兩個(gè)腺办,其一是通過重復(fù)使用 heap space 來減少內(nèi)存空間的碎片。其二是方便解釋器釋放整塊內(nèi)存空間糟描。
那么在真正的使用場(chǎng)景下怀喉,Ruby僅僅釋放了tomb空間10%的內(nèi)存,因?yàn)閞uby解釋器程序具有馬太效應(yīng)船响,使用過多對(duì)象的應(yīng)用躬拢,在未來還會(huì)使用更多的對(duì)象,也就是更多的heap space所以见间,它通過保留80%多的tomb內(nèi)存空間聊闯。這樣未來要使用更多的內(nèi)存空間時(shí),不需要再次分配內(nèi)存(分配內(nèi)存消耗資源)米诉。
優(yōu)化內(nèi)存的使用主要就是兩點(diǎn)菱蔬,一是添加內(nèi)存分配的參數(shù),二是調(diào)整GC觸發(fā)的參數(shù)史侣。下面的幾個(gè)Ruby參數(shù)就是和內(nèi)存使用優(yōu)化相關(guān)的參數(shù):
RUBY_GC_HEAP_INIT_SLOTS: Ruby初始化slots數(shù)量拴泌,數(shù)量越大后續(xù)malloc次數(shù)越小
RUBY_GC_HEAP_GROWTH_FACTOR:內(nèi)存分配增長(zhǎng)因子
RUBY_GC_MALLOC_LIMIT: GC觸發(fā)條件之一, 分配內(nèi)存超過限制后運(yùn)行GC
RUBY_GC_HEAP_FREE_SLOTS:對(duì)堆空間free slot少于該參數(shù)是 觸發(fā)GC
最后這些參數(shù)修改的要點(diǎn)是,需要根據(jù)你現(xiàn)有應(yīng)用的內(nèi)存使用情況來設(shè)置惊橱,比如像Rails這樣的應(yīng)用可以在初始化的時(shí)候就需要分配很多的內(nèi)存弛针,那么就可以一開始吧初始化的slots數(shù)量調(diào)高一些,這樣在應(yīng)用啟動(dòng)時(shí)能夠減少malloc的數(shù)量李皇。