簡體   English   中英

OOP 為二叉樹實施清除 - 所有節點都需要放棄其子節點以進行適當的 GC,還是僅需要放棄根節點?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM