1. 程式人生 > >Java與C++如何處理迴圈引用問題

Java與C++如何處理迴圈引用問題

最近刷題剛剛研究過這個問題。

何為迴圈引用
如果有兩個或者以上的物件,它們彼此引用,就會造成迴圈引用。如下面的例子

class Node {
Node next ;
}
Node a = new Node ();
Node b = new Node ();
a . next = b ;
b . next = a ;

程式碼中,a物件引用了b物件,b物件也引用了a物件,這種情況下a物件和b物件就形成了迴圈引用。

  1. 引用計數GC(java)

    • 什麼是引用計數

引用計數是一種垃圾回收的形式,每一個物件都會有一個計數來記錄有多少指向它的引用。其引用計數會變換如下面的場景
當物件增加一個引用,比如賦值給變數,屬性或者傳入一個方法,引用計數執行加1運算。
當物件減少一個引用,比如變數離開作用域,屬性被賦值為另一個物件引用,屬性所在的物件被回收或者之前傳入引數的方法返回,引用計數執行減1操作。
當引用計數變為0,代表該物件不被引用,可以標記成垃圾進行回收。

  • 如何處理

實際上單純的基於引用計數實現的計數器無法處理迴圈引用帶來的問題。

CPython的垃圾回收就是採用引用計數,採用引用計數的主垃圾回收器會清理垃圾,對於那些因為迴圈引用無法清理的物件,CPython會不時啟動一個輔助的基於引用遍歷的垃圾回收器來清理它們。
引用遍歷GC處理

  1. 什麼事是引用物件遍歷
    垃圾回收器從被稱為GC Roots的點開始遍歷遍歷物件,凡是可以達到的點都會標記為存活,堆中不可到達的物件都會標記成垃圾,然後被清理掉。 GC Roots有哪
    類,由系統類載入器載入的類。這些類從不會被解除安裝,它們可以通過靜態屬性的方式持有物件的引用。注意,一般情況下由自定義的類載入器載入的類不能成為GC Roots
    執行緒,存活的執行緒
    Java方法棧中的區域性變數或者引數
    JNI方法棧中的區域性變數或者引數
    JNI全域性引用
    用做同步監控的物件
    被JVM持有的物件,這些物件由於特殊的目的不被GC回收。這些物件可能是系統的類載入器,一些重要的異常處理類,一些為處理異常預留的物件,以及一些正在執行類載入的自定義的類載入器。但是具體有哪些前面提到的物件依賴於具體的JVM實現。

    • r如何處理
      基於引用物件遍歷的垃圾回收器可以處理迴圈引用,只要是涉及到的物件不能從GC Roots強引用可到達,垃圾回收器都會進行清理來釋放記憶體
  1. 對於C++而言
    標頭檔案迴圈引用:兩個類互相引用,導致單純互相引用標頭檔案,從而無法通過編譯。如類StrOp和類Other之間錯誤的互相引用如下:
    1).在StrOp.h
#include"Others.h"
class StrOp{
...
private:
    Others oth;
};

2)在Others.h

#include"StrOp.h"
class Others{
...
private:
    StrOp strop;
};

結果:編譯不通

改正之後的結果如下

連結:https://www.nowcoder.com/questionTerminal/15be363c213f437081abf0b31b727951
來源:牛客網

.在StrOp.h

class Others;
class StrOp{
...
private:
    Others* oth;//在此不用指標,編譯不通,原因請看最後解釋
};
//解釋:這個宣告,有時候被稱為前向宣告(forward declaration),在程式中引入了類型別的Others.在宣告之後,定義之前,類Others是一個不完全型別(incompete type),即已知Others是一個型別,但不知道包含哪些成員. 不完全型別只能以有限方式使用,不能定義該型別的物件,不完全型別只能用於定義指向該型別的指標及引用,或者用於宣告(而不是定義)使用該型別作為形參型別或返回型別的函式.
2.在Other.h
#include"StrOp.h"
class{
...
private:
StrOp strop;
};