目次
三、哪些情况会引起内存泄漏?
1. 意外的全局变量
2. 被遗忘的计时器或回调函数
3. 闭包
4. 没有清理的DOM元素引用
四、内存泄漏的识别方法
五、垃圾回收的使用场景优化
1. 数组array优化
2. 对象尽量复用
3. 在循环中的函数表达式,能复用最好放到循环外面。
参考资料
ホームページ ウェブフロントエンド jsチュートリアル JSのガベージコレクションとメモリリークについて詳しく解説

JSのガベージコレクションとメモリリークについて詳しく解説

Nov 13, 2020 pm 05:56 PM
javascript メモリーリーク ガベージコレクション

JSのガベージコレクションとメモリリークについて詳しく解説

#プログラムを実行するにはメモリが必要です。プログラムがメモリを要求するたびに、オペレーティング システムまたはランタイムはメモリを提供する必要があります。いわゆるメモリ リークとは、単に使用されなくなり、期限内に解放されなかったメモリのことです。メモリ リークをより適切に回避するために、まず Javascript ガベージ コレクション メカニズムを導入します。

C や C などの言語では、開発者はアプリケーションとメモリのリサイクルを直接制御できます。ただし、Java、C#、および JavaScript 言語では、変数のメモリ空間の適用と解放はプログラム自体によって処理されるため、開発者は気にする必要はありません。つまり、JavaScript には自動ガベージコレクション機構 (Garbage Collection) が備わっています。

1. ガベージ コレクションの必要性

以下の文章は『JavaScript 入門 (第 4 版)』から引用です

文字列、オブジェクト、配列は、固定サイズなので、サイズがわかっている場合は、それらに対して動的ストレージ割り当てを実行できます。 JavaScript プログラムが文字列、配列、またはオブジェクトを作成するたびに、インタープリターはそのエンティティを格納するためにメモリを割り当てる必要があります。このようにメモリが動的に割り当てられる場合は、再利用できるように最終的にメモリを解放する必要があります。そうしないと、JavaScript インタプリタがシステム内の利用可能なメモリをすべて消費し、システムがクラッシュする原因になります。

JSのガベージコレクションとメモリリークについて詳しく解説

この文章では、システムにガベージ コレクションが必要な理由を説明します。JavaScript は C/C とは異なり、独自のガベージ コレクション メカニズムを備えています。

JavaScript のガベージ コレクションのメカニズムは、使用されなくなった変数を見つけて、その変数が占有しているメモリを解放するという非常に単純な仕組みですが、このプロセスはオーバーヘッドが比較的大きいため、それほど時間はかかりません。ガベージ コレクターは、一定の間隔で定期的に実行されます。

var a = "浪里行舟";
var b = "前端工匠";
var a = b; //重写a
ログイン後にコピー

このコードを実行すると、文字列 "Wanglizhou" はその参照を失います (以前は a によって参照されていました)。システムはこの事実を検出した後、文字列の記憶領域を解放します。再利用できます。

2. ガベージ コレクション メカニズム

ガベージ コレクション メカニズムは、どのメモリが不要になったかをどのようにして知るのでしょうか?

ガベージ コレクションには

マークのクリアと参照カウント の 2 つの方法があります。参照カウントはあまり一般的には使用されず、マークアンドスイープがより一般的に使用されます。

1. マークのクリア

これは、JavaScript で最も一般的に使用されるガベージ コレクション メソッドです。変数が実行環境に入るとき、その変数を「環境に入る」としてマークします。論理的に言えば、環境に入る変数によって占有されているメモリは、実行フローが対応する環境に入る限り使用される可能性があるため、解放されることはありません。変数が環境から離れると、「環境から離れる」とマークされます。

ガベージ コレクターは、実行時にメモリに格納されているすべての変数にマークを付けます。次に、環境内の変数と、環境内の変数によって参照されるタグを削除します。これ以降にマークされた変数は、環境内の変数がこれらの変数にアクセスできなくなるため、削除される変数とみなされます。やっと。ガベージ コレクターはメモリ クリーニング作業を完了し、マークされた値を破棄し、それらが占有しているメモリ領域を再利用します。

JSのガベージコレクションとメモリリークについて詳しく解説

例を使用してこの方法を説明しましょう:

var m = 0,n = 19 // 把 m,n,add() 标记为进入环境。
add(m, n) // 把 a, b, c标记为进入环境。
console.log(n) // a,b,c标记为离开环境,等待垃圾回收。
function add(a, b) {
  a++
  var c = a + b
  return c
}
ログイン後にコピー

2. 参照カウント

