漫談GC——小心遞歸中產(chǎn)生的內(nèi)存溢出

之前的文章中有系統(tǒng)的講過GC相關(guān)的理論知識方库,如果對GC相關(guān)的理論知識不太理解的朋友障斋,可以閱讀一下:漫談GC —— GC基本理論和深度剖析

提到遞歸,很多人的第一反應(yīng)就是著名的 StackOverflowException, 棧溢出錯誤, 能夠理解遞歸調(diào)用的邏輯是操作系統(tǒng)上的一個壓棧操作,通常情況下晴裹,棧的內(nèi)存非常小,所以調(diào)用層次很深的話就會產(chǎn)生類似的錯誤只磷。我做了一個小測試,看一下調(diào)用多少層之后预厌,我的系統(tǒng)會報出棧溢出元媚。

代碼如下(C#實現(xiàn)):

public class TestClass
    {
        public void M1(int num)
        {
            if (num < int.MaxValue)
            {
                num++;
                Console.WriteLine(num);
                M1(num);
            }
        }
    }

可以看到我設(shè)置的遞歸出口是int自增到int最大值的時候退出。

棧溢出統(tǒng)計

可以看到我的機器大概會在51520次的時候炭晒,棧溢出甥角。由于方法里面幾乎沒有額外操作,所以這個數(shù)值比實際情況中大一些震束,而且計算機不同当犯,結(jié)果肯定也不一樣,日常的方法調(diào)用層數(shù)一般不會大于這個數(shù)值嚎卫,所以常規(guī)的開發(fā)中,棧溢出基本上不會碰到胸懈。這里不是為了測試棧溢出的臨界值的恰响,所以只是簡單理解一下棧溢出的異常就夠了。

那這里提出一個問題首有,遞歸有沒有可能內(nèi)存溢出呢枢劝?

看下面這段測試代碼(C#):

       public void M1(int num)
        {
            Console.WriteLine("倒數(shù)第:" + num); //輸出
            int[] arr = new int[100000]; //定義了一個長度為10W的數(shù)組
            for (int i = 0; i < arr.Length; i++)
            {
                arr[i] = 100;
            }
            if (num > 0)
            {
                M1(num - 1);
            }
        }
       public static void Main(string[] args)
        {
            temp.M1(50000);//由于我的電腦的棧溢出大概在50000以上,所以設(shè)置50000烙常,看一下效果
        }

上面的代碼也不難理解,有幾點提及一下:

  • 我的電腦 的棧溢出在50000+蚕脏,所以調(diào)用層數(shù)設(shè)置為50000驼鞭,
  • 每次遞歸的時候,定義了一個比較大的數(shù)組挣棕,長度為10W,每次賦值之后沒有額外的任何操作固耘。

我們看一下執(zhí)行的結(jié)果:

內(nèi)存溢出

可以看到词身,發(fā)生了OutOfMemoryException內(nèi)存溢出,而遞歸只進行了不到10000次。

C# & Java GC算法


這里不談太多的理論上的內(nèi)容户辫,理論上的細節(jié)可以看我在開篇引言中提到的另一篇文章渔欢。
C#與Java中,GC采用的是GC Root 的鏈路可達性分析算法解決的GC標記問題奥额。原理就是一個對象只要有GC Root引用,就不會釋放韩肝,這個不難理解九榔,那么什么對象會被定義為GC Root對象呢?
GC Root對象大概包括這幾類(我在概念文章里也提及了):

  • 虛擬機棧對象(JVM哲泊,CLR等)
  • OS 棧(操作系統(tǒng)本地方法的棧切威,Native)
  • 方法區(qū)中的常量或者靜態(tài)引用的對象等。

上面的例子就是由于方法壓棧先朦,產(chǎn)生的問題犬缨,每一個新生成的數(shù)組锋谐,都被壓棧進去的方法所引用涮拗,進行GC的時候,都是可達對象三热,無法GC,所以不會被釋放呐能。

實際生產(chǎn)問題


這里描述一下當時的問題:
當時是有一個服務(wù)器的文件夾抑堡,里面存儲了大量的mp3文件,遞歸的過程中首妖,需要把他們統(tǒng)一取出來有缆,放入一個數(shù)組,過程中使用了遞歸的方式遍歷所有文件夾下的文件棚壁,生成了大量的臨時字符串,而字符串本質(zhì)上也是一個byte數(shù)組史隆,長度很大的話曼验,也是比較消耗內(nèi)存的,遞歸過程中同樣不被釋放魄幕,而服務(wù)器的內(nèi)存較小颖杏,只有2GB。本地測試的時候,機器是16G電腦咙轩,沒有問題阴颖,但是到服務(wù)器上就崩潰了。


遞歸雖然是很優(yōu)雅的編程方式钾菊,在諸多算法里面也是熱點偎肃,但是同樣不能忽視遞歸調(diào)用過程中的棧溢出,以及上文提及的內(nèi)存溢出累颂。

后記

Go語言的垃圾回收機制,沒有使用鏈路可達性分析料饥,而是三色標記法朱监,細節(jié)可以看這里

這里還是貼一段代碼:

package main

var count = 0
func main() {
    arr := make([]int,10)
    test(arr)
}

func test(arr []int ) []int {
    if count == 20 {
            return arr
        }else{
            count++
            caps:= cap(arr)
        for i:= 0;i< 2 * caps;i++{
            arr = append(arr, i)
        }
        println(arr, cap(arr))
        arr = test(arr)
    }
    return arr
}

上面的遞歸算法同樣沒有到20次赌朋,就拋出了內(nèi)存溢出篇裁,請了解Go的小伙伴通過對三色標記法的理解,留言討論一下吧达布。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末黍聂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子产还,更是在濱河造成了極大的恐慌脐区,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炕柔,死亡現(xiàn)場離奇詭異,居然都是意外死亡匕累,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門衰琐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來际插,“玉大人框弛,你說我怎么就攤上這事∩悖” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵僻焚,是天一觀的道長膝擂。 經(jīng)常有香客問我,道長狞山,這世上最難降的妖魔是什么叉寂? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任屏鳍,我火速辦了婚禮,結(jié)果婚禮上钓瞭,老公的妹妹穿的比我還像新娘。我一直安慰自己超埋,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布媒惕。 她就那樣靜靜地躺著来庭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪月弛。 梳的紋絲不亂的頭發(fā)上帽衙,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音恍飘,去河邊找鬼谴垫。 笑死,一個胖子當著我的面吹牛翩剪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚪缀,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼恕出,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起狈醉,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤惠险,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后渣慕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡眨猎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年强经,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兰迫。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡炬称,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出据德,到底是詐尸還是另有隱情府蔗,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布赡译,位于F島的核電站不铆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏誓斥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望距芬。 院中可真熱鬧,春花似錦舀武、人聲如沸离斩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至操软,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間聂薪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工仁锯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留翔悠,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓双炕,卻偏偏與公主長得像撮抓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丹拯,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容