文章目錄
- 一.什麼是垃圾回收機制
- 二.為什麼要有垃圾回收機制
- 三.垃圾回收機制的原理
- 1.引用計數
- 2.棧區 / 堆區
- 3.總結
- 四.标記清除
- 1.循環引用問題(也叫交叉引用)
- 3.循環引用導緻的結果
- 4.解決方法 : 清除-标記
- 五.分代回收
- 1.效率問題
- 2.解決方法 : 分代回收
一.什麼是垃圾回收機制
- 垃圾回收機制(簡稱GC), 解釋器自帶的一種機制
- 它是一種動态存儲管理技術,自動釋放不再被程式引用的對象所占用的記憶體空間
二.為什麼要有垃圾回收機制
- 程式的運作過程中會申請大量的記憶體空間
- 對于一些無用的空間如果不及時清理的話會導緻記憶體溢出(不夠用),程式就會崩潰
- 管理記憶體是非常複雜的事情,垃圾回收機制就把程式員從複雜的記憶體管理中解放出啦
三.垃圾回收機制的原理
1.引用計數
引用計數就是變量名與變量值的關聯次數, 以此來跟蹤和回收垃圾
- 直接引用
🌴通過變量名直接引用
x = 18 #18被引用了一次,計數為1
y = x #18被引用加1次,計數為2
z = y #18被引用加1次,計數為3
print(id(x)) #140725488808736
print(id(y)) #140725488808736
print(id(z)) #140725488808736
- 間接引用
🌴容器對其的引用都是間接
x = 18 #18被引用一次,計數為1
li = [1,2,x] #通過清單引用,計數加1,為2
dic = {'age': x} #通過字典引用, 計數加1,為3
print(id(x)) #140725486514976
print(id(li[2])) #140725486514976 清單引用,計數4
print(id(dic['age'])) #140725486514976 字典引用,計數5
2.棧區 / 堆區
- 棧區 : 存放的是變量名與變量值的記憶體位址映射關系
- 堆區 : 存放的是值真正的位置
3.總結
- 直接引用指的是從棧區出發直接引用到的記憶體位址
- 間接引用指的是從棧區出發引用到堆區後,再通過進一步引用才能到達的記憶體位址
四.标記清除
1.循環引用問題(也叫交叉引用)
🌴我們先定義清單
l1=[0] # 清單1被引用一次,清單1的引用計數變為1
l2=[1] # 清單2被引用一次,清單2的引用計數變為1
🌴将清單加入另一個清單
l1.append(l2) # 把清單2追加到l1中作為第二個元素,清單2的引用計數變為2
l2.append(l1) # 把清單1追加到l2中作為第二個元素,清單1的引用計數變為2
🌴解除比變量名"l1"和"l2"與值的對應關系
del l1
del l2
3.循環引用導緻的結果
- 值不再被任何名字關聯,但是值的引用計數并不會為0
- 應該被回收但又不能被回收
4.解決方法 : 清除-标記
- 容器對象的的引用都有可能産生循環引用, 而清除-标記就是為解決這個問題的
- 當應用程式可用空間被耗盡時, 清除-标記會停止整個程式, 然後先标記, 再清除
🌴标記
但凡是可以從棧區出發,找到對應堆區内容的(直接或間接引用)就标記存活,非存活則清除
具體點:标記的過程其實就是,周遊所有的"GC Roots"對象(棧區中的所有内容或者線程都可以作為"GC Roots"對象)
然後将所有"GC Roots"的對象可以直接或間接通路到的對象标記為存活的對象,其餘的均為非存活對象,應該被清除
🌴清除
周遊堆中的對象,将沒有标記存活的對象都清理掉
五.分代回收
1.效率問題
- 基于引用計數的回收機制,每次回收記憶體,都需要把所有對象的引用計數都周遊一遍
- 這是非常消耗時間的,于是引入了分代回收來提高回收效率
- 分代回收采用的是用**“空間換時間”**的政策。
2.解決方法 : 分代回收
- 分代
🌴分代指的是根據變量的存活時間來劃分他們的等級
🌴一個變量經常被引用,等級(權重)就會提高,權重達到設定值就會進入下一個等級
🌴當經過多次掃描都沒有被回收,"GC機制"就會認為該變量是常量
🌴于是對其的掃描頻率會降低
- 回收
🌴當計數降低,就容易被回收
🌴分代回收可以起到提升效率的效果,但也存在一定的缺點:
🐏比如一個變量剛從低等級轉入高等級,它就被解除了綁定關系
🐏它應該被回收,但高等級掃描頻率低于低等級
🐏那麼這個已被解除綁定關系的變量無法及時得到清理
- 總結
🌴垃圾回收機制是在清理垃圾和釋放記憶體的前提下
🌴允許一些垃圾不被釋放為代價(就是等級權重高點的垃圾不會及時被清理)
🌴以此換取引用計數掃描頻率的降低,進而提升其性能
🌴這是一種以空間換時間的解決方案