いわゆる「参照カウント」とは、エンジンには、すべてのリソース (通常はさまざまな値) への参照の数をメモリに保存する「参照テーブル」があります。値の参照数が 0 の場合は、その値が使用されなくなったことを意味するため、メモリを解放できます。

JSのガベージコレクションとメモリリークについて詳しく解説

上の図では、左下隅の 2 つの値には参照がないため、解放できます。

値が不要になったが参照番号が 0 でない場合、ガベージ コレクション メカニズムはこのメモリを解放できず、メモリ リークが発生します。

var arr = [1, 2, 3, 4];
arr = [2, 4, 5]
console.log('浪里行舟');
ログイン後にコピー

上記のコードでは、配列 [1, 2, 3, 4] は値であり、メモリを占有します。変数 arr はこの値への唯一の参照であるため、参照の数は 1 です。次のコードでは arr は使用されていませんが、引き続きメモリを占有します。メモリを解放する方法については、以下で紹介します。

コードの 3 行目では、配列 [1, 2, 3, 4] によって参照される変数 arr が別の値を取得し、配列 [1, 2, 3, 4] への参照の数が減ります。 1 ずつ増加すると、この時点でその参照カウントは 0 になります。これは、この値にアクセスする方法がなくなったことを意味するため、この値が占有しているメモリ空間を回復できます。

しかし、参照カウントの最大の問題は、循環参照です。

function func() {
    let obj1 = {};
    let obj2 = {};

    obj1.a = obj2; // obj1 引用 obj2
    obj2.a = obj1; // obj2 引用 obj1
}
ログイン後にコピー

関数 func が実行されると、戻り値は未定義であるため、関数全体と内部変数はリサイクルされる必要がありますが、参照カウント方式では、obj1 と obj2 の参照数は 0 ではないため、再利用されません。

循環参照の問題を解決するには、使用しない場合は手動で空に設定するのが最善です。上記の例は次のように実行できます:

obj1 = null;
obj2 = null;
ログイン後にコピー

三、哪些情况会引起内存泄漏?

虽然JavaScript会自动垃圾收集,但是如果我们的代码写法不当,会让变量一直处于“进入环境”的状态,无法被回收。下面列一下内存泄漏常见的几种情况:

1. 意外的全局变量

function foo(arg) {
    bar = "this is a hidden global variable";
}
ログイン後にコピー

bar没被声明,会变成一个全局变量,在页面关闭之前不会被释放。

另一种意外的全局变量可能由 this 创建:

function foo() {
    this.variable = "potential accidental global";
}
// foo 调用自己,this 指向了全局对象(window)
foo();
ログイン後にコピー

在 JavaScript 文件头部加上 'use strict',可以避免此类错误发生。启用严格模式解析 JavaScript ,避免意外的全局变量。

2. 被遗忘的计时器或回调函数

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        // 处理 node 和 someResource
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);
ログイン後にコピー

这样的代码很常见,如果id为Node的元素从DOM中移除,该定时器仍会存在,同时,因为回调函数中包含对someResource的引用,定时器外面的someResource也不会被释放。

3. 闭包

function bindEvent(){
  var obj=document.createElement('xxx')
  obj.onclick=function(){
    // Even if it is a empty function
  }
}
ログイン後にコピー

闭包可以维持函数内局部变量,使其得不到释放。上例定义事件回调时,由于是函数内定义函数,并且内部函数--事件回调引用外部函数,形成了闭包。

// 将事件处理函数定义在外面
function bindEvent() {
  var obj = document.createElement('xxx')
  obj.onclick = onclickHandler
}
// 或者在定义事件处理函数的外部函数中,删除对dom的引用
function bindEvent() {
  var obj = document.createElement('xxx')
  obj.onclick = function() {
    // Even if it is a empty function
  }
  obj = null
}
ログイン後にコピー

解决之道,将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用。

4. 没有清理的DOM元素引用

有时,保存 DOM 节点内部数据结构很有用。假如你想快速更新表格的几行内容,把每一行 DOM 存成字典(JSON 键值对)或者数组很有意义。此时,同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中。将来你决定删除这些行时,需要把两个引用都清除。

var elements = {
    button: document.getElementById('button'),
    image: document.getElementById('image'),
    text: document.getElementById('text')
};
function doStuff() {
    image.src = 'http://some.url/image';
    button.click();
    console.log(text.innerHTML);
}
function removeButton() {
    document.body.removeChild(document.getElementById('button'));
    // 此时,仍旧存在一个全局的 #button 的引用
    // elements 字典。button 元素仍旧在内存中,不能被 GC 回收。
}
ログイン後にコピー

