内存机制及内存泄漏相关总结

内存空间

常用数据结构:

  1. 栈数据结构:后进先出(LIFO)
  2. 堆数据结构
  3. 队列:先进先出(FIFO),事件循环的基础结构

JS内存空间:

  1. 栈(stack):存放变量
  2. 堆(heap):存放复杂对象
  3. 池:一般归为栈,存放常量

注意闭包中的变量不存放在栈中,而是存放在堆中!!

变量的存放:

  1. 基本数据类型:保存在栈中
  2. 饮用数据类型:对象保存在堆内存中,因为JS不允许直接访问堆内存中的位置,因此在操作对象时实际是在操作对象的引用(即保存在栈中的一个地址)而不是实际的对象

QUESTION:为什么会有栈内存和堆内存的区别?

由于垃圾回收机制,为了使程序运行所占用的空间最小。

var a = {n: 1};
var b = a;
a.x = a = {n: 2};

a.x     // 这时 a.x 的值是多少
b.x     // 这时 b.x 的值是多少

上面这个问题的结果:a.x值为undefined,b.x的值为{n:2}

重点在 a.x = a = {n:2};这句!

赋值运算是从左到右解析:

a.x = a  [ = undefined  ]    得到两个引用,a.x表示在{n:1}这个对象中新增加了一个x

a = {n:2}

从右到左赋值:

a = {n:2}   这句的意思是给a重新赋值,将a的引用指向新的对象,即指向{n:2}

a.x = ( a = {n:2} )  此时将{n:2}赋值给x,此时,由于b指向的地址没有变,则b的当前对象为{ n:1, x: {n:2} }

so

a.x值为undefined,因为新的a里没有对象x

b.x值为{n:2}

内存回收

局部变量和全局变量的销毁:

  • 在函数执行完后,局部变量就没存在的必要了,垃圾回收器可以很容易的做出判断并回收
  • 全局变量什么时候自动释放内存空间很难判断,所以要尽量避免使用全局变量。如果必须使用全局变量存储大数据时,确保使用完之后将它设置为null或重新定义

垃圾回收算法的核心:如何判断内存已经不再使用了。

垃圾回收算法:引用计数(目前只有老IE在用,);标记清除【Mark-and-sweep】(现代浏览器,常用)

引用计数算法定义“内存不再使用”的标准是:看一个对象是否有指向它的引用,如果没有其他对象指向它,即引用数为0,就说明该对象已经不再需要了,可以释放该内存。

如果一个值不需要了,但是引用数却不为0,那么垃圾回收机制就没法释放这块内存,从而导致内存泄露。

let arr = [1, 2, 3, 4];
console.log(‘hello world‘);

上面这个代码中,[1,2,3,4]是一个值,会占用内存,变量arr是对这个值的引用,so引用数为1,尽管后面的代码没有用到arr,它还是会持续占用内存。

如果在最后一行增加一句:arr = null; 则解除了arr对[1,2,3,4]的引用,这块内存就可以被释放了。

// 创建一个对象person,他有两个指向属性age和name的引用
var person = {
    age: 12,
    name: ‘aaaa‘
};

person.name = null; // 虽然name设置为null,但因为person对象还有指向name的引用,因此name不会回收

var p = person;
person = 1;         //原来的person对象被赋值为1,但因为有新引用p指向原person对象,因此它不会被回收

p = null;           //原person对象已经没有引用,很快会被回收

上面的例子很好的解释了什么是“没有其他对象指向它

引用计数还有一个问题是:循环引用!如果两个对象相互引用,尽管它们不再使用了,但是还是不会回收,从而造成内存泄露。

标记清除算法将“不再使用的对象”定义为“无法到达的对象”。从根部(全局对象)开始,凡是能从根部到达的对象,都是还需要使用的!无法由根部出发触及到的对象(什么意思?!)(包含没有任何引用的对象)被标记为不再使用,稍后进行回收。

现代的垃圾回收器改良算法,本质为:可达内存被标记,其余的被当做垃圾回收

var div = document.createElement("div");
div.onclick = function() {
    console.log("click");
};

这个例子我没搞懂为啥在标记清除算法下就可以回收,在引用计数算法下不能回收?!

QUESTION:从内存来看null和undefined本质区别是什么??

WeakMap和WeakSet

ES6提出的新的数据结构,它们对于值的引用都是不计入垃圾回收机制的。(???啥意思???)

const wm = new WeakMap();
const element = document.getElementById(‘example‘);

wm.set(element, ‘some information‘);
wm.get(element) // "some information"

以上代码新建一个WeakMap实例,将一个DOM节点作为键名存入该实例,‘some information’作为键值一起存放在WeakMap里。

这时,WeakMap对element的引用就是弱引用,即DOM节点对象的引用计数是1,这时,一旦消除对该节点的引用,它占用的内存就会被释放,WeakMap保存的这个键值对也会自动消失。

常见的JavaScript内存泄露:

  1. 意外的全局变量:比如在函数内部变量忘记用var声明,实际上JS会把这个变量挂载到全局对象上,这样就意外创建了一个全局变量!

    • 解决办法:使用‘use strict‘严格模式
  2. 被遗忘的计时器或回调函数
    • 对于addEventListener这类观察者的例子,目前现代浏览器能很好的检测和处理循环引用,因此在处理回收节点内存的问题时,不需要非要调用removeEventListener 
  3. 脱离DOM的引用
  4. 闭包

原文地址:https://www.cnblogs.com/ningyn0712/p/11651989.html

