Close Advertisement
这篇文章估计要写一些时间,因为我即然写了GC机制探究,我就想用C++的语言来自己模拟下这个mark-sweep算法。也许不会在本文出现,但至少对于理解如何写好Jscript和Script或者说理解GC来说,是很有好处的。
关键词:GC(Garbage Collector), closure, memory leak
文章思路——了解IE的GC回收机制,结合MS的一些对其Memory Leak的解释,针对代码作相应的注释,这样对IE下的memory leak心里就自然有一把秤了。
1。GC(垃圾回收器)
在IE6.0中,对于javascript object内部,jscript使用的是nongeneration mark-and-sweep算法,而对于javascript object与外部object(包括native object和vbscript object等等)的引用时,IE 6使用的才是计数器的算法,引用计数法(Reference Counting Collector) ,下面是对于GC的两种处理方法的一些资料和一些自己的理解:
引用计数法是唯一没有使用根集的垃圾回收得法,该算法使用引用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1。当对象出了作用域后(该对象丢弃不再使用),引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。
基于引用计数器的垃圾收集器运行较快,不会长时间中断程序执行,适宜地必须 实时运行的程序。但引用计数器增加了程序执行的开销,因为每次对象赋给新的变量 ,计数器加1,而每次现有对象出了作用域生,计数器减1。
tracing算法(Tracing Collector)
tracing算法是为了解决引用计数法的问题而提出,它使用了根集的概念。基于tracing算法的垃圾收集器从根集开始扫描,识别出哪些对象可达,哪些对象不可达,并用某种方式标记可达对象,例如对每个可达对象设置一个或多个位。在扫描识别过程中,基于tracing算法的垃圾收集也称为标记和清除(mark-and-sweep)垃圾收集器。用程序的来表达可以这样:
for each root variable r
mark (r);
sweep ();
void mark (Object p)
if (!p.marked)
p.marked = true;
for each Object q referenced by p
mark (q);
void sweep ()
for each Object p in the heap
if (p.marked)
p.marked = false
else
heap.release (p);
执行过程如图:mark (r);
sweep ();
void mark (Object p)
if (!p.marked)
p.marked = true;
for each Object q referenced by p
mark (q);
void sweep ()
for each Object p in the heap
if (p.marked)
p.marked = false
else
heap.release (p);
2。对于IE下的几种内存泄露
1),最常见的就是Circular References(循环引用),看这张图:
举一个明显的例子。
<html>
<head>
<script language="JScript">
var myGlobalObject;
function SetupLeak()
{
// First set up the script scope to element reference
myGlobalObject =
document.getElementById("LeakedDiv");
// Next set up the element to script scope reference
document.getElementById("LeakedDiv").expandoProperty =
myGlobalObject;
}
function BreakLeak()
{
document.getElementById("LeakedDiv").expandoProperty =
null;
}
</script>
</head>
<body onload="SetupLeak()" onunload="BreakLeak()">
<div id="LeakedDiv"></div>
</body>
</html>
<head>
<script language="JScript">
var myGlobalObject;
function SetupLeak()
{
// First set up the script scope to element reference
myGlobalObject =
document.getElementById("LeakedDiv");
// Next set up the element to script scope reference
document.getElementById("LeakedDiv").expandoProperty =
myGlobalObject;
}
function BreakLeak()
{
document.getElementById("LeakedDiv").expandoProperty =
null;
}
</script>
</head>
<body onload="SetupLeak()" onunload="BreakLeak()">
<div id="LeakedDiv"></div>
</body>
</html>
如果一会儿还不能理解代码,请看下面的示意图:
那随之而来一个问题,为什么这样会有Memory Leak?
在这里有一篇文章可以探究http://blogs.msdn.com/ericlippert/archive/2003/09/17/53038.aspx。
The benefits of this approach are numerous, but the principle benefit is that circular references are not leaked unless the circular reference involves an object not owned by JScript.
"采用此方法带来很多好处,其根本的益处在于处于循环引用不会被释放,直到Jscript的Object不在属于此循环引用当中。"
其根本原因也是由于IE使用的是GC回收机制的问题。在上文已经给出了nongeneration mark-and-sweep算法示意图。
IE中可以正常的处理Script级的纯Object之间循环引用,也就是这样
<script>
var objA = new Object; objA.a = "never-online";
var objB = new Object; objB.ref = objA;
var objRef = new Object;
objRef = objB.ref;
alert(objRef.a);
delete objRef;
delete objB;
delete objA;
</script>
var objA = new Object; objA.a = "never-online";
var objB = new Object; objB.ref = objA;
var objRef = new Object;
objRef = objB.ref;
alert(objRef.a);
delete objRef;
delete objB;
delete objA;
</script>
而不能正确的处理,Object与DOM之间交互的循环引用。如第一个例中所展示的那样出现Memory Leak。
这样应该很好理解了。再举一个例子。
//TODO
Javascript的垃圾回收机制
在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。
文章继续编写中...
[最后修改由 Rank, 于 2008-04-15 15:35:21]
评论Feed: http://www.never-online.net/blog/feed.asp?q=comment&id=105





我用手机上网容易吗,打开就看到个文章编写中。
QWERT键盘让我输入不在是瓶颈。:)
等你下文啦