CPU(Central Processing Unit)工做的时候:
一、须要从存储器里取数据出来。
二、进行运算,要不停地用存储器读写。
三、计算出结果再返回到存储器里。
举例子形容关系
咱们的PC的APP,手机的APP都是跑在内存上的。
程序的运行须要内存。只要程序提出要求,操做系统就必须供给内存。html
内存就是处于外存和CPU之间的桥梁,用于存储CPU的运算数据,这样内存就能够保持记忆功能,你写的全部代码,都是须要在内存上跑的,虚拟内存是从外存上分配的,很慢
内存的频率(mhz)越高表明内存运算更快,同一块内存我跑的更快哟,这就是为何DDR5比DDR3快的缘由
说这个的缘由,就是若是个人计算机性能足够好的话,内存泄漏带来的问题就会愈来愈小。android
out of memory
ios
内存溢出是指程序在申请内存时,没有足够的内存空间供其使用,就会出现内存溢出。
在手机上,好比任何一个app,系统初始的时候可能只会给你分配100m的内存,若是有android studio的话,能够在log上看到,这个时候你点击了某个图片列表页(为何用图片举例,是由于图片特有的状况,图片自己若是是20Kb,长宽为300的话,渲染到手机上因为图片采用的ARGB-888色彩格式,每一个像素点占用4个字节(双通道),这样图片实际占用内存就是3003004/1024/1024 = 300+k dpi为1的状况),这个时候内存就会暴涨,一旦接近临界值,程序就会去找操做系统说,我内存不够了,再给我点,系统就会又给你分配一段,完了你返回首页了,可是由于你的代码写的有问题,暴露各类全局对象啊,各类监听啊,一进一出屡次,可是系统给每一个app分配的内存是有上限的,直到内存不够分,泄漏致使的内存溢出。而后crash掉。之前我写rn的时候,早期的scrollview性能堪忧,出现过内存溢出的现象。程序员
memory leak
web
内存泄漏指的是你申请了一块内存,在使用后没法释放已申请的内存空间,好比程序会认为你可能会用到这个变量,就一直给你留着不释放,一次内存泄漏能够被忽略,可是内存泄露堆积后果很严重,不管多少内存,早晚会被占光。
既然内存我能够申请,就能够被系统回收,在C语言中,须要程序员手动malloc
去申请内存,而后free
掉它,这写起来很麻烦,因此其余大多数语言都提供了自动回收的机制,那么既然自动回收了,就很容易出现各类问题。算法
一般来讲问题并非特别大,由于正常一个进程的生命周期有限,在当下的大内存快cpu的手机下,影响有限,不过仍是要列举一些状况。
1:安卓手机内存管理很差,致使只要不重启,时间越长,可用内存越少,即便杀程序。具体原因可能还和安卓开放过多权限致使无良app各类保持后台后门运行也有必定关系。
2:致使内存溢出,若是手机内存被挤占的有限,那么手机会变卡,严重的本身crash掉,若是是pc端,浏览器的内存泄漏致使的溢出会让浏览器出现假死状态,只能经过强制关闭解决,若是是在webview上,好比我开始的时候写过一个代码在ios微信浏览器上调用swiper 的3d变换致使微信直接闪退。
3:以上仍是客户端的,客户端大多数状况下不会停留时间过长,因此除非是很是规操做,不多会出大问题,可是,跑在服务端的程序,一般都是一直跑几天甚至是几个月的,若是这个里面有内存泄漏引起的内存溢出的话,那么就会致使服务器宕机,必须重启。那带来的损失就很大了。chrome
JavaScript 对未声明变量的处理方式:在全局对象上建立该变量的引用(即全局对象上的属性,不是变量,由于它能经过delete删除)。若是在浏览器中,全局对象就是window对象。
若是未声明的变量缓存大量的数据,会致使这些数据只有在窗口关闭或从新刷新页面时才能被释放。这样会形成意外的内存泄漏。浏览器
那么为何会对未声明的变量处理方式是挂window下呢?
“当引擎执行LHS查询时,若是在顶层(全局做用域)中也没法找到目标变量,全局做用域中就会建立一个具备该名称的变量,并将其返还给引擎,前提是程序运行在非“严格模式”下”缓存
摘录来自: Kyle Simpson、赵望野、梁杰. “你不知道的JavaScript(上卷)。” iBooks.
function foo(arg) { bar = 'this is hidden global variable'; }
等同于:安全
function foo(arg) { window.bar = 'this is hidden global variable'; }
另外,经过this建立意外的全局变量:
function foo() { this.variable = 'this is hidden global variable'; } // 当在全局做用域中调用foo函数,此时this指向的是全局对象(window),而不是'undefined' foo();
------------->演示
正常的定义全局变量没有问题,可是这种是属于意外的泄漏,因此可使用严格模式处理,规范本身的代码。
传递给console.log的对象是不能被垃圾回收 ♻️,由于在代码运行以后须要在开发工具能查看对象信息。因此最好不要在生产环境中console.log任何对象。
追踪线上问题,console绝非是个好的方式。由于发生问题通常在用户哪里,你没办法看用户的日志。
function aaa() { this.name = (Array(100000)).join('*'); console.log(this); } document.getElementsByClassName('console-obj')[0].addEventListener('click', function () { var oo = new aaa(); });
------------->演示
能够删除本身的console.log,可是显然,在开发环境下,我就是想看个人console.log,这样注释来注释去也挺麻烦的,因此能够判断下当前的环境是否是env
,若是是product
环境下的话,直接
window.console.log = function(){return 'warn:do not use my log'}
这样的手法不只能够屏蔽console.log
,还能防止别人在咱们的页面下console.log
调试
延伸:如何保护本身的页面安全
因为闭包的特性,经过闭包而能被访问到的变量,显然不会被内存回收♻️,由于被回收的话就没闭包了这个概念了。
function foo() { var str = Array(10000).join('#'); var msg = "test message"; function unused() { var message = 'it is only a test message'; str = 'unused: ' + str; } function getData() { return msg; } return getData; } var bar; document.getElementsByClassName('closure-obj')[0].addEventListener('click', function () { bar = foo(); }); // var list = []; // document.getElementsByClassName('closure-obj')[0].addEventListener('click', function () { // list.push(foo()); // });
闭包形成的内存泄漏占用会比其余的要多。
缘由是在相同做用域内建立的多个内部函数对象是共享同一个变量对象(variable object)。若是建立的内部函数没有被其余对象引用,无论内部函数是否引用外部函数的变量和函数,在外部函数执行完,对应变量对象便会被销毁。反之,若是内部函数中存在有对外部函数变量或函数的访问(能够不是被引用的内部函数),而且存在某个或多个内部函数被其余对象引用,那么就会造成闭包,外部函数的变量对象就会存在于闭包函数的做用域链中。这样确保了闭包函数有权访问外部函数的全部变量和函数。
延伸:VO/AO,call stack
不暴露到全局变量上,这样就不会有问题,暴露到全局变量上就手动置为null,垃圾回收器下次回来会带走它
在 JavaScript 中,DOM 操做是很是耗时的。由于 JavaScript/ECMAScript 引擎独立于渲染引擎,而 DOM 是位于渲染引擎,相互访问须要消耗必定的资源。如 Chrome 浏览器中 DOM 位于 WebCore,而 JavaScript/ECMAScript 位于 V8 中。假如将 JavaScript/ECMAScript、DOM 分别想象成两座孤岛,两岛之间经过一座收费桥链接,过桥须要交纳必定“过桥费”。JavaScript/ECMAScript 每次访问 DOM 时,都须要交纳“过桥费”。所以访问 DOM 次数越多,费用越高,页面性能就会受到很大影响。
为了减小 DOM 访问次数,通常状况下,当须要屡次访问同一个 DOM 方法或属性时,会将 DOM 引用缓存到一个局部变量中。
但若是在执行某些删除、更新操做后,可能会忘记释放掉代码中对应的 DOM 引用,这样会形成 DOM 内存泄露。
<input type="button" value="remove" class="remove" style="display:none;"> <input type="button" value="add" class="add"> <div class="container"> <ul class="wrapper"></ul> </div> // 由于要屡次用到pre.wrapper、div.container、input.remove、input.add节点,将其缓存到本地变量中, var wrapper = document.querySelector('.wrapper'); var container = document.querySelector('.container'); var removeBtn = document.querySelector('.remove'); var addBtn = document.querySelector('.add'); var counter = 0; var once = true; // 方法 var hide = function(target){ target.style.display = 'none'; } var show = function(target){ target.style.display = 'inline-block'; } // 回调函数 var removeCallback = function(){ removeBtn.removeEventListener('click', removeCallback, false); addBtn.removeEventListener('click', addCallback, false); hide(addBtn); hide(removeBtn); container.removeChild(wrapper); wrapper = null; } var addCallback = function(){ let p = document.createElement('li'); p.appendChild(document.createTextNode("+ ++counter + ':a new line text\n")); wrapper.appendChild(p); // 显示删除操做按钮 if(once){ show(removeBtn); once = false; } } // 绑定事件 removeBtn.addEventListener('click', removeCallback, false); addBtn.addEventListener('click', addCallback, false);
--------->演示代码
var refA = document.getElementById('refA'); var refB = document.getElementById('refB'); document.body.removeChild(refA); // #refA不能GC回收,由于存在变量refA对它的引用。将其对#refA引用释放,但仍是没法回收#refA。 refA = null; // 还存在变量refB对#refA的间接引用(refB引用了#refB,而#refB属于#refA)。将变量refB对#refB的引用释放,#refA就能够被GC回收。 refB = null;
var counter = 0; var clock = { start: function () { // setInterval(this.step, 1000); if(!this.timer){ this.timer = setInterval(this.step, 1000); } }, step: function () { var date = new Date(); var h = date.getHours(); var m = date.getMinutes(); var s = date.getSeconds(); console.log('step running'); } } // function goo(){ // // clock = null; // clearInterval(clock.timer); // console.log('click stop'); // } document.querySelector('.start').addEventListener('click', function () { clock.start(); // document.querySelector('.stop').addEventListener('click',); }); document.querySelector('.stop').addEventListener('click', function () { // clock = null; clearInterval(clock.timer); });
监听器没有及时回收或者是匿名回收致使的。
bind,call,apply的区别
以上就是咱们使用的时候的步骤
那么对这个performances
里的各项是如何理解的呢?
前置问题1:什么是回流,什么是重绘,以及为何回流必定会致使重绘,可是重绘不会致使回流?
中置问题2:浏览器到了渲染阶段的过程是什么?
一次性能的记录就完整的展现的浏览器的渲染全过程。从图中也能够看出,layout后的阶段是Painting
跑一个performances
重点看看这个event-log
,以回流为例子,再次确认回流后跟着painting,看看有哪些回流,而后去看看时间节点,发现对应的页面出现。
回流操做仍是挺占用时间的
以拼团列表图片高度加载致使的回流问题,能够用一个object-fit
来搞定常见的状况
注意代码规范,注意代码规范,注意代码规范
讲讲垃圾回收,说白了,内存泄漏,溢出,就是由于js有自动垃圾回收的机制,而后自动的垃圾回收器并不能准确的回收你所不想用的东西,就会出一些问题,那么常见的垃圾回收有两种
当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。 若是同一个值又被赋给另外一个变量,则该值的引用次数加 1。相反,若是包含对这个值引用的变量又取 得了另一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这 个值了,于是就能够将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那 些引用次数为零的值所占用的内存。
//赋值给o1的对象a{},赋值给o2的对象b{}; var o1 = { o2: { x: 1 } }; //a+1 = 1,b做为属性也+1 = 1; var o3 = o1; //a+1+1 = 2,b+1+1 = 2 o1 = 1; //a+1+1-1 = 1,b+1+1-1 = 1; var o4 = o3.o2; //a+1+1-1 = 1,b+1+1-1+1 = 2; o3 = '374'; //a+1+1-1-1 = 0,b+1+1-1+1-1 = 1; o4 = null; //b-1 = 0;
//o1:x{},o2:y{}; function f() { var o1 = {}; //x+1 = 1; var o2 = {}; //y+1 = 1; o1.p = o2; // o1 references o2 //y+1+1 = 2; o2.p = o1; // o2 references o1. This creates a cycle. //x+1+1 = 2; } f();
这段代码o1和o2互相引用致使引用次数回收的时候不为1,就没有办法回收。
假设没有o2.p= o1
这段,那么o1在出函数的时候要给对应的对象减一,结果发现,o1有一个属性p还没解除引用,因此先去解o1.p的,这个时候o2的对象就减一次,完了后o1.p就没了,那o1就能够解除o1的对象,o2再-它本身的,都为0,没泄漏
反过来,若是上了那段代码的话,o1要解除,先走p,o1.p想解除,结果发现o2有个p,又去解o2.p,死循环,一个都解不了,仍是2.
假如这个函数被重复屡次调用,就会致使大量内存得 不到回收。为此,Netscape 在 Navigator 4.0 中放弃了引用计数方式,转而采用标记清除来实现其垃圾收 集机制。但是,引用计数致使的麻烦并未就此终结。到目前为止,几乎全部的浏览器都是使用的标记清楚策略,只不过垃圾收集的时间间隔稍微不一样。
当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,由于只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其 标记为“离开环境”。
过去几年,JavaScript 垃圾回收(代数、增量、并行、并行垃圾收集)领域的全部改进都是对该算法(mark-and-sweep)的实现进行改进,但并无对垃圾回收算法自己进行改进,其目标是肯定一个对象是否可达。
这样的话,循环引用将再也不是问题
尽管两个对象仍是存在引用,可是他们从 root 出发已是不可达的了。
在Javascript中,完全避免垃圾回收或者是内存泄漏是很是困难的。因此咱们能作的就是减小泄漏,减小垃圾回收的频率。对一些高频使用的函数之类的东西去作一些相似的优化。综合考虑优化成本