时间: 2024-11-08 20:53:06

内存机制及内存泄漏相关总结的相关文章

C++内存机制中内存溢出、内存泄露、内存越界和栈溢出的区别和联系

当我们在用C++做底层驱动的时候,经常会遇到内存不足的警告,究其原因,往往是因为内存出现溢出,泄露或者越界等原因.那么他们之间有什么联系吗? 内存溢出(out of memory) 是指程序在申请内存时,没有足够的内存空间供其使用. 内存泄漏(memory leak) 是指程序在申请内存后,无法释放已申请的内存空间,占用有用内存. 注:内存泄漏最终会导致内存溢出 简单理解,内存溢出就是要求分配的内存超出了系统所给的.内存泄漏是指向系统申请分配内存进行使用(new),但是用完后不归还(delete

内存泄漏相关的

内存泄漏是个大问题 . 而且很难解决. ×××××××××××××××××××××××××××××××××× 解决方式目前大致分为两种:一是使用被人写的 专业软件,二是自己找. ×××××××××××××××××××××××××××××××××××××××××× 自己找有分为多种: 最容易上手的就是 搜索 new malloc ,看看有没有落单的, 稍微复杂的就是使用编译器提供的一些函数. ××××××××××××××××××××××××× Windows平台下面Visual Studio 调试

转自Android内存机制分析1——了解Android堆和栈

转自http://www.cnblogs.com/mythou/p/3202238.html 昨天用Gallery做了一个图片浏览选择开机画面的功能,当我加载的图片多了就出现OOM问题.以前也出现过这个问题,那时候并没有深究.这次打算好好分析一下Android的内存机制. 因为我以前是做VC++开发,因此对C++在Window下的内存机制还是比较了解.不过转到Android后,一直都没有刻意去处理内存问题,因为脑子里一直想着Java的GC机制.不过现在想想,自己对Android的GC和内存管理并

【Android】Android内存机制,了解Android堆和栈

1.dalvik的Heap和Stack 这里说的只是dalvik java部分的内存,实际上除了dalvik部分,还有native. 下面针对上面列出的数据类型进行说明,只有了解了我们申请的数据在哪里,才能更好掌控我们自己的程序. 2.对象实例数据 实际上是保存对象实例的属性,属性的类型和对象本身的类型标记等,但是不保存实例的方法.实例的方法是属于数据指令,是保存在Stack里面,也就是上面表格里面的类方法. 对象实例在Heap中分配好以后,会在stack中保存一个4字节的Heap内存地址,用来

内存分析与内存泄漏检测

Android 查看所有进程内存占用情况 (1)连接设备,打开USE调试模式 (2)打开命令行,执行命令:adb shell procrank 字段说明: PID:进程id VSS(Virtual Set Size): 虚拟耗用内存(包含共享库占用的内存) RSS(Resident Set Size):实际使用物理内存(包含共享库占用的内存) PSS(Proportional Set Size): 实际使用的物理内存(比例分配共享库占用的内存) USS(Unique Set Size): 进程独

JVM系列之六:内存溢出、内存泄漏 和 栈溢出

1. OOM && SOF OutOfMemoryError异常: 除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能, 内存泄露:指程序中动态分配内存给一些临时对象,但是对象不会被GC所回收,它始终占用内存.即被分配的对象可达但已无用. 内存溢出:指程序运行过程中无法申请到足够的内存而导致的一种错误.内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况. 从定义上可以看出内存泄露是内存溢出的一

2.1 自动内存管理机制--Java内存区域与内存溢出异常

自动内存管理机制 第二章.Java内存区域与内存溢出异常 [虚拟机中内存如何划分,以及哪部分区域.什么样代码和操作会导致内存溢出.各区域内存溢出的原因] 一.运行时数据区域 Java虚拟机所管理的内存包括以下几个运行时数据区域[虚拟机内存模型]: 1.程序计数器: 可以看作是当前线程所执行的字节码的行号指示器.在虚拟机中,字节码解释器工作时就是通过程序计数器的值来选择下一条需要执行的字节码指令.Java虚拟机中多线程是通过线程轮流切换并分配处理机执行时间的方式实现的,在任何一个确定的时刻,一个处

6.1 内存机制及使用优化

6.1.1  Android的内存机制 Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似.程序员通过new为对象分配内存,所有对象在java堆内分配空间:然而对象的释放是由垃圾回收器来完成的. 那么GC怎么能够确认某一个对象是不是已经被废弃了呢?Java采用了有向图的原理.Java将引用关系考虑为图的有向边,有向边从引用者指向引用对象.线程对象可以作为有向图的起始顶点,该图就是从起始顶点开始的一棵树,根顶点可以到达的对象都是有效对象,GC不会回收这些对

Andfroid 内存溢出与内存泄漏的简单分析与解决

<一>内存溢出与内存泄露 首先我们要知道内存溢出与内存泄露的概念,什么是内存溢出和内存泄露. 内存溢出:就想杯子里得水满了,就溢出了.内存溢出就是分配的内存被用光了,不够用了. 内存泄露:就如同杯子里面有石子,导致杯子里面的一部分空间没有被利用,在APP中内存泄露就是指该被回收的内存没有被回收,导致一部分内存一直被占着,可利用内存变少了.当泄露过多 时,可利用的内存越来越少,就会引起内存溢出了. <二> 查找内存泄露与内存溢出 (1) 内存溢出,最明显的地方就是报错,APP奔溃并报