Chromium 采用 Chrome:// 协议开头的形式, 规定了一系列的内部协议, 有的用来显示数据, 有的用来实现一些功能, 但对普通用户进行了屏蔽。在Chrome浏览器地址栏直接访问就好了!
chrome://bookmarks
收藏管理器
chrome://credits
展示所有第三方软件许可证(开放源代码软件)
chrome://dns
展示DNS状态, 一般用来监控排查排查网络问题
chrome://downloads
用网页的形式展示下载管理器
chrome://chrome-urls
内部URL列表
chrome://extensions
展示所有安装的扩展程序
chrome://flags
传说中的里选项, 打开以后会看到警告性的”核辐射”标志, 除非遇到问题(比如GPU什么的), 这里的选项还是不要随便碰.
chrome://flash
展示flash 的详细信息, 对于我们来说, 除了flash 的版本号, 其他信息暂时不需要了解. 而flash 版本号在 chrome://version 中已经进行了集成
chrome://gpu-internals
展示GPU相关信息, 主要是排查问题和调试用
chrome://history
浏览器的历史记录
chrome://media-internals
媒体数据终端, 用来展示在线播放的一些数据. 基本没啥用
chrome://memory
chrome://memory-redirect
非常有用的一个页面, 这里展示了所有标签的资源占用情况.
chrome://net-internals
在chrome 中打开后, 会开始捕获浏览器中所有的网络状况, 用于监视数据, 代理, 请求, DNS, HTTP缓存等资料
chrome://plugins
展示所有的插件, 并可以进行管理.
这也是一个非常有用的页面, 你可以在这里看到Flash, Java 等熟悉的名字
chrome://quota-internals
用来展示浏览器的用户数据磁盘占用
chrome://sessions
用来展示chrome同步数据中的”打开的标签”. 比如你在iPad chrome上打开了一部分标签, 在PC上打开了一部分不同的标签, 这里就会集中显示
chrome://sync-internals
数据同步终端, 用来展示数据同步的所有指标, 应该是用于调试和排错
Web开发的发展
在过去一些的时候,Web开发人员并没有太多的去关注内存泄露问题。那时的页面间联系大都比较简单,并主要使用不同的连接地址在同一个站点中导航,这样的设计方式是非常有利于浏览器释放资源的。即使Web页面运行中真的出现了资源泄漏,那它的影响也是非常有限而且常常是不会被人在意的。
今天人们对Web应用有了高更的要求。一个页面很可能数小时不会发生URL跳转,并同时通过Web服务动态的更新页面内容。复杂的事件关联设计、基于对象的JScript和DHTML技术的广泛采用,使得代码的能力达到了其承受的极限。在这样的情况和改变下,弄清楚内存泄露方式变得非常的急迫,特别是过去这些问题都被传统的页面导航方法给屏蔽了。
还算好的事情是,当你明确了希望寻找什么时,内存泄露方式是比较容易被确定的。大多数你能遇到的泄露问题我们都已经知道,你只需要少量额外的工作就会给你带来好处。虽然在一些页面中少量的小泄漏问题仍会发生,但是主要的问题还是很容易解决的。
泄露方式
在接下来的内容中,我们会讨论内存泄露方式,并为每种方式给出示例。其中一个重要的示例是JScript中的Closure技术,另一个示例是在事件执行中使用Closures。当你熟悉本示例后,你就能找出并修改你已有的大多数内存泄漏问题,但是其它Closure相关的问题可能又会被忽视。
现在让我们来看看这些个方式都有什么:
1、循环引用(Circular References) — IE浏览器的COM组件产生的对象实例和网页脚本引擎产生的对象实例相互引用,就会造成内存泄漏。这也是Web页面中我们遇到的最常见和主要的泄漏方式;
2、内部函数引用(Closures) — Closures可以看成是目前引起大量问题的循环应用的一种特殊形式。由于依赖指定的关键字和语法结构,Closures调用是比较容易被我们发现的;
3、页面交叉泄漏(Cross-Page Leaks) — 页面交叉泄漏其实是一种较小的泄漏,它通常在你浏览过程中,由于内部对象薄计引起。下面我们会讨论DOM插入顺序的问题,在那个示例中你会发现只需要改动少量的代码,我们就可以避免对象薄计对对象构建带来的影响;
4、貌似泄漏(Pseudo-Leaks) — 这个不是真正的意义上的泄漏,不过如果你不了解它,你可能会在你的可用内存资源变得越来越少的时候极度郁闷。为了演示这个问题,我们将通过重写Script元素中的内容来引发大量内存的"泄漏"。
循环引用
循环引用基本上是所有泄漏的始作俑者。通常情况下,脚本引擎通过垃圾收集器(GC)来处理循环引用,但是某些未知因数可能会妨碍从其环境中释放资源。对于IE来说,某些DOM对象实例的状态是脚本无法得知的。下面是它们的基本原则:
Figure 1: 基本的循环引用模型
本模型中引起的泄漏问题基于COM的引用计数。脚本引擎对象会维持对DOM对象的引用,并在清理和释放DOM对象指针前等待所有引用的移除。在我们的示例中,我们的脚本引擎对象上有两个引用:脚本引擎作用域和DOM对象的expando属性。当终止脚本引擎时第一个引用会释放,DOM对象引用由于在等待脚本擎的释放而并不会被释放。你可能会认为检测并修复假设的这类问题会非常的容易,但事实上这样基本的的示例只是冰山一角。你可能会在30个对象链的末尾发生循环引用,这样的问题排查起来将会是一场噩梦。
如果你仍不清楚这种泄漏方式在HTML代码里到底怎样,你可以通过一个全局脚本变量和一个DOM对象来引发并展现它。
<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>
你可以使用直接赋null值得方式来破坏该泄漏情形。在页面文档卸载前赋null值,将会让脚本引擎知道对象间的引用链没有了。现在它将能正常的清理引用并释放DOM对象。在这个示例中,作为Web开发员的你因该更多的了解了对象间的关系。
作为一个基本的情形,循环引用可能还有更多不同的复杂表现。对基于对象的JScript,一个通常用法是通过封装JScript对象来扩充DOM对象。在构建过程中,你常常会把DOM对象的引用放入JScript对象中,同时在DOM对象中也存放上对新近创建的JScript对象的引用。你的这种应用模式将非常便于两个对象之间的相互访问。这是一个非常直接的循环引用问题,但是由于使用不用的语法形式可能并不会让你在意。要破环这种使用情景可能变得更加复杂,当然你同样可以使用简单的示例以便于清楚的讨论。
<head>
<script language="JScript">
function
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
下面就是我的学习笔记,对于Javascript初学者应该是很有用的。
一、变量的作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
function f1(){ alert(n); }
f1(); // 999
另一方面,在函数外部自然无法读取函数内的局部变量。
alert(n); // error
这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
f1();
alert(n); // 999
二、如何从外部读取局部变量?
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
var n=999;
function f2(){ alert(n); // 999 }
}
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
var n = 999;
function f2() { alert(n); }
return f2;
}
var result = f1();
result(); // 999
三、闭包的概念
上一节代码中的f2函数,就是闭包。
各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
四、闭包的用途
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
怎么来理解这句话呢?请看下面的代码。
var n = 999;
nAdd = function () { n += 1 }
function f2() { alert(n); }
return f2;
}
var result = f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
五、使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
本文链接