2017-07-28 62 views
2

我一直在閱讀有關的方法來減少我的Ruby/Rails應用程序的內存使用情況,以及一件事是mentioned被凍結的對象。根據Activity Monitor的說法,我已經嘗試了下面的代碼(MRI,Ruby 2.3.3),並且它確實節省了內存,而不是凍結字符串。然而,如果我對哈希文字嘗試相同,它會使用大量內存,除非我將哈希分配給變量並凍結它。爲什麼冷凍散列文字與凍結字符串文字不一樣?

pipeline = [] 
100_000.times { pipeline << {hello: 'world'}.freeze } # Uses about 25MB 

my_hash = {hello: 'world'} 
my_hash.freeze 
100_000.times { pipeline << my_hash} # This uses about 1MB 

任何人都可以解釋爲什麼嗎?我一直認爲字符串案例有點奇怪,因爲它看起來就像創建大量不同的字符串對象,單獨凍結每個字符串對象,並向數組中添加大量凍結對象。不知道它爲什麼起作用,但是,嘿,它的確如此。現在,哈希案更符合我的預期,但我不知道爲什麼它不會像字符串一樣。

回答

2

這可能是Ruby的優化器可以識別字符串爲從一個循環相同的下一個的情況下,但它無法識別該散列爲相同的所以它是新的。在第二個變體中,您實際上使用相同的散列,以便優化程序可以處理它。

爲了證明這一點,看看這個:

pipeline = [] 
100_000.times { pipeline << 'hello world'.freeze } 
pipeline.map(&:object_id).uniq.length 
# => 1 

這是相同對象的數組,只有一個分配。

pipeline = [] 
100_000.times { pipeline << {hello: 'world'}.freeze }  

pipeline.map(&:object_id).uniq.length 
# => 100000 

這是100,000個不同的對象。

+0

謝謝!我試過你的第一個例子,但我得到的長度是100000.無論如何,我想我知道你的意思。我檢查了數組中的對象ID並注意到裏面的對象是相同的。我現在發現了一些關於Ruby 2.1的東西,看起來他們當時確實對解釋器做了一些改變,以便讓字符串的行爲像你說的那樣。 – flavio

+0

Ruby的每個重要版本都增加了更多優化。嘗試2.4,你可能會得到更接近我在這裏得到的結果。 – tadman

+1

我認爲你需要這個測試的'pipeline.map(&:object_id).uniq.length',不是嗎?無論如何,只是爲了獲得信息,我分別在2.2.5和2.4.1中獲得了1和100000這些測試。 –

2

任何人都可以解釋,爲什麼?我一直認爲字符串案例有點奇怪,因爲它看起來就像創建大量不同的字符串對象,單獨凍結每個字符串對象,並向數組中添加大量凍結對象。

表達形式

'string literal'.freeze 

是一種特殊的表達形式,是特例,由語言。它不僅凍結字符串對象,還執行重複數據刪除。 (類似的符號。)

這是一個特例,表達形式。這是評估字符串文本,然後將其發送的消息freeze。相反,它被視爲一個單一的實體,如果你願意的話,它是一種不同形式的字符串文字。

事實上,原提案介紹文字是這樣一種不同形式的字符串:

'string literal'f 

的提議改爲使其向前兼容:'foo'f將是一個語法錯誤,如果你不得不在老版本的Ruby中運行你的代碼,而'foo'.freeze只是在老版本的Ruby中以相同的方式工作,它只使用更多的內存。

注:這意味着它僅作品的文字。在這裏,串去重複:

'foo'.freeze 

這裏,是不是:

foo = 'foo' 
foo.freeze 

不知道爲什麼它的工作原理,但是,嘿,它做到了。

基本上,它的工作原理,因爲語言規範如此說。

現在,散列情況更符合我的預期,但我不知道它爲什麼不像字符串。

再次,它不起作用,因爲語言規範只是特殊情況下的字符串文字。