ACDK白皮书-内存管理
翻译:薛长宇
一个程序设计语言重要的方向是实现了内存管理概念.
本章内容:
引用计数
垃圾回收
第一步标记对象
第二步删除没有外部引用的对象
堆栈对象
用户堆
引用计数
对于每一个ACDK的类,都有使用’R’作为前缀引用类型。
一个RstringBuffer的例子,一个引用类似于StringBuffer对象的例子.
RStringBuffer sb = new StringBuffer("ACDK");
变量’sb’保存了一个StringBuffer实例的引用。一个对象可以被销毁,如果他没有更多的变量保存对他的引用,引用计数是一种高效灵活内存内存使用策略
引用计数的例子:
RStringBuffer append(RString s)
{
RStringBuffer sb = new StringBuffer(); // RefCount = 1
{
RStringBuffer sb2 = sb; // RefCount = 2
sb2->append(s);
} // RefCount = 1
return sb; // RefCount = 2
} // RefCount = 1
void foo()
{
RString str = new String("Hallo"); // RefCount = 1
RStringBuffer sb = append(str); // str.RefCount = 2,
// sb.RefCount = 1
// str.RefCount = 1
} // str.RefCount = 0 => destroy String
// sb.RefCount = 0 => destroy StringBuffer
垃圾回收
垃圾回收是对象基于这样一个事实的情况下,不能被静态对象引用或者本地的变量对象已经被释放。引用计数的缺点是在循环引用的对象的时候,引用计数器永远也不可能是NULL.因此, 全部的数据结构不能被释放. 垃圾回收机制没有这个限制.
循环引用:
class A {
public:
RObject other;
};
{
RA a1 = new A(); // a1->refCount = 1
RA a2 = new A(); // a1->refCount = 1
a1->other = a2; // a2->refCount = 2
a2->other = a1; // a1->refCount = 2
}
// a1->refCount = 1, a2->refCount = 1
这个垃圾回收机制已经被使用ACDK中了。他是基于2个步骤标记/清除算法.标记/清除算法背后的主要想法是找出一个对象是否直接或者间接地被外部访问。
例子
第一步标记对象(本节没有翻译因为翻译后感觉不如原文清晰)
Object 1 referenced from an object outside of the set(external reference): Object gets marked.
2 is not referenced from an object: 2 is not marked.
5 gets referenced from 6. 6 is inside the set: continuing with 6
6 gets referenced from 7. 7 is inside the set: continuing with 7
7 gets referenced from 8. 8 is outside of the set: 7 gets marked. Back to 6.
6 gets referenced from a marked object (7) : 6 gets marked. Back to 5.
5 gets referenced from a marked object (6) : 5 gets marked.
9 gets referenced from 5 : 9 gets marked.
For 10, 11 und 12: All objects only have internal, cyclic references. Objects do not get marked.
.
第二步删除没有外部引用的对象
在步骤2,所有有没有标记的对象将被释放, 因为他们没有什么会去访问他们了. 内存区域一般被指向到静态数据段, 堆栈和堆. 如果一个对象没有被直接或者间接的被静态数据段访问,那么他们就可以被移除了.
垃圾回收机制的一个缺点是,它必须知道所有的对象并且在很深的引用关系中查找引用,这需要花费相当多的时间。传统的垃圾回收工作在后台,而且也必须付出多个线程的同步的代价
经典的内存回收工具是主流的解释语言的主流,因为在他们的运行环境中必然需要熟悉和控制对象
栈框架(stackframe)的精确结构是大家所熟知的,平台的CPU 寄存器和地址寄存器的访问必然要用类似于C/C++的编译语言.
这个两步机制是ACDK内存管理的工具. 引用计数被使用在标准的机构上,垃圾回收仅仅被使用在循环引用的时候.
ACDK 通过下面的方式来改进垃圾回收带来的负面影响:
* 垃圾回收是可选的
* 垃圾回收是作为引用技术的补充和增强.
* 堆可以被通过线程管理, 因此同步并不是所有线程必需的.
* ACDK 也可以在堆栈上生成.
*ACDK 甚至允许选择内存管理机制.
堆栈对象
对象引用的实现类似于JAVA对象. 因此, 访问无效的对象将产生空指针异常(NullPointerException )或者使用同步方法保护对象的并发访问.
为了在运行时可提供合适的运行开销,同步对象在堆栈中分配.
不像在C/C++, 这没有任何更多的运行时刻的开销
堆对象和堆栈对象的不同:
// 使用堆栈对象t
StringBuffer sb(10); // NO cost for allocating with new
for (int i = 0; i < 10; i++) {
sb.add(i); /* NO cost for checking sb == Nil +
NO cost for dispatch virtual function +
NO cost for synchronizing in add
*/
}
-------------------------------------------------------------------------
void
addSomething(RStringBuffer sb) //引用对象
{
sb->add("Save!"); /* cost for checking sb == Nil +
cost for dispatch virtual function +
NO cost for synchronizing in add */
}
StringBuffer sb(10);
addSomething(SR(StringBuffer, sb)); // 转换StringBuffer 到 RStringBuffer
-------------------------------------------------------------------------
void
addSomething(StringBuffer& sb) // 堆栈对象
{
sb.add("Fast!"); /* NO cost for checking sb == Nil +
NO cost for dispatch virtual function +
NO cost for synchronizing in add */
}
RStringBuffer sb = getStringBufferFromElseWhere();
addSomething(*sb); // cost for checking sb == Nil
用户堆
高级的可以定义堆来实现内存.
下面的策略是允许的:
* 大容量存储器. 快速的可以重新申请的内存,在所有计算结束后才被释放掉,在这之前即使对象执行完,也不释放.
* 实时内存. 大容量存储器可以为进程提供实时能力.
* 共享内存. 用来进行进程间的通讯,同步机制使用互斥对象管理.
* 内存映射文件. 很容易在内存结构中映射文件中的数据结构.
* ROM. 在只读内存中的对象映像.
* 缓冲池. 当时用很多形式相同的对象时,缓冲池用来提供高性能. 因此 释放的对象仅仅是结束了,但是并没有释放,并且他们可以被下一次的请求重新使用
本文是ACDK的相关文章,由于本人的翻译水平实在有限,因此难免有错误,请大家见谅
翻译:薛长宇
2004-12