模拟自动回收内存机制(有向图多点可达性)

之前写过一篇边界标识内存管理算法,在分配内存端,分配算法没问题,有很多种,首次适应、最佳适应、最坏适应法等,分配的时机,看用户什么时候申请。在回收端,回收情况分很四种:左/右临界区为空闲块、左右临界区均为空闲块和左右临界区均不为空,根据不同的情况,空闲块回收的方法也不同。那么回收的时机呢?在C语言中,我们要显式地使用free()来回收不再使用的内存,在Java里,我们不需要这么做,自动回收机制会帮我们这么做,它又是怎么实现的呢?它怎么知道当前哪些对象不被使用,哪些对象可以被回收了呢?

在程序执行过程中,很多对象的引用和变量的使用,我们随时都可以访问到这些对象,而对于那些不能被直接访问到的对象,就应该被回收,以释放内存给新的对象使用。那么怎么知道哪些对象能被直接访问到,哪些对象不能被访问?我们看,对象的引用就像指针,把对象看做一个顶点,一个对象对另一个对象的应用看作一个箭头相连,那么多个对象之间的引用关系就构成了一个有向图。

在有向图中,一个顶点可以到达另一个顶点,那么就认为这两个顶点是连通的,就像一个对象可以直接或间接地访问到另一个对象。遍历有向图,找出所有不能被访问的顶点,然后把它们回收,实际的内存管理中回收机制也是这样的,一个顶点表示一个对象,对象之间的引用表示一条边,至于寻找图中无法被访问的顶点,那就是用到有向图中顶点可达性的寻找方法。给定的某一个顶点或是一组顶点,在图中遍历寻找所有从这些顶点开始,可到达的顶点,至于不能到达的顶点,则认为是不再使用的垃圾块或空闲块,将其回收。

有向图用背包数据结构来存储邻接表的方式,背包是一种不能做删除操作得集合数据类型,使用背包的目的是:收集元素并迭代遍历所有收集到的元素。由于我们的有向图无论是深度优先还是广度优先搜索,都要遍历图,所以用背包很合适。

package directeddfs;

import java.util.Iterator;

public class Bag<Item> implements Iterable<Item> {
    private Node first; //链表头结点
    private int N;

    //链表结点的数据结构
    private class Node {
        Item item;
        Node next;
    }

    public boolean isEmpty() {
        return first == null;// or N == 0
    }

    public int size() {
        return N;
    }

    public void add(Item item) {
        Node oldfirst = first;
        first = new Node();
        first.item = item;
        first.next = oldfirst;
    }

    @Override
    public Iterator<Item> iterator() {
        return new ListIterator();
    }

    private class ListIterator implements Iterator<Item> {

        private Node current = first;

        @Override
        public boolean hasNext() {
            return current != null;
        }

        @Override
        public Item next() {
            Item item = current.item;
            current = current.next;
            return item;
        }

        @Override
        public void remove() {

        }
    }
}

背包的实现这里不详述,就是一个链表结构,可以添加元素和遍历,但没有删除元素的方法。来看看有向图的代码:

(有向图的初始化)

对于addEdge()方法,也就是向图中插入一条边,由于是有向图,所以只需要设置一次,v1指向v2就把Graph[ v1 ] = v2,否则相反。

//深度优先搜索
public void DFSSearch(GraphDemo Graph, int tag) {
	mark[tag] = true; //从顶点tag开始
	for(int v : Graph.returnGraph(tag)) {
		if(mark[v] != true) {
			DFSSearch(Graph, v);
		}
	}
}

遍历有向图的方法,可以使用深度优先搜索或广度优先搜索。

用一个boolean类型的mark数组来记录遍历图过程中可以访问到的顶点,在DFS中,每一次被访问到的顶点都把其mark[ v ]的值设置为true。

这里给出两种实现,第一种是单个顶点的可达性搜索:

//在图中查找顶点tag可到达的所有顶点
public dfsSearchDemo(GraphDemo Graph, int tag) {
	mark = new boolean[Graph.returnV()];
	DFSSearch(Graph, tag); //在图中查找tag顶点可达的所有顶点
}

它要做的事情很简单,先获得图中顶点的数量,根据顶点数实例化mark数组,然后在图Graph中寻找并标记与tag连通的顶点,每访问到一个顶点就把mark数组中对应的mark[ v ]设置为true。最后完成一次遍历下来,找出Graph中所有顶点tag可到达的顶点。

对于多个顶点来说,方法一模一样,例如下面这个例子:

//在图中查找集合graph中的多个顶点可到达的所有顶点
public dfsSearchDemo(GraphDemo Graph, Iterable<Integer> graph) {
	mark = new boolean[Graph.returnV()];
	for(int v : graph) {
		if(mark[v] != true) {
			DFSSearch(Graph, v);
		}
	}
}

我们把存放了多个顶点的集合,每次拿出一个顶点来做DFS,最后能获得集合中所有顶点在Graph中可到达的所有顶点(表述得有点拗口- -、)。

看看运行结果:

在典型的内存管理中,系统会定时地运行一个类似于这样有向图的多点可达性算法,来检测程序中的对象引用,然后清除那些不再被引用的对象,释放其内存。

完整实现代码已上传:

https://github.com/justinzengtm/Algorithms/tree/master/DepthFirstSearch/MultipointDirectedDFS

发布了97 篇原创文章 · 获赞 71 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/justinzengTM/article/details/89887040