ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

JS内存管理之隐藏类和删除操作

2022-06-18 19:33:29  阅读:128  来源: 互联网

标签:name 对象 JS let 内存 new Article 隐藏


内存管理篇

①Chrome浏览器引擎

②内存泄漏

③静态分配与对象池

1.Chrome浏览器的V8 JavaScript引擎

在初始化对象的时候,V8引擎会创建一个隐藏类,随后在程序运行过程中每次增减属性,就会创建一个新的隐藏类或者查找之前已经创建好的隐藏类。每个隐藏类都会记录对应属性在内存中的偏移量,从而在后续再次调用的时候能更快地定位到其位置。

 

比如如下代码:

function Article(){
  this.title='Inauguration Ceremony Features Kazoo Band'  ;
}
let a1=new Article();
let a2=new Article();

V8会在后台配置,让这两个类实例共享相同的隐藏类,因为这两个实例共享的同一个构造函数和原型。假设之后又添加了下面这行代码:

a2.author='Jake';

  此时两个Article实例就会对应两个不同的隐藏类。根据这种操作的频率和隐藏类的大小,这有可能对性能产生明显影响。

  当然,解决方案就是避免JavaScript的“先创建再补充”式的动态属性赋值,并在构造函数中一次性声明所有的属性,如下图所示:

function Article(opt_author){
  this.title=''Inauguration Ceremony Features Kazoo Band'';
  this.author=opt_author;    
}
let a1=new Article();
let a2=new Article('Jake');

这样,两个实例基本上就一样了(不考虑hasOwnProperty的返回值),因此可以共享一个隐藏类,从而带来潜在的性能提升。不过要记住,使用delete关键字会导致生成相同的隐藏类片段。看一下这个例子:

function Article(){
  this.title='Inauguration Ceremony Features Kazoo Band';
  this.author='Jake''    
}
let a1=new Article();
let a2=new Article();
delete a1.author;

代码结束之后,即使两个实例使用了同一个构造函数,它们也不再共享同一个隐藏类。动态删除属性与动态添加属性导致的后果一样。最佳实践是把不想要的属性设置为null。这样可以保持隐藏类不变和继续共享,同时也能达到删除引用值供垃圾回收程序回收的效果。比如:

function Article(){
  this.titile='Inauguiration Ceremony Features Kazoo Band '
  this.author='Jake';  '  
}

let a1=new Article();
let a2=new Article();

a1.author=null;

2.内存泄漏

1)意外声明全局变量

function setName(){
  name='Jake';  
}

2)定时器操作造成的内存泄漏

let name='Jake';
setInterval(()=>{console.log(name)},100);

只要定时器一直运行,回调函数中引用的name就会一直占用内存。

3)闭包造成的内存泄漏

let outer =Function(){
  let name='Jake';
  return function(){
      return name;          
}    
}

这回导致分配给name的内存被泄漏。以上代码创建了一个内部闭包,只要outer函数存在就不能清理name,因为闭包一直再引用着它。假如name的内容很大,那就可能是个大问题。

3.静态分配与对象池

浏览器判断何时进行垃圾回收程序的一个标准就是对象更替的速度。如果又很多对象被初始化,然后一下子又都超出了作用域,那么浏览器就会采用更激进的方式调度垃圾回收程序运行,这样会影响性能。比如下方例子:

functio addVector(a,b){
  let resulttant=new Vector();
  resultant.x=a.x+b.x;
  resultant.y=a.y+b.y;
  return resultant;          
}

调用这个歌函数会在堆上创建一个新对象,然后修改它,最后再返给调用者。如果这个矢量对象的生命周期很短,那么它很快就会失去对它的引用,成为可以被回收的值。假如这个矢量加法函数频繁被调用,那么垃圾回收调度程序会发现这里对象更替的速度很快,从而会更频繁地安排垃圾回收。

  该问题的解决方法是不要动态创建矢量对象,比如可以修改上面的函数让他使用一个已有的矢量对象:

functio addVector(a,b,resultant){
  resultant.x=a.x+b.x;
  resultant.y=a.y+b.y;
  return resultant;          
}

不过这个行为是在其他地方实例化矢量参数resultant,但这个函数的行为没有变。那么在哪里创建矢量可以不让垃圾回收调度程序盯上呢?

其中一种策略是使用对象池,在初始化的某一时刻,可以创建一个对象池,用来管理一组可以回收的对象。应用程序可以向这个对象池请求一个对象、设置其属性、使用它。然后再操作完成后再把它还给对象池。由于没有发生对象初始化,垃圾回收探测就不会发现有对象更替,,因此垃圾回收程序就不会那么频繁地运行,下面是一个对象池的伪实现:

//vectorPool是已有的对象池
let v1=vectorPool.allocate();
let v2=vectorPool.allocate();
let v3=vectorPool.allocate();

v1.x=10;
v1.y=5;
v2.x=-3;
v2.y=-6;


addVector(v1,v2,v3);
console.log([v3.x,v3.y]);  //[7,-1]

vectorPool.free(v1);
vectorPool.free(v2);
vectorPool.free(v3);


//如果对象有属性引用了其他对象
//则这里也需要把这些属性设置为null
v1=null;
v2=null;
v3=null;

如果对象池只按需分配矢量 (在对象不存在时创建新的,对象存在时候则复用存在的),那么这个实现本质上是一个贪婪算法,有单调增长但为静态的内存。这个对象池必须使用某种结构维护所有对象,数组是比较好的选择。不过,使用数组来实现,必须留意不要招致额外的垃圾回收。比如下面例子:

let vectorList=new Array(100);
let vector=new Vector();
vectorList.push(vector);

 

标签:name,对象,JS,let,内存,new,Article,隐藏
来源: https://www.cnblogs.com/jaetyn/p/16389071.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有