• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            coreBugZJ

            此 blog 已棄。

            垃圾回收與弱引用 (轉)

            在一個允許在堆上動態分配內存空間并且采取隱式內存釋放的程序設計語言里,如何確保內存的正確釋放不再是程序員的關注點,而由運行時環境來提供支持。無法被程序引用的在堆上已分配的內存空間成為垃圾(無用內存單元)。運行時環境要清除垃圾有兩種方式:比較積極的方式,引用計數;與比較懶惰的方式,垃圾回收。


            引用計數方式會為每個已分配內存單元設置計數器,當計數器減少到0的時候就意味著該單元無法再被引用,于是立即執行釋放內存的動作。垃圾回收方式的基本思想是mark-and-sweep(標記-清除),每隔一段時間或者在堆空間不足的時候才進行一次垃圾回收,每次垃圾回收先將所有堆上分配的內存單元標記為“不可到達”,然后從一組根引用開始掃描,把所有從根引用出發可以達到的單元標記為“可以到達”;然后把標記為“不可到達”的內存單元回收到可用的堆空間中。


            這兩種清除垃圾的方式的特點很不一樣。


            其中,引用計數方式有四個主要問題:
            1、如果分配的內存單元本身比較小,則用于計數的計數器所占的空間就會變得明顯(significant),而垃圾回收方式只需要為每個分配的內存單元設置一個比特位的標記;
            2、維護計數器的狀態需要消耗時間。每當一個指針或者引用被賦值的時候,計數器的狀態都要跟著改變。像LISP這樣的語言,幾乎所有操作都涉及改變指針(因為要操縱鏈表),計數器的狀態維護會占據整個程序執行時間中明顯的部分。這并不像MSDN Channel 9上Stephan T. Lavavej對C++ TR1中的介紹中所說的“shared_ptr沒有垃圾回收所帶來的額外時間消耗”,引用計數只不過是把這種消耗平均的分攤到了程序運行的整個過程中而已;
            3、計數器相關的代碼可能會分布在運行時系統,甚至用戶代碼的各處,不便于代碼的維護;
            4、當存在循環引用時,內存的正確釋放會比較復雜。當然并不是不可解決,下文會再提到。


            標記-清除式的垃圾回收則有另外的一些問題:
            1、標記-清除會不定時的產生運算資源消耗的高峰(spike)。在沒有進行垃圾回收的時候,程序可以運行得比較順暢,但在執行垃圾回收的時候一般需要把整個程序停下來并執行標記-清除的過程,除非使用并行回收機制。標記的過程可能很長并且很消耗資源,如果是實時系統則一般無法承受這種消耗高峰而寧可使用引用計數方式將消耗分攤到程序的整個運行過程上。分代式的垃圾回收在一定程度上緩解了這個問題,但并沒有根除消耗高峰的問題;
            2、當你最需要垃圾回收器工作的時候,它的運行效果卻最差。最需要進行垃圾回收的時候顯然是堆上的內存已經快分配盡了的時候,但此時已分配的內存單元很多,需要使用大量時間來做標記,但實際能釋放的內存單元卻未必很多。
            3、標記-清除算法有兩種實現思路,一是“保守式”(conservative),二是“準確式”(exact)。保守式不需要知道內存的具體布局形式,會把棧上和全局區上所有“看起來像指針”的數值看作指針并納入標記計算中;準確式則要求運行時系統清楚的了解內存布局形式,能夠分辨哪些數據是指針哪些不是,并且只將指針納入標記計算中。前者未必能保證內存的準確釋放(但能夠保證正在被引用的內存不被釋放),后者則相對需要消耗更多的內存和更多的時間。
            有名的Boehm GC就是保守式的代表。去年開源了的Adobe Virtual Machine 2(AVM2,又稱Tamarin)中的MMgc也是保守式的。未換用Tamarin之前的SpiderMonkey則使用了準確式的垃圾回收。

             
            =========================================================================================

             
            當代許多程序設計語言的運行時系統都采用了隱式內存釋放的設計,并出于各自的設計目標選用了不同的清除垃圾的方法。


            在許多采用引用計數方式實現垃圾清除的系統中都有所謂的弱引用,例如說Squirrel,為的是解決循環引用的問題。讓我們來看看Squirrel 2.2參考手冊里的一段描述:


            引用

            Weak References

            The weak references allows the programmers to create references to objects without influencing the lifetime of the object itself. In squirrel Weak references are first-class objects created through the built-in method obj.weakref(). All types except null implement the weakref() method; however in bools, integers and float the method simply returns the object itself(this because this types are always passed by value). When a weak references is assigned to a container (table slot, array, class or instance) is treated differently than other objects; When a container slot that hold a weak reference is fetched, it always returns the value pointed by the weak reference instead of the weak reference object. This allow the programmer to ignore the fact that the value handled is weak. When the object pointed by weak reference is destroyed, the weak reference is automatically set to null.


            換句話說,如果我們預先知道可能出現循環引用狀況,而其中一些引用并不需要那么“強”,那么我們就可以使用弱引用來消除循環引用的問題。在這個語境下,弱引用就是不會影響計數器狀態的引用。這意味著即使我們擁有對某個對象的弱引用也不會阻止它被清除;也就是說,我們并不能知道手上的弱引用是否指向一個有效的對象。一般弱引用的實現都會保證當某個弱引用指向的不是有效對象時它會被設置為空值,例如null、nil、Nothing之類。


            有人為Visual Basic 5/6也提出了弱引用的實現方法:Avoiding Circular References: WeakReference in VB-Classic。


            微軟的Component Object Model(COM)可以說是應用的最廣泛的采用引用計數式垃圾清除的系統吧,但它的IUnknown.AddRef和IUnknown.Release方法沒少給程序員帶來頭疼,而且也沒提供解決統一的循環引用檢測機制。即便如此現在還是有很多大型系統運行于其上,而且看起來好好的(搖頭
             不,不都是好好的。想想IE的內存泄漏問題吧。在IE里用JScript想要造成內存泄漏還是挺簡單的,不知道怎么做的話搜一下吧。^ ^

            吉里吉里2也采用了引用計數方式的垃圾清除,但為了保證垃圾的清除,特別是引用的循環和程序結束時的資源釋放,做了許多特別措施。這個另外在找時間寫。


            =========================================================================================

             
            如上文所述,在引用計數環境中使用弱引用主要是為了解決循環引用帶來的問題。而標記-清除方式的垃圾回收并不會受循環引用的影響:假如一組對象相互存在引用,而從根引用組出發已經無法達到它們之中的任何一個,則它們仍然會被回收。


            但是許多采取標記-清除方式垃圾回收的環境也提供了弱引用。為什么呢?


            首先想到的,弱引用肯定是為了解決問題而存在的;也就是說垃圾回收還是有問題,無法根除內存泄漏的問題。


            在這種環境下的內存泄漏經常是人為失誤造成的:無意的長時間持有了對已經不需要的對象的引用。這種引用經常存在于生命期特別長的對象中,例如一些全局對象中;Java和C#等語言雖然不允許在類之外定義全局數據,但類變量(而不是成員變量)或者諸如singleton等的特例的表現與C/C++中的全局變量并沒有什么區別。

             
            為了解決這樣的問題,像Java和.NET Framework這樣的平臺也提供了弱引用機制。與引用計數環境一樣,弱引用也不影響判定某個內存單元是否為垃圾的標記計算。

             
            在Java中有三種“弱”引用:java.lang.ref.WeakReference<T>、java.lang.ref.SoftReference<T>、java.lang.ref.PhantomReference<T>。
             .NET Framework中也有System.WeakReference。


            下面幾篇文章對它們分別做了介紹:


            Java theory and practice: Plugging memory leaks with weak references
            Understanding Weak References
            C# WeakReference Example


            =========================================================================================

             
            參考


            Concepts of Programming Languages, Seventh Edition, Heap Management, Pages 301-305, by Robert W. Sebesta。

            P.S. 上文把"reference counting"與"garbage collection"(GC)放在了同一層次來討論,但GC的定義在許多資料中都不一樣。我個人的看法是"reference counting"只是與"mark-and-sweep"一樣,屬于GC的一種策略;GC這個概念應該比reference counting和mark-and-sweep的層次更抽象才對。不過由于本文參考的資料里是按照"reference counting"和"garbage collection"來區分的,這里也就沿用了。

            posted on 2012-03-17 15:26 coreBugZJ 閱讀(644) 評論(0)  編輯 收藏 引用 所屬分類: ProgrammingLanguage

            天天爽天天爽天天片a久久网| 少妇精品久久久一区二区三区| 久久久久婷婷| 久久91这里精品国产2020| 久久伊人色| 午夜精品久久久久久99热| 日日噜噜夜夜狠狠久久丁香五月| 精品人妻久久久久久888| 久久精品欧美日韩精品| 国产精品久久久久乳精品爆| 欧美粉嫩小泬久久久久久久| 久久人人爽人人爽人人AV东京热| 久久se精品一区二区| 久久久久亚洲精品无码网址| 777午夜精品久久av蜜臀| 伊人色综合久久| 久久久久久久久无码精品亚洲日韩| 久久亚洲精品视频| 久久久亚洲AV波多野结衣 | 亚洲国产精品婷婷久久| 国产精品成人久久久| 国内精品久久久久久久久电影网| 久久影院综合精品| 青青热久久国产久精品| 麻豆成人久久精品二区三区免费| 久久只这里是精品66| 久久线看观看精品香蕉国产| 97精品伊人久久久大香线蕉 | 色妞色综合久久夜夜| 日韩欧美亚洲综合久久影院Ds| 欧美久久精品一级c片片| 狠狠色婷婷久久综合频道日韩| 久久人妻少妇嫩草AV蜜桃| 色狠狠久久AV五月综合| 综合久久给合久久狠狠狠97色| 久久久精品视频免费观看| 久久99免费视频| 精品久久综合1区2区3区激情 | 色综合久久88色综合天天| 久久99精品久久久久久hb无码| 亚洲色大成网站www久久九|