虽然我们用removeChild移除了button,但是还在elements对象里保存着#button的引用,换言之,DOM元素还在内存里面。

四、内存泄漏的识别方法

新版本的chrome在 performance 中查看:

JSのガベージコレクションとメモリリークについて詳しく解説

步骤:

  • 打开开发者工具 Performance
  • 勾选 Screenshots 和 memory
  • 左上角小圆点开始录制(record)
  • 停止录制

图中 Heap 对应的部分就可以看到内存在周期性的回落也可以看到垃圾回收的周期,如果垃圾回收之后的最低值(我们称为min),min在不断上涨,那么肯定是有较为严重的内存泄漏问题。

避免内存泄漏的一些方式:

  • 减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收
  • 注意程序逻辑,避免“死循环”之类的
  • 避免创建过多的对象

总而言之需要遵循一条原则:不用了的东西要及时归还

五、垃圾回收的使用场景优化

1. 数组array优化

将[]赋值给一个数组对象,是清空数组的捷径(例如: arr = [];),但是需要注意的是,这种方式又创建了一个新的空对象,并且将原来的数组对象变成了一小片内存垃圾!实际上,将数组长度赋值为0(arr.length = 0)也能达到清空数组的目的,并且同时能实现数组重用,减少内存垃圾的产生。

const arr = [1, 2, 3, 4];
console.log('浪里行舟');
arr.length = 0  // 可以直接让数字清空,而且数组类型不变。
// arr = []; 虽然让a变量成一个空数组,但是在堆上重新申请了一个空数组对象。
ログイン後にコピー

2. 对象尽量复用

对象尽量复用,尤其是在循环等地方出现创建新对象,能复用就复用。不用的对象,尽可能设置为null,尽快被垃圾回收掉。

var t = {} // 每次循环都会创建一个新对象。
for (var i = 0; i < 10; i++) {
  // var t = {};// 每次循环都会创建一个新对象。
  t.age = 19
  t.name = &#39;123&#39;
  t.index = i
  console.log(t)
}
t = null //对象如果已经不用了,那就立即设置为null;等待垃圾回收。
ログイン後にコピー

3. 在循环中的函数表达式,能复用最好放到循环外面。

// 在循环中最好也别使用函数表达式。
for (var k = 0; k < 10; k++) {
  var t = function(a) {
    // 创建了10次  函数对象。
    console.log(a)
  }
  t(k)
}
ログイン後にコピー
// 推荐用法
function t(a) {
  console.log(a)
}
for (var k = 0; k < 10; k++) {
  t(k)
}
t = null
ログイン後にコピー

参考资料

作者:浪里行舟

更多编程相关知识,请访问:编程学习网站!!

以上がJSのガベージコレクションとメモリリークについて詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Go メモリ リーク追跡: Go pprof 実践ガイド Go メモリ リーク追跡: Go pprof 実践ガイド Apr 08, 2024 am 10:57 AM

pprof ツールを使用すると、Go アプリケーションのメモリ使用量を分析し、メモリ リークを検出できます。メモリ プロファイルの生成、メモリ リークの特定、およびリアルタイム分析機能を提供します。 pprof.Parse を使用してメモリ スナップショットを生成し、pprof-allocspace コマンドを使用してメモリ割り当てが最も多いデータ構造を特定します。同時に、pprof はリアルタイム分析をサポートし、メモリ使用量情報にリモートでアクセスするためのエンドポイントを提供します。

Java 関数のメモリ管理技術とセキュリティの間にはどのような関係がありますか? Java 関数のメモリ管理技術とセキュリティの間にはどのような関係がありますか? May 02, 2024 pm 01:06 PM

Java のメモリ管理には、ガベージ コレクションと参照カウントを使用してメモリの割り当て、使用、再利用を行う自動メモリ管理が含まれます。効果的なメモリ管理は、バッファ オーバーフロー、ワイルド ポインタ、メモリ リークを防ぎ、プログラムの安全性を向上させるため、セキュリティにとって非常に重要です。たとえば、不要になったオブジェクトを適切に解放することでメモリ リークを回避でき、それによってプログラムのパフォーマンスが向上し、クラッシュを防ぐことができます。

クロージャによって引き起こされるメモリリークの問題を解決する クロージャによって引き起こされるメモリリークの問題を解決する Feb 18, 2024 pm 03:20 PM

