精华内容
下载资源
问答
  • 张阳 【环球网智能报道 记者 张阳】智能手机近年来发展飞速,芯片处理能力越来越强大,2GB内存(RAM)早已成为历史,甚至有不少厂商已经将旗舰机型的内存(RAM)容量扩展到12GB。可能对于普通消费者的认知来说,简单理解...

    张阳

    【环球网智能报道 记者 张阳】智能手机近年来发展飞速,芯片处理能力越来越强大,2GB内存(RAM)早已成为历史,甚至有不少厂商已经将旗舰机型的内存(RAM)容量扩展到12GB。可能对于普通消费者的认知来说,简单理解,内存(RAM)容量越大效果越好,但是具体而言究竟是如何做到的呢?

    983f1c267c912654f26e531346d9d842.png

    多任务处理

    智能手机已经完全改变了我们的通信联系方式,甚至还提供了消息、视频娱乐、游戏等等丰富的应用,可以说手机现在就是网络购物的商场,社交、休闲的中心。

    但是所有这些应用程序都会消耗大量的内存和设备处理能力。因此,大家的普遍认知是非常正确的,如果手机内存量不足,手机的效能会大大降低,因此,它可能无法同时满足不同应用程序同时执行任务。

    更高效率

    智能手机已成为接入网络的首选设备,移动网络的速度越来越快,从3G到4G,如今已经迎来了5G时代,网速的提升也给存储和处理器的性能带来了更大的压力。此外更大的显示屏幕,更清晰的图像这都需要更多的内存。

    最近的视频和游戏都有了更好的图像和视觉效果,如果你想要更好的享受游戏乐趣,畅享高清视频娱乐内容,同样也需要更多的内存支持,这样能保持你的设备长时间运行不卡顿。

    超前法则

    未来的总是更好的,这是被大多数手机制造商所采用的经验法则,每一年智能手机都会被推动向更好的性能发展,但是对于消费者来说不可能每年更换一部设备,你的手机也要为未来而考虑,超出当前性能的内存量,是为了未来所考虑,确保在后续的性能升级中,你的手机仍然能保持不错的性能。

    随着Android和iOS版本的更新,8GB内存甚至更大容量内存会成为智能手机的标准,确保在接下来新功能推出时,你的手机还能更加可靠而且高效。

    责编:张阳

    版权作品,未经环球网 huanqiu.com 书面授权,严禁转载,违者将被追究法律责任。

    展开全文
  • Chrome限制了浏览器所能使用的内存极限(64位1.4GB,32位1.0GB),这也就意味着浏览器将无法直接操作一些大内存对象。 V8引擎在执行垃圾回收时会阻塞 JavaScript应用逻辑,直到垃圾回收结束再重新执行JavaScript...
  • 为什么要关注内存如果我们有内存溢出,程序占用内存越来越大,最终引起客户端卡顿,甚至无响应。如果我们使用Node.js做后端应用,因为后端程序会长时间运行,如果有内存溢出,造成的后果会更严重,服务器内存...
    为什么要关注内存
    如果我们有内存溢出,程序占用的内存会越来越大,最终引起客户端卡顿,甚至无响应。如果我们使用Node.js做后端应用,因为后端程序会长时间运行,如果有内存溢出,造成的后果会更严重,服务器内存可能会很快就消耗光,应用不能正常运行。

    JS数据类型与JS内存机制

    JS有如下数据类型

    原始数据类型:String, Number, Boolean, Null, Undefined, Symbol

    引用数据类型:Object(对象,数组,函数)

    而存放这些数据的内存又可以分为两部分:栈内存(Stack)和堆内存(Heap)。

    原始数据类型(基本类型)的值直接存在栈内存中,值与值之间是独立存在,修改一个变量的值不会影响其他变量的值

    对象类型(引用类型)存在堆内存中,每创建一个对象,就会在堆内存中开辟出一个新的空间,而变量保存的对象的内存地址(对象的引用,如果两个变量保存的同一个对象,当一个变量改变属性值时,另一个变量的属性值也会变 )

    栈内存

    栈是一种只能一端进出的数据结构,先进后出,后进先出。假如我们有如下三个变量:

    var a = 10;
    var b = 'hello';
    var c = true;
    

    根据我们的定义顺序,a会首先入栈,然后是b,最后是c。最终结构图如下所示:

    11d9d06f0eb14e543ed1e51761265ddd.png

    我们定义一个变量是按照如下顺序进行的,以var a = 10; 为例,我们先将10放入内存,然后申明一个变量a,这时候a的值是undefined,最后进行赋值,就是将a与10关联起来。

    650d6cfeb4f6c6616668c752d17d5601.png

    a600ff5fb9deffdba8287c1245b3f645.png

    e8a4ed376bb21f6e7024b5f5d10caceb.png

    从一个栈删除元素就是出栈,从栈顶删除,他相邻的元素成为新的栈顶元素。

    7d114ddf1ccdff8c5c3236739b14becc.png

    堆内存

    JS中原始数据类型的内存大小是固定的,由系统自动分配内存。

    但是引用数据类型,比如Object, Array,他们的大小不是固定的,所以是存在堆内存的。

    JS不允许直接操作堆内存,我们在操作对象时,操作的实际是对象的引用,而不是实际的对象。

    可以理解为对象在栈里面存了一个内存地址,这个地址指向了堆里面实际的对象。所以引用类型的值是一个指向堆内存的引用地址。

    f8e3f924fb57d5eef65a87494a440309.png

    函数也是引用类型,当我们定义一个函数时,会在堆内存中开辟一块内存空间,将函数体代码以字符串的形式存进去。然后将这块内存的地址赋值给函数名,函数名和引用地址会存在栈上。

    acb81e1e5c3ef4ffe9a6cc15aaff805d.png

    可以在Chrome调试工具中尝试一下,定义一个方法,然后不加括号调用,直接输出函数,可以看到,打印出来的是函数体字符串:

    f30a9355222cd83cb16e68c49d46b680.png

    垃圾回收

    垃圾回收就是找出那些不再继续使用的变量,然后释放其占用的内存,垃圾回收器会按照固定的时间间隔周期性执行这一操作。JS使用垃圾回收机制来自动管理内存,但是他是一把双刃剑:

    优势: 可以大幅简化程序的内存管理代码,降低程序员负担,减少因为长时间运行而带来的内存泄漏问题。 劣势: 程序员无法掌控内存,JS没有暴露任何关于内存的API,我们无法进行强制垃圾回收,更无法干预内存管理。

    引用计数(reference counting)

    引用计数是一种回收策略,它跟踪记录每个值被引用的次数,每次引用的时候加一,被释放时减一,如果一个值的引用次数变成0了,就可以将其内存空间回收。

    const obj = {a: 10};  // 引用 +1
    const obj1 = {a: 10};  // 引用 +1
    const obj = {};  // 引用 -1
    const obj1 = null;  // 引用为 0
    

    当声明了一个变量并将一个引用类型值赋值该变量时,则这个值的引用次数就是1.如果同一个值又被赋给另外一个变量,则该值得引用次数加1。相反,如果包含对这个值引用的变量又取 得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那 些引用次数为零的值所占用的内存。

    使用引用计数会有一个很严重的问题:循环引用。循环引用指的是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用。

    function problem(){ 
        var objectA = {};
        var objectB = {}; 
    
        objectA.a = objectB;
        objectB.b = objectA; 
    }
    

    在这个例子中,objectA 和 objectB 通过各自的属性相互引用;也就是说,这两个对象的引用次数都是 2。当函数执行完毕后,objectA 和 objectB 还将继续存在,因为它们的引用次数永远不会是 0。

    因为引用计数有这样的问题,现在浏览器已经不再使用这个算法了,这个算法主要存在于IE 8及以前的版本,现代浏览器更多的采用标记-清除算法。在老版的IE中一部分对象并不是原生 JavaScript 对象。例如,其 BOM 和 DOM 中的对象就是使用 C++以 COM(Component Object Model,组件对象模型)对象的形式实现的,而 COM对象的垃圾 收集机制采用的就是引用计数策略。

    因此,即使 IE的 JavaScript引擎是使用标记清除策略来实现的,但 JavaScript访问的 COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及 COM对象,就会存在循环引用的问题。

    下面这个简单的例子,展示了使用 COM对象导致的循环引用问题:

    var element = document.getElementById("some_element"); 
    var myObject = new Object();
    myObject.element = element; 
    element.someObject = myObject;
    

    这个例子在一个 DOM元素(element)与一个原生 JavaScript对象(myObject)之间创建了循环引用。

    其中,变量 myObject 有一个名为 element 的属性指向 element 对象;而变量 element 也有 一个属性名叫 someObject 回指 myObject。

    由于存在这个循环引用,即使将例子中的 DOM从页面中移除,它也永远不会被回收。

    为了避免类似这样的循环引用问题,最好是在不使用它们的时候手工断开原生 JavaScript 对象与 DOM元素之间的连接。例如,可以使用下面的代码消除前面例子创建的循环引用:

    myObject.element = null; 
    element.someObject = null;
    

    将变量设置为 null 意味着切断变量与它此前引用的值之间的连接。当垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存。

    为了解决上述问题,IE9把 BOM和 DOM对象都转换成了真正的 JavaScript对象。这样,就避免了两种垃圾收集算法并存导致的问题,也消除了常见的内存泄漏现象。

    标记-清除算法

    标记-清除算法就是当变量进入环境是,这个变量标记位“进入环境”;而当变量离开环境时,标记为“离开环境”,当垃圾回收时销毁那些带标记的值并回收他们的内存空间。这里说的环境就是执行环境,执行环境定义了变量或函数有权访问的数据。每个执行环境都有一个与之关联的变量对象(variable object),环境中所定义的所有变量和函数都保存在这个对象中。某个执行环境中所有代码执行完毕后,改环境被销毁,保存在其中的所有变量和函数也随之销毁。

    全局执行环境

    全局执行环境是最外围的一个执行环境,在浏览器中,全局环境是window,Node.js中是global对象。全局变量和函数都是作为window或者global的属性和方法创建的。全局环境只有当程序退出或者关闭网页或者关闭浏览器的时候才会销毁。

    局部执行环境(环境栈)

    每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境会被推入一个环境栈中。当这个函数执行之后,栈将其环境弹出,把控制权返回给之前的环境。ECMAScript程序中的执行流就是这个机制控制的。

    0488176275bb9bda673ab432c3dc6d01.png

    在一个环境中声明变量的时候,垃圾回收器将其标记为“进入环境”,当函数执行完毕时,将其标记为“离开环境”,内存被回收。

    可能造成内存泄露的情况

    上面我们提到了两种可能造成内存泄露的情况:

    1. 对象之间的循环引用
    1. 老版IE(IE8及以前)里面DOM与对象之间的循环引用

    其他也可能造成循环引用的情况:

    1. 全局变量会存在于整个应用生命周期,应用不退出不会回收,使用严格模式可以避免这种情况
    2. 闭包因为自身特性,将函数内部变量暴露到了外部作用域,当其自身执行结束时,所暴露的变量并不会回收
    3. 没有clear的定时器

    V8的内存管理

    V8是有内存限制的,因为它最开始是为浏览器设计的,不太可能遇到大量内存的使用场景。关键原因还是垃圾回收所导致的线程暂停执行的时间过长。根据官方说法,以1.5G内存为例,V8一次小的垃圾回收需要50ms,而一次非增量的,即全量的垃圾回收更需要一秒。这显然是不可接受的。因此V8限制了内存使用的大小,但是Node.js是可以通过配置修改的,更好的做法是使用Buffer对象,因为Buffer的内存是底层C++分配的,不占用JS内存,所以他也就不受V8限制。

    新生代

    新生代内存中的垃圾回收主要通过 Scavenge 算法进行,具体实现时主要采用了 Cheney 算法。新生代的堆内存被分为多个Semispace,每个Semispace分为两部分from和to,只有from的空间是使用中的,分配对象空间时,只在from中进行分配,to是闲置的。进行垃圾回收时按照如下步骤进行:

    1. 找出from中还在使用的对象,即存活的对象
    2. 将这些活着的对象全部复制到to
    3. 反转from和to,这时候from中全部是存活对象,to全部是死亡对象
    4. 对to进行全部回收

    40c2e5b7143107bf408adc58e4dccaaa.png

    可以看到在新生代中我们复制的是存活的对象,死亡对象都留在原地,最后被全部回收。这是因为对于大多数新增变量来说,可能只是用一下,很快就需要释放,那在新生代中每次回收会发现存活的是少数,死亡的是多数。那我们复制的就是少数对象,这样效率更高。如果一个变量在新生代中经过几次复制还活着,那他生命周期可能就比较长,会晋升到老生代。有两种情况会对对象进行晋升:

    1. 新生代垃圾回收过程中,当一个对象经过多次复制后还存活,移动到老生代;
    2. 在from和to进行反转的过程中,如果to空间的使用量超过了25%,那么from的对象全部晋升到老生代

    老生代

    老生代存放的是生命周期较长的对象,他的结构是一个连续的结构,不像新生代分为from和to两部分。老生代垃圾回收有两种方式,标记清除和标记合并。

    53bb92e38a34c84acb50317b6a188cdd.png

    标记清除

    标记清除是标记死亡的对象,直接其空间释放掉。在标记清除方法清除掉死亡对象后,内存空间就变成不连续的了,所以出现了另一个方案:标记合并。

    af9101c7d62a4a6e480afcce6ce760b2.png

    标记合并

    这个方案有点像新生代的Cheney算法,将存活的对象移动到一边,将需要被回收的对象移动到另一边,然后对需要被回收的对象区域进行整体的垃圾回收。

    8d6f61b64df24aeb9d3d1a612daba149.png

    与新生代算法相比,老生代主要操作死亡对象,因为老生代都是生命周期较长的对象,每次回收死亡的比较少;而新生代主要操作的存活对象,因为新生代都是生命周期较短的对象,每次回收存活的较少。这样无论新生代还是老生代,每次回收时都尽可能操作更少的对象,效率就提高了。

    展开全文
  • 为什么要使用微服务

    2019-07-13 13:23:34
    以下是我个人对“为什么要使用微服务”的理解 ...一整个应用占用一个进程,一旦哪个部分出现什么错误,比如内存泄漏,便会拖垮整个项目; 每次功能修改,都要整个项目重新部署,部分公司的应用每天都会修改,...

    以下是我个人对“为什么要使用微服务”的理解

    一般来说以往的应用都是单体应用,随着时间的推移,一个应用可能会越来越大,代码量从数千行递增到几十万,上百万行,这个时候,每次编译就会耗费很长的时间,编码十分钟,编译两小时(有点夸张),降低了生产力;

    一整个应用占用一个进程,一旦哪个部分出现什么错误,比如内存泄漏,便会拖垮整个项目;

    每次功能修改,都要整个项目重新部署,大部分公司的应用每天都会修改,这样的话每天都要进行部署,耗费时间;

    还有就是想用新的技术,就会比较麻烦,比如想用新的框架,那么就要全部进行重写,这会耗费很多的资源和人力,成本大大提升

     

     

    展开全文
  • JavaScript的内存管理

    2020-11-10 15:04:23
    如果我们有内存溢出,程序占用内存越来越大,最终引起客户端卡顿,甚至无响应。如果我们使用Node.js做后端应用,因为后端程序会长时间运行,如果有内存溢出,造成的后果会更严重,服务器内存可能会很快就消耗光...

    为什么要关注内存
    如果我们有内存溢出,程序占用的内存会越来越大,最终引起客户端卡顿,甚至无响应。如果我们使用Node.js做后端应用,因为后端程序会长时间运行,如果有内存溢出,造成的后果会更严重,服务器内存可能会很快就消耗光,应用不能正常运行。
    JS数据类型与JS内存机制
    JS有如下数据类型

    原始数据类型:String, Number, Boolean, Null, Undefined, Symbol

    引用数据类型:Object(对象,数组,函数)

    而存放这些数据的内存又可以分为两部分:栈内存(Stack)和堆内存(Heap)。

    原始数据类型(基本类型)的值直接存在栈内存中,值与值之间是独立存在,修改一个变量的值不会影响其他变量的值

    对象类型(引用类型)存在堆内存中,每创建一个对象,就会在堆内存中开辟出一个新的空间,而变量保存的对象的内存地址(对象的引用,如果两个变量保存的同一个对象,当一个变量改变属性值时,另一个变量的属性值也会变 )

    栈内存

    栈是一种只能一端进出的数据结构,先进后出,后进先出。假如我们有如下三个变量:

    var a = 10;
    var b = 'hello';
    var c = true;
    

    根据我们的定义顺序,a会首先入栈,然后是b,最后是c。最终结构图如下所示:
    在这里插入图片描述

    我们定义一个变量是按照如下顺序进行的,以var a = 10; 为例,我们先将10放入内存,然后申明一个变量a,这时候a的值是undefined,最后进行赋值,就是将a与10关联起来。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    从一个栈删除元素就是出栈,从栈顶删除,他相邻的元素成为新的栈顶元素。
    在这里插入图片描述
    堆内存
    JS中原始数据类型的内存大小是固定的,由系统自动分配内存。

    但是引用数据类型,比如Object, Array,他们的大小不是固定的,所以是存在堆内存的。

    JS不允许直接操作堆内存,我们在操作对象时,操作的实际是对象的引用,而不是实际的对象。

    可以理解为对象在栈里面存了一个内存地址,这个地址指向了堆里面实际的对象。所以引用类型的值是一个指向堆内存的引用地址。
    在这里插入图片描述

    函数也是引用类型,当我们定义一个函数时,会在堆内存中开辟一块内存空间,将函数体代码以字符串的形式存进去。然后将这块内存的地址赋值给函数名,函数名和引用地址会存在栈上。
    在这里插入图片描述
    可以在Chrome调试工具中尝试一下,定义一个方法,然后不加括号调用,直接输出函数,可以看到,打印出来的是函数体字符串:

    在这里插入图片描述
    垃圾回收
    垃圾回收就是找出那些不再继续使用的变量,然后释放其占用的内存,垃圾回收器会按照固定的时间间隔周期性执行这一操作。JS使用垃圾回收机制来自动管理内存,但是他是一把双刃剑:

    优势: 可以大幅简化程序的内存管理代码,降低程序员负担,减少因为长时间运行而带来的内存泄漏问题。
    劣势: 程序员无法掌控内存,JS没有暴露任何关于内存的API,我们无法进行强制垃圾回收,更无法干预内存管理。

    引用计数(reference counting)
    引用计数是一种回收策略,它跟踪记录每个值被引用的次数,每次引用的时候加一,被释放时减一,如果一个值的引用次数变成0了,就可以将其内存空间回收。

    const obj = {a: 10};  // 引用 +1
    const obj1 = {a: 10};  // 引用 +1
    const obj = {};  // 引用 -1
    const obj1 = null;  // 引用为 0
    

    当声明了一个变量并将一个引用类型值赋值该变量时,则这个值的引用次数就是1.如果同一个值又被赋给另外一个变量,则该值得引用次数加1。相反,如果包含对这个值引用的变量又取 得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那 些引用次数为零的值所占用的内存。

    使用引用计数会有一个很严重的问题:循环引用。循环引用指的是对象A中包含一个指向对象B的指针,而对象B中也包含一个指向对象A的引用。

    function problem(){ 
        var objectA = {};
        var objectB = {}; 
    
        objectA.a = objectB;
        objectB.b = objectA; 
    }
    

    在这个例子中,objectA 和 objectB 通过各自的属性相互引用;也就是说,这两个对象的引用次数都是 2。当函数执行完毕后,objectA 和 objectB 还将继续存在,因为它们的引用次数永远不会是 0。

    因为引用计数有这样的问题,现在浏览器已经不再使用这个算法了,这个算法主要存在于IE 8及以前的版本,现代浏览器更多的采用标记-清除算法。在老版的IE中一部分对象并不是原生 JavaScript 对象。例如,其 BOM 和 DOM 中的对象就是使用 C++以 COM(Component Object Model,组件对象模型)对象的形式实现的,而 COM对象的垃圾 收集机制采用的就是引用计数策略。

    因此,即使 IE的 JavaScript引擎是使用标记清除策略来实现的,但 JavaScript访问的 COM对象依然是基于引用计数策略的。换句话说,只要在IE中涉及 COM对象,就会存在循环引用的问题。

    下面这个简单的例子,展示了使用 COM对象导致的循环引用问题:

    var element = document.getElementById("some_element"); 
    var myObject = new Object();
    myObject.element = element; 
    element.someObject = myObject;
    

    这个例子在一个 DOM元素(element)与一个原生 JavaScript对象(myObject)之间创建了循环引用。

    其中,变量 myObject 有一个名为 element 的属性指向 element 对象;而变量 element 也有 一个属性名叫 someObject 回指 myObject。

    由于存在这个循环引用,即使将例子中的 DOM从页面中移除,它也永远不会被回收。

    为了避免类似这样的循环引用问题,最好是在不使用它们的时候手工断开原生 JavaScript 对象与 DOM元素之间的连接。例如,可以使用下面的代码消除前面例子创建的循环引用:

    myObject.element = null; 
    element.someObject = null;
    

    将变量设置为 null 意味着切断变量与它此前引用的值之间的连接。当垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存。

    为了解决上述问题,IE9把 BOM和 DOM对象都转换成了真正的 JavaScript对象。这样,就避免了两种垃圾收集算法并存导致的问题,也消除了常见的内存泄漏现象。

    标记-清除算法

    标记-清除算法就是当变量进入环境是,这个变量标记位“进入环境”;而当变量离开环境时,标记为“离开环境”,当垃圾回收时销毁那些带标记的值并回收他们的内存空间。这里说的环境就是执行环境,执行环境定义了变量或函数有权访问的数据。每个执行环境都有一个与之关联的变量对象(variable object),环境中所定义的所有变量和函数都保存在这个对象中。某个执行环境中所有代码执行完毕后,改环境被销毁,保存在其中的所有变量和函数也随之销毁。

    全局执行环境

    全局执行环境是最外围的一个执行环境,在浏览器中,全局环境是window,Node.js中是global对象。全局变量和函数都是作为window或者global的属性和方法创建的。全局环境只有当程序退出或者关闭网页或者关闭浏览器的时候才会销毁。

    局部执行环境(环境栈)

    每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境会被推入一个环境栈中。当这个函数执行之后,栈将其环境弹出,把控制权返回给之前的环境。ECMAScript程序中的执行流就是这个机制控制的。
    在这里插入图片描述
    在一个环境中声明变量的时候,垃圾回收器将其标记为“进入环境”,当函数执行完毕时,将其标记为“离开环境”,内存被回收。

    可能造成内存泄露的情况
    上面我们提到了两种可能造成内存泄露的情况:

    1.对象之间的循环引用

    2.老版IE(IE8及以前)里面DOM与对象之间的循环引用

    其他也可能造成循环引用的情况:

    1.全局变量会存在于整个应用生命周期,应用不退出不会回收,使用严格模式可以避免这种情况
    2.闭包因为自身特性,将函数内部变量暴露到了外部作用域,当其自身执行结束时,所暴露的变量并不会回收
    3.没有clear的定时器

    V8的内存管理

    V8是有内存限制的,因为它最开始是为浏览器设计的,不太可能遇到大量内存的使用场景。关键原因还是垃圾回收所导致的线程暂停执行的时间过长。根据官方说法,以1.5G内存为例,V8一次小的垃圾回收需要50ms,而一次非增量的,即全量的垃圾回收更需要一秒。这显然是不可接受的。因此V8限制了内存使用的大小,但是Node.js是可以通过配置修改的,更好的做法是使用Buffer对象,因为Buffer的内存是底层C++分配的,不占用JS内存,所以他也就不受V8限制。

    新生代

    新生代内存中的垃圾回收主要通过 Scavenge 算法进行,具体实现时主要采用了 Cheney 算法。新生代的堆内存被分为多个Semispace,每个Semispace分为两部分from和to,只有from的空间是使用中的,分配对象空间时,只在from中进行分配,to是闲置的。进行垃圾回收时按照如下步骤进行:

    1. 找出from中还在使用的对象,即存活的对象
    2. 将这些活着的对象全部复制到to
    3. 反转from和to,这时候from中全部是存活对象,to全部是死亡对象
    4. 对to进行全部回收
    

    在这里插入图片描述
    可以看到在新生代中我们复制的是存活的对象,死亡对象都留在原地,最后被全部回收。这是因为对于大多数新增变量来说,可能只是用一下,很快就需要释放,那在新生代中每次回收会发现存活的是少数,死亡的是多数。那我们复制的就是少数对象,这样效率更高。如果一个变量在新生代中经过几次复制还活着,那他生命周期可能就比较长,会晋升到老生代。有两种情况会对对象进行晋升:

    1.新生代垃圾回收过程中,当一个对象经过多次复制后还存活,移动到老生代;
    2.from和to进行反转的过程中,如果to空间的使用量超过了25%,那么from的对象全部晋升到老生代
    

    老生代

    老生代存放的是生命周期较长的对象,他的结构是一个连续的结构,不像新生代分为from和to两部分。老生代垃圾回收有两种方式,标记清除和标记合并。
    在这里插入图片描述
    标记清除

    标记清除是标记死亡的对象,直接其空间释放掉。在标记清除方法清除掉死亡对象后,内存空间就变成不连续的了,所以出现了另一个方案:标记合并。

    在这里插入图片描述
    标记合并

    这个方案有点像新生代的Cheney算法,将存活的对象移动到一边,将需要被回收的对象移动到另一边,然后对需要被回收的对象区域进行整体的垃圾回收。

    在这里插入图片描述
    与新生代算法相比,老生代主要操作死亡对象,因为老生代都是生命周期较长的对象,每次回收死亡的比较少;而新生代主要操作的存活对象,因为新生代都是生命周期较短的对象,每次回收存活的较少。这样无论新生代还是老生代,每次回收时都尽可能操作更少的对象,效率就提高了。

    展开全文
  • 为什么要并发充分利用CPU计算能力随时多核处理器的发展,计算机的计算能力越来越强大。但是,由于磁盘读写、网络IO等这些通信、存储系统与CPU的速度相差多个数量级,我们在应用计算机时,如果单纯的让cpu等待磁盘IO...
  • 你工作几年了,经验丰富,但是为什么你的应用卡的慢? Android手机开发语言是java(kotlin也是java的封装),Java本身有很好的内存管理机制——GC。但是GC经常遇到不可恢复的内存,即内存泄漏和内存抖动,结果可能是...
  • 内存泄漏是一种程序错误,有内存泄漏的程序会不断向系统申请内存,然后使用所申请的内存,当不再使用这些内存时也不释放它们,其占用内存越来越大。如果有内存泄漏的程序是一个长时间运行的应用程序,如交互式...
  • 反过来,如果内存运行不稳定,可以将此参数设,以提高内存稳定性。 Act to Precharge Delay: 这个项目控制了给DRAM参数使用之DRAM频率的数值。同理,数值小性能高,但是对内存的质量也要求严格! DRAM RAS# to...
  •  关于占用内存的问题,很多用户反映 WinMount 占用内存过多。这实际上是使用方法的问题。当 WinMount 把一个ZIP 或RAR文件 mount 一个虚拟盘时,实际上并不占用多少内存,虽然你看到的虚拟盘有2GB这么。对虚拟...
  • **为什么需要量化: ** 随着深度学习的发展,模型变得越来越庞大,这就非常不利于将模型应用到一些低成本的嵌入式系统的情况。为了解决该问题,模型量化应运而生。目的就是在损失少量精度的情况下对模型进行压缩,使...
  • loadrunner测试资料

    2010-03-21 16:51:02
     随着web应用的增多,服务器应用解决方案中以Web核心的应用越来越多,很多公司各种应用的架构都以web应用为主。一般的web测试和以往的应用程序的测试的侧重点不完全相同,在基本功能已经通过测试后,就要进行...
  • 1、为什么需要使用线程池(Thread Pool)减少线程间上下文切换。线程执行一定的时间片后,系统会自动把cpu切换给另一个线程使用,这时还需要保存当 前的线程上下文状态,并加载新线程的上下文状态。当程序中有大量的...
  • 因为即使删除了某个程序,可是它使用的DLL文件仍然会存在,因而随着使用日久,Windows的启动和退出时需要加载的DLL动态链接库文件越来越大,自然系统运行速度也就越来越慢了。这时我们就需要使用一些彻底删除DLL的...
  • 但是,随着.NET Framework中越来越多的库发布NuGet包,越来越多的功能超出了.NET Framework的界限。例如,Entity Framework、ASP.NET Web API和其他.NET库都获得了巨大的改进。 对于.NET Framework的每个版本,...
  • ASP.NET在线测评系统

    热门讨论 2009-12-25 22:26:50
    4)CLR集成了内存和过程的管理从而实现了系统的可伸缩性,而绝多数系统的功能都集中在CLR中,因此,不需要再付出任何代价,即可获得应用程序的较伸缩性。 5)秉承了微软的一贯作风:一个良好的IDE环境,即使...
  • 当一个Windows函数返回一个错误代码时,它常常可以用来了解函数为什么会运行失败。Microsoft公司编译了一个所有可能的错误代码的列表,并且为每个错误代码分配了一个32位的号码。 从系统内部来讲,当一个Windows函数...
  • vfp6.0系统免费下载

    2009-09-17 13:49:13
    问题 2-4: 为什么有两个应用程序向导? 答案: 为了简化 Visual FoxPro 开发人员开发应用程序的过程,在 6.0 版中加入了很多新特性,其中包括新的应用程序框架、应用程序生成器、和组件管理库。为保持向后兼容性,也...
  • 1、为什么要引入“进程” (1)进程调度属于低级处理机管理,即确定系统中哪个进程将获得CPU;而作业调度属于高级处理机管理,即确定系统中哪些作业将获得CPU。 (2)进程是一个具有一定独立功能的程序...
  •  本书针对大多数日常的oracle database 11g数据库管理任务,全面覆盖dba行业知识,并将理论与实践相结合,旨在初中级dba提供高效运行数据库所需的方方面面的知识,帮助他们从oracle公司发行的大量资料中找到自己...
  • sesvc.exe 阿萨德

    2019-06-02 17:11:12
    codeceo 首页问答热门文章RSS订阅 文章首页 ...但是为什么呢?简单分析下。 看过上文的还记得在 HashMap 扩容的时候会调用 resize() 方法,就是这里的并发操作容易在一个桶上形成环形链表;这样当获取...
  • 很强的ccna教程

    2013-12-02 15:02:54
    低开销:协议自身开销(占用内存、CPU、网络带宽等) 安全性 普适性:适合各种拓扑和各种规模网络 可扩展性 强壮性与稳定性:在面临非正常或不可预见的情况下还能够正常工作 距离矢量路由协议 1、距离矢量路由协议...
  • RamDisk Plus,绝对好使

    2008-12-04 13:52:08
    可是硬碟呢,虽然硬碟的容量越来越大, 可是速度的发展却远远的落后了。不过我们有变通的方法,就是把记忆体当作硬碟。是不是很奇怪, 平时都用硬碟来当做记忆体的,不过现在我们要反其道行之。毕竟硬碟的速度跟...
  • meise_UGenius U盘加密

    2009-09-03 13:13:49
    4、为什么我打开魅色U盘精灵就出现一个错误提示框:I/O错误,请检查是否选定的驱动器内没有磁盘 答:这种情况一般会出现在有内置读卡器且没有插入存储卡的笔记本上,只要在“请选择可移动磁盘”的下拉 列表中选择你...
  • 一个的商品中心服务可以被拆类目、商品、价格、库存四个微服务。这样每个服务的职责专注,接口单一。每个服务可以有自己的存储,甚至可以用不同的存储,比如商品适合Elasticsearch的搜索引擎&...

空空如也

空空如也

1 2
收藏数 34
精华内容 13
关键字:

为什么应用占用内存越来越大