1. 程式人生 > >常見的 JavaScript 內存泄露

常見的 JavaScript 內存泄露

圖片 渲染引擎 cti 節點 時間 click png com 相對

內存泄漏:由於疏忽或錯誤造成程序未能釋放已經不再使用的內存。內存泄漏並非指內存在物理上的消失,而是應用程序分配某段內存後,由於設計錯誤,導致在釋放該段內存之前就失去了對該段內存的控制,從而造成了內存的浪費。

1、意外的全局變量

js對未聲明變量會在全局最高對象上創建它的引用,(是以屬性存在的,而不是變量),如果在遊覽器上就是window對象,如果在node環境下就是global;如果未聲明的變量緩存大量的數據,它可能只有在頁面被刷新或者被關閉的時候才會釋放內存,這樣就造成了內存意外泄漏。如下例子:

function my() {
    name="my name is tokey"
}
my()
console.log(window)

在控制臺可以看到

技術分享圖片

還有通過this創建意外的全局變量

function my() {
    this.name="my name is tokey"
}
my()
console.log(window.name)  //my name is tokey 此時的this指向的並不是undefined而是全局對象window

針對上面類型的內存泄漏我們在平時一定要聲明變量,不要有全局直接引用。(在JavaScript文件中添加 ‘use strict‘,開啟嚴格模式,可以有效地避免上述問題。)

2、console.log

作為前端平時使用console.log在控制臺打出相對應的信息可以說是非常常見。但如果沒有去掉console.log可能會存在內存泄漏。因為在代碼運行之後需要在開發工具能查看對象信息,所以傳遞給console.log的對象是不能被垃圾回收。

3、閉包

首先閉包是一個函數A返回一個內聯的函數B,及時A函數執行完函數B也可以訪問函數A裏面的變量,這就是一個簡單的閉包。本質上閉包是將函數內部和外部連接起來的一座橋梁。

function my(name) {
    function sendName() {
        console.log(name)
    }
    return sendName
}
var test=my("tokey")
test()  //tokey

在my()內部創建的sendName()函數是不會被回收的,因為它被全局變量test引用,處於隨時被調用的狀態。如果向釋放內存可以設置test=null;

由於閉包會攜帶包含它的函數的作用域,因此會比其他函數占用更多的內存。過度使用閉包可能會導致內存占用過多。

4、DOM泄漏

遊覽器中DOM和js采用的是不一樣的引擎,DOM采用的是渲染引擎,而js采用的是v8引擎,所以在用js操作DOM時會比較耗費性能,因為他們需要橋來鏈接他們。為了減少DOM的操作,我們一般將常用的DOM。我們會采用變量引用的方式會將其緩存在當前環境。如果在進行一些刪除、更新操作之後,可能會忘記釋放已經緩存的DOM,話不多說直接來個例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="maximum-scale=1.0,minimum-scale=1.0,user-scalable=0,initial-scale=1.0, width=device-width">
    <title>Title</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }

    </style>
</head>
<body>
<div class="main">
    <div class="test">天</div>
    <div class="item">天</div>
    <div class="item">向</div>
</div>
<button id="add">點擊我增加</button>
<button id="remove">點擊我減少</button>
</body>
<script src="js/Zepto.js"></script>
<script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
var add=document.querySelector("#add");
var remove=document.querySelector("#remove");
var main=document.querySelector(".main")
var test=document.querySelector(".test")
add.onclick=function () {
    var itemN=document.createElement(div)
    var txt=document.createTextNode()
    itemN.appendChild(txt)
    main.appendChild(itemN)
}
remove.onclick=function () {
    main.removeChild(test)
}
</script>
</html>

通過chrome遊覽器的內存調試工作我們可以看到

技術分享圖片

在我點擊了三次增加之後,可以明顯的看到節點(綠線)有三次明顯的增加,之後我又刪除了一個節點,但綠線沒有下降,這是為什麽呢?,這也就是內存泄漏。原因就是刪除的DOM在js中有全局的引用。也就是我刪除的test在文中被引用,所以無法釋放內存。所以在刪除更新等操作後應該將其設置為null。

5、被遺忘的timers

js中常用的定時器setInterval()、setTimeout().他們都是規定延遲一定的時間執行某個代碼,而其中setInterval()和鏈式setTimeout()在使用完之後如果沒有手動關閉,會一直存在執行占用內存,所以在不用的時候我們可以通過clearInterval()、clearTimeout() 來關閉其對應的定時器,釋放內存。熟悉朋友都知道這類定時器是有誤差的,所以遊覽器給出了專門的API-requestAnimationFrame();大家可以試一下。

常見的 JavaScript 內存泄露