タイトル: クロージャによって引き起こされるメモリ リークと解決策 はじめに: クロージャは JavaScript における非常に一般的な概念であり、内部関数が外部関数の変数にアクセスできるようにします。ただし、クロージャを誤って使用すると、メモリ リークが発生する可能性があります。この記事では、クロージャによって引き起こされるメモリ リークの問題を調査し、解決策と具体的なコード例を提供します。 1. クロージャによるメモリリーク クロージャの特徴は、内部関数が外部関数の変数にアクセスできることです。つまり、クロージャ内で参照される変数はガベージコレクションされません。不適切に使用すると、

Golang の技術的なパフォーマンスの最適化でメモリ リークを回避するにはどうすればよいですか? Golang の技術的なパフォーマンスの最適化でメモリ リークを回避するにはどうすればよいですか? Jun 04, 2024 pm 12:27 PM

メモリ リークは、ファイル、ネットワーク接続、データベース接続などの使用されなくなったリソースを閉じることによって、Go プログラムのメモリを継続的に増加させる可能性があります。弱参照を使用してメモリ リークを防ぎ、強参照されなくなったオブジェクトをガベージ コレクションの対象にします。 go coroutine を使用すると、メモリ リークを避けるために、終了時にコルーチンのスタック メモリが自動的に解放されます。

Valgrind を使用してメモリ リークを検出するにはどうすればよいですか? Valgrind を使用してメモリ リークを検出するにはどうすればよいですか? Jun 05, 2024 am 11:53 AM

Valgrind は、メモリの割り当てと割り当て解除をシミュレートすることでメモリ リークとエラーを検出します。使用するには、次の手順に従います。 Valgrind をインストールします。公式 Web サイトからオペレーティング システムのバージョンをダウンロードしてインストールします。プログラムをコンパイルする: Valgrind フラグ (gcc-g-omyprogrammyprogram.c-lstdc++ など) を使用してプログラムをコンパイルします。プログラムを分析する: valgrind--leak-check=fullmyprogram コマンドを使用して、コンパイルされたプログラムを分析します。出力を確認します。Valgrind はプログラムの実行後にレポートを生成し、メモリ リークとエラー メッセージを表示します。

C++ でのメモリ リークのデバッグ手法 C++ でのメモリ リークのデバッグ手法 Jun 05, 2024 pm 10:19 PM

C++ におけるメモリ リークとは、プログラムがメモリを割り当てたにもかかわらず解放し忘れ、メモリが再利用されなくなることを意味します。デバッグ手法には、デバッガー (Valgrind、GDB など) の使用、アサーションの挿入、メモリ リーク検出ライブラリ (Boost.LeakDetector、MemorySanitizer など) の使用が含まれます。実際のケースを通じてメモリ リークを検出するための Valgrind の使用法を示し、割り当てられたメモリを常に解放する、スマート ポインターを使用する、メモリ管理ライブラリを使用する、定期的なメモリ チェックを実行するなど、メモリ リークを回避するためのベスト プラクティスを提案します。

クロージャでのメモリリークを効果的に回避するにはどうすればよいでしょうか? クロージャでのメモリリークを効果的に回避するにはどうすればよいでしょうか? Jan 13, 2024 pm 12:46 PM

クロージャでのメモリリークを防ぐにはどうすればよいでしょうか?クロージャは JavaScript の最も強力な機能の 1 つであり、関数のネストとデータのカプセル化を可能にします。ただし、クロージャでは、特に非同期やタイマーを扱う場合にメモリ リークが発生する傾向があります。この記事では、クロージャでのメモリ リークを防ぐ方法を説明し、具体的なコード例を示します。メモリ リークは通常、オブジェクトが不要になったにもかかわらず、そのオブジェクトが占有しているメモリを何らかの理由で解放できない場合に発生します。クロージャ内で、関数が外部変数を参照する場合、これらの変数は

Python CPython パフォーマンス最適化のヒント Python CPython パフォーマンス最適化のヒント Mar 06, 2024 pm 06:04 PM

Pythonはさまざまな分野で広く使われており、その使いやすさと強力な機能が高く評価されています。ただし、場合によっては、そのパフォーマンスがボトルネックになる可能性があります。 CPython 仮想マシンを深く理解し、いくつかの賢い最適化テクニックを使用することで、Python プログラムの実行効率を大幅に向上させることができます。 1. CPython 仮想マシンを理解する CPython は Python の最も一般的な実装であり、仮想マシン (VM) を使用して Python コードを実行します。 VM はバイトコードを機械命令に解釈するため、一定量の時間オーバーヘッドが発生します。 VM がどのように動作するかを理解することは、パフォーマンスのボトルネックを特定して最適化するのに役立ちます。 2. ガベージ コレクション Python はガベージ コレクションに参照カウント メカニズムを使用しますが、ガベージ コレクションが定期的に停止する可能性があります。

See all articles