免费的黄金网站有哪些咋做个人网站
免费的黄金网站有哪些,咋做个人网站,做汉字网站的外国人,福建省建设注册中心网站公众号#xff1a;程序员白特#xff0c;欢迎一起交流学习~ 在日常的业务开发中#xff0c;偶尔会出现内存泄漏的情况#xff0c;那么我们该怎么排查呢#xff1f;现在跟着文章一起学习下吧~
使用Chrome devTools查看内存情况
打开Chrome的无痕模式#xff0c;以屏蔽Ch… 公众号程序员白特欢迎一起交流学习~ 在日常的业务开发中偶尔会出现内存泄漏的情况那么我们该怎么排查呢现在跟着文章一起学习下吧~
使用Chrome devTools查看内存情况
打开Chrome的无痕模式以屏蔽Chrome插件对我们之后测试内存占用情况的影响。然后打开开发者工具找到Performance栏可以看到一些功能按钮如开始录制按钮、刷新页面按钮、清空记录按钮、记录并可视化js内存、节点、事件监听器按钮、触发垃圾回收机制按钮等。 请简单录制一下百度页面观察我们能够获取到什么信息如下动图所示 从图表中我们可以清楚地观察到在页面加载过程中JS Heapjs堆内存、documents文档、NodesDOM节点、Listeners监听器、GPU memoryGPU内存的最低值、最高值以及随时间的变化趋势这是我们关注的重点。
查看开发者工具中的Memory一栏主要用于记录页面堆内存的具体情况以及js堆内存随加载时间线动态的分配情况。 堆快照类似于照相机可以记录当前页面的堆内存情况。每次进行快照都会生成一条快照记录。 根据上图所示我们首先进行了一次快照记录了当时堆内存空间占用为33.7MB。随后我们点击了页面中的一些按钮再次执行了一次快照记录了当时堆内存空间占用为32.5MB。此外通过点击相应的快照记录我们可以查看当时所有内存中的变量情况包括结构和占总内存的百分比等信息。 在记录数据后我们可以观察到图表右上角有起伏的蓝色和灰色柱状图其中蓝色代表当前时间线下所占用的内存灰色表示之前占用的内存空间已被清除释放。
在发现存在内存泄漏的情况时我们可以使用Memory来更清晰地确认问题并定位问题。
首先可以使用Allocation instrumentation on timeline来确认问题如下图所示. 内存泄漏的场景
闭包使用不当引起内存泄漏全局变量分离的DOM节点控制台的打印遗忘的定时器
1. 闭包使用不当引起内存泄漏
使用Performance和Memory来查看一下闭包导致的内存泄漏问题
button onclickmyClick()执行fn1函数/button
scriptfunction fn1 () {let a new Array(10000) // 这里设置了一个很大的数组对象let b 3function fn2() {let c [1, 2, 3]}fn2()return a}let res [] function myClick() {res.push(fn1())}}
/script在fn1函数执行上下文退出后本应将该上下文中的变量a视为垃圾数据并进行回收。然而由于fn1函数最终将变量a返回并赋值给全局变量res这导致对变量a的引用产生使得变量a被标记为活动变量并一直占用相应的内存。如果假设后续不再使用变量res那么这就是一个闭包使用不当的例子。
为了能够在performance的曲线图中观察效果我们设置了一个按钮每次点击执行时将fn1函数的返回值添加到全局数组变量res中。如下图所示 在每次录制开始时手动触发一次垃圾回收机制这是为了确认一个初始的堆内存基准线便于后面的对比。然后我们点击了几次按钮即往全局数组变量res中添加了几个比较大的数组对象。最后再触发一次垃圾回收发现录制结果的JS Heap曲线刚开始成阶梯式上升的最后的曲线的高度比基准线要高说明可能是存在内存泄漏的问题。 在得知有内存泄漏的情况存在时我们可以改用Memory来更明确地确认问题和定位问题。 首先可以使用Allocation instrumentation on timeline来确认问题如下图所示. 每次点击按钮后动态内存分配情况图上都会出现一个蓝色的柱形而且在我们触发垃圾回收后蓝色柱形都没有变成灰色柱形也就是说之前分配的内存没有被清除。 因此我们可以明确地确认存在内存泄漏的问题。接下来我们需要精确定位问题可以使用Heap snapshot来进行定位如下图所示 首先我们需要点击快照记录初始的内存情况然后多次点击按钮后再次点击快照记录此时的内存情况。我们发现从原来的1.1M内存空间变成了1.4M内存空间。接着我们选中第二条快照记录可以看到右上角有一个All objects的字段表示展示当前选中的快照记录所有对象的分配情况。我们想要知道的是第二条快照与第一条快照的区别在哪因此选择Object allocated between Snapshot1 and Snapshot2即展示第一条快照和第二条快照存在差异的内存对象分配情况。这时我们可以看到Array的百分比很高初步可以判断是该变量存在问题。点击查看详情后就能查看到该变量对应的具体数据了。
这是一个判断闭包是否导致内存泄漏问题并简单定位的方法
2. 全局变量
全局变量通常不会被垃圾回收但并非所有变量都不能存在于全局范围。有时由于疏忽会导致某些变量流失到全局比如未声明变量却直接对其赋值这将导致该变量在全局范围创建。如下所示
function fn1() {// 此处变量name未被声明name new Array(99999999)
}
fn1()此时当出现这种情况时会自动在全局范围内创建一个变量name并将一个大型数组赋值给name。由于它是全局变量所以该内存空间将一直保持不释放。 要解决这个问题我们需要自己在平时多加注意不要在变量声明之前进行赋值。另外也可以考虑开启严格模式这样在不知不觉中犯错时会收到错误警告。例如
function fn1() {use strict;name new Array(99999999)
}
fn1()3. 分离的DOM节点
如果您手动删除了一个dom节点本应该释放该节点占用的内存但由于疏忽导致某处代码仍然引用了该被移除节点最终导致该节点占用的内存无法被释放这种情况是很常见的。
div idrootdiv classchild我是子元素/divbutton移除/button
/div
scriptlet btn document.querySelector(button)let child document.querySelector(.child)let root document.querySelector(#root)btn.addEventListener(click, function() {root.removeChild(child)})
/script代码的功能是在点击按钮后移除.child节点尽管节点在点击后确实从dom中移除了但全局变量child仍然保留对该节点的引用导致该节点的内存无法释放建议使用Memory的快照功能进行检测具体操作如下图所示。 先记录下初始状态的快照然后在点击移除按钮后再次点击一次快照。此时我们无法看出内存大小的任何变化因为被移除的节点占用的内存非常小可以忽略不计。但是我们可以点击第二条快照记录在筛选框中输入“detached”这样就会显示所有脱离但尚未被清除的节点对象。
解决办法如下图所示
div idrootdiv classchild我是子元素/divbutton移除/button
/div
scriptlet btn document.querySelector(button)btn.addEventListener(click, function() { let child document.querySelector(.child)let root document.querySelector(#root)root.removeChild(child)})
/script修改非常简单只需将对.child节点的引用移动到click事件的回调函数中。这样当移除节点并退出回调函数的执行上下文后对该节点的引用将自动清除从而避免了内存泄漏的情况。让我们来验证一下。如下图所示 结果很明显这样处理过后就不存在内存泄漏的情况了
4. 控制台的打印
button按钮/button
scriptdocument.querySelector(button).addEventListener(click, function() {let obj new Array(1000000)console.log(obj);})
/script我们在按钮的点击回调事件中创建了一个很大的数组对象并打印用performance来验证一下 开始录制时首先进行一次垃圾回收以清除初始内存。然后点击按钮三次即执行了三次点击事件。最后再次触发一次垃圾回收。观察录制结果发现JS Heap曲线呈阶梯状上升并且最终保持的高度比初始基准线高很多。这说明每次执行点击事件时创建的大型数组对象obj由于被浏览器保存并且无法回收导致内存占用增加。
接下来注释掉console.log再来看一下结果
button按钮/button
scriptdocument.querySelector(button).addEventListener(click, function() {let obj new Array(1000000)// console.log(obj);})
/script可以看到没有打印以后每次创建的obj都立马被销毁了并且最终触发垃圾回收机制后跟初始的基准线同样高说明已经不存在内存泄漏的现象了
其实同理 console.log也可以用Memory来进一步验证
未注释 console.log 注释掉了console.log 最后简单总结一下在开发环境下可以使用控制台打印便于调试但是在生产环境下尽可能得不要在控制台打印数据。所以我们经常会在代码中看到类似如下的操作
// 如果在开发环境下打印变量obj
if(isDev) {console.log(obj)
}这样就避免了生产环境下无用的变量打印占用一定的内存空间同样的除了console.log之外console.error、console.info、console.dir等等都不要在生产环境下使用
5. 遗忘的定时器
定时器也是平时很多人会忽略的一个问题比如定义了定时器后就再也不去考虑清除定时器了这样其实也会造成一定的内存泄漏。来看一个代码示例
button开启定时器/button
scriptfunction fn1() {let largeObj new Array(100000)setInterval(() {let myObj largeObj}, 1000)}document.querySelector(button).addEventListener(click, function() {fn1()})
/script这段代码是在点击按钮后执行fn1函数fn1函数内创建了一个很大的数组对象largeObj同时创建了一个setInterval定时器定时器的回调函数只是简单的引用了一下变量largeObj我们来看看其整体的内存分配情况吧 按道理来说点击按钮执行fn1函数后会退出该函数的执行上下文紧跟着函数体内的局部变量应该被清除但图中performance的录制结果显示似乎是存在内存泄漏问题的即最终曲线高度比基准线高度要高那么再用Memory来确认一次 在我们点击按钮后从动态内存分配的图上可以看到一个蓝色柱形表示浏览器为变量largeObj分配了一段内存。然而这段内存并没有被释放这说明存在内存泄漏的问题。其实问题的原因是setInterval的回调函数内对变量largeObj有一个引用关系而定时器一直未被清除所以变量largeObj的内存也自然不会被释放。 那么我们如何解决这个问题呢假设我们只需要让定时器执行三次我们可以对代码进行一些修改
button开启定时器/button
scriptfunction fn1() {let largeObj new Array(100000)let index 0let timer setInterval(() {if(index 3) clearInterval(timer);let myObj largeObjindex }, 1000)}document.querySelector(button).addEventListener(click, function() {fn1()})
/script现在我们再通过performance和memory来看看还不会存在内存泄漏的问题
performance 这次的录制结果表明最终曲线的高度与初始基准线的高度相同这意味着没有发生内存泄漏。
memory 这里需要澄清一下图中最初出现的蓝色柱形是因为我在录制后刷新了页面可以忽略接着我们点击了按钮看到又出现了一个蓝色柱形这时是为fn1函数中的变量largeObj分配了内存3s后该内存被释放变成了灰色柱形。因此可以得出结论这段代码没有内存泄漏问题。
简要总结一下在使用定时器时务必在不需要定时器时清除否则可能出现类似本例的情况。除了setTimeout和setInterval浏览器还提供了API如requestAnimationFrame也可能存在这种问题。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/92410.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!