[英]OOP Implementing Clear for Binary Tree - do all nodes need to disown their children for proper GC, or does only the root node need to be disowned?
我寫了一個關於二叉樹的示例代碼。 向二叉樹添加和遍歷節點都是從根節點開始的。 想清空整個二叉樹怎么辦? 使用 clear1 方法還是 clear2 方法? clear1方法只是將根節點設置為null,clear2方法遍歷每個節點然后將每個節點設置為null,貌似都可以達到清除的目的。 我不知道兩者之間的區別。 如果用clear1不知道不設置為null的節點會不會影響垃圾回收
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class BinaryTree<T> {
public int size;
public Node root;
private Queue<Node> queue;
public class Node {
public T value;
public Node leftNode;
public Node rightNode;
private Node(T value, Node leftNode, Node rightNode) {
this.value = value;
this.leftNode = leftNode;
this.rightNode = rightNode;
}
}
public void add(T addValue) {
Node node = new Node(addValue, null, null);
if (null == root) {
root = node;
queue = new LinkedList<>();
queue.offer(node);
} else {
Node queNode = queue.peek();
if (null == queNode.leftNode) {
queNode.leftNode = node;
queue.offer(queNode.leftNode);
} else if (null == queNode.rightNode) {
queNode.rightNode = node;
queue.poll();
queue.offer(queNode.rightNode);
}
}
size++;
}
/**
* Postorder Traversal
*/
public List<T> postTraverse() {
return postTraverse(new ArrayList<>(), root);
}
private List<T> postTraverse(List<T> list, Node node) {
if (list.size() != size && null != node) {
postTraverse(list, node.leftNode);
postTraverse(list, node.rightNode);
list.add(node.value);
}
return list;
}
/**
* clear Binary Tree method1
*/
public void clear1() {
queue = null;
size = 0;
root = null;
}
/**
* clear Binary Tree method2
*/
public void clear2() {
queue = null;
clear2(root);
size = 0;
}
private void clear2(Node node) {
if (null != node) {
clear2(node.leftNode);
clear2(node.rightNode);
node = null;
}
}
}
測試:
public static void main(String[] args) {
BinaryTree<String> binaryTree = new BinaryTree<>();
binaryTree.add("A");
binaryTree.add("B");
binaryTree.add("C");
binaryTree.add("D");
binaryTree.add("E");
binaryTree.add("F");
binaryTree.add("G");
binaryTree.add("H");
binaryTree.add("i");
binaryTree.add("j");
binaryTree.add("k");
binaryTree.add("L");
binaryTree.add("M");
binaryTree.add("N");
binaryTree.add("O");
System.out.println(binaryTree.postTraverse());
binaryTree.clear1();
}
只需將root
設置為null
是最有效的解決方案。
不知道沒有設置成
null
的節點會不會影響垃圾回收。
假設沒有任何其他對象引用任何Node
對象(如果代碼實現正確,情況應該如此),那么將root
設置為null
會導致整個Node
對象樹無法訪問。 它們通常會在下一個新的空間集合中收集。
遍歷樹並清除所有引用將一事無成,而且實際上會浪費時間。
對於所有現有的 Java GC 實現(某些“回退”收集器可能除外),實際上在無法訪問的堆節點上執行最少的工作。 GC 不需要標記它們,或訪問它們來檢查標記位。 相反,在將所有可達對象復制出正在收集的“空間”之后,它通過(有效地)將零寫入整個空間1來擦除 rest 。
由於 GC 不會以任何其他方式遍歷、標記或查看無法訪問的樹,因此您的代碼對clear2()
中的節點所做的任何更改都是毫無意義的。 實際上,此類更改可能會阻礙 GC。 例如,對於 G1GC,分配的寫后屏障可能會導致“卡片”被不必要地標記為臟,從而延遲收集它們覆蓋的對象。
1 - 你有沒有想過為什么他們將原始和參考字段的默認值定義為false
或零和null
? 一個原因是因為它們都表示為二進制零。
使用第一種方法。 clear2 沒有任何好處,它只是做了很多無用的工作。
即使 GC 確實需要遍歷樹(通常不需要),它也不會比您在 clear2 中的代碼做更多的工作,所以只需執行最少的操作來更新您的樹並讓 GC 去做這是工作。 GC 代碼是 JVM 的一些最優化的區域,不要試圖超越它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.