App之性能优化

一般来说,浏览器的内存泄漏对于 web 应用程序来说并不是什么问题。用户在页面之间切换,每个页面切换都会引起浏览器刷新。即使页面上有内存泄漏,在页面切换后泄漏就解除了。由于泄漏的范围比较小,因此常常被忽视。

但在移动端,内存泄漏就成了一个比较严重的问题。在单面应用中,用户不能刷新页面的,整个应用程序构建在一个页面上。在这种情况下泄漏会被累积,导致内存不被回收。

Javascript中的垃圾回收机制类似于Java/C#这类语言中的回收机制:

一个对象不再被引用,即将被自动回收

具体回收时刻是我们无法控制的,我们只需适当地解除对象的引用,剩下的事,让运行时去做吧。

在我们开发过程中,往往稍不留神,内存泄露了我们可能都不会察觉:

例1:

1 function doFn(){
2    bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
3 }

不论是你不小心少写了个var,还是觉得这样写很cool,执行doFn(),即退出函数作用域后,bigString会被回收掉么?

不会被回收,bigString现在成为了全局对象window的一个属性,在应用的整个生命周期,window都是一直存在的,所以其属性是不会被销毁的。

例2:

1 var doFn=(function(){
2 var bigString=new Array(1000).join(new Array(2000).join("XXXXX"));
3    return  function(){
4       console.dir(bigString);
5    } ;
6 })();

上面代码运行后,bigString会被回收么?

不会被回收,闭包里的数据是不会被释放的。

例3:

<intput type=”button” value=”submit”  id=”submit” />
1 (function(){
2   var Zombie=function(){};
3   var zombie=new Zombie;
4   var print=function(){
5      console.dir(zombie);
6   };
7   var node=document.getElementById(‘submit’);
8   node.addEventListener(‘click‘,print,false);
9 })()

运行代码后,事件处理函数执行正常,会打印zombie到控制台,而且这里会发生内存泄露,zombie一直不能被回收。

也许有人会说,离开这个页面,zombie就会被释放。在单页应用中,离开当前页面,实质是,移除页面上body内的所有DOM元素,然后再把新的HTML追加至body的DOM树上。

所以,我们来移除button这个节点:

1 node.parentNode.removeChild(node);

执行之后,我们发现页面上按钮被移除了。现在,zombie对象应该被回收了吧?

我们用chrome浏览器的Heap Profiler来追踪下内存,下面是内存快照:

发现即使移除DOM节点,内存泄露一样存在。当我们在移除元素的同时移除其上的事件时,发现这次zombie被回收了:

1 node.parentNode.removeChild(node);
2 node.removeEventListener(‘click’,print,false);

再次追踪内存,已经没有在Zombie类型的对象遗留在内存中了。

所以,我们得出一个结论:移除一个DOM元素的同时,也要移除元素上面的事件,不然很可能会发生内存泄露,伤你于无形。

说到这里,我就想起了zepto里的移除元素的remove方法:

1 remove: function(){
2   return this.each(function(){
3      if(this.parentNode != null)
4        this.parentNode.removeChild(this)
5   })
6 }

说好的要移除元素上面的事件呢?

另外我们对比下zepto和jQuery里的empty方法:

zepto的empty方法:

1 empty: function(){
2      return this.each(function(){ this.innerHTML = ‘‘ })
3 }

jQuery的empty方法:

 1 empty: function() {
 2     var elem,i = 0;
 3     for ( ; (elem = this[i]) != null; i++ ) {
 4            if ( elem.nodeType === 1 ) {
 5            // Prevent memory leaks
 6            jQuery.cleanData( getAll( elem, false ) );
 7            // Remove any remaining nodes
 8            elem.textContent = "";
 9         }
10     }
11     return this;
12 }

API文档里还有这么一句话:

To avoid memory leaks, jQuery removes other constructs such as data and event handlers from the child elements before removing the elements themselves.

可见,对于移除DOM元素时,jQuery处理要更为严谨和合理。

在模块化编程时,当我们会用RequireJS来组织代码时,有一种情况是需要注意的:

1 define([],function(){
2    var obj={
3        bigString:new Array(1000).join(new Array(2000).join("XXXXX"));
4        //…
5    };
6    return obj;
7 });

当这个模块作为一个数据源时,在某个地方被加载一次后,即时当前视图已不再需要它,它还会一直保留在内存中。也就是说,返回值为一个对象时,它是不会被释放的。

至于为何这样,你可以想想,我们define一个类后,能通过require来调用它,那么它肯定是在什么地方被保存了起来。所以,我们这个obj在RequireJs内部也会被引用,无法释放。

也许你会问,那你干嘛要返回一个对象呢?我想,有时候,你应该也是这么做的。

另外,不知道大家的Controller层是如何写的,我是让它继承Backbone.Router的:

 1  jass.Controller = Backbone.Router.extend({
 2         module: "",
 3         name: "",
 4         _bindRoutes: function () {
 5             if (!this.routes) return;
 6             this.routes = _.result(this, ‘routes‘);
 7             var route, routes = _.keys(this.routes);
 8             var prefix = this.module + "/" + this.name + "/";
 9             while ((route = routes.pop()) != null) {
10                 this.route(prefix + route, this.routes[route]);
11             }
12         },
13         close: function () {
14             // destory
15             // remove actions from history.Handlers ???
16             this.stopListening();
17             this.off();
18             this.trigger(‘destroy‘);
19         }
20 });

这样写也会内存泄露,我们跟踪下router方法:

1 this.route(prefix + route, this.routes[route]);  // this -->controller

controller被引用了,它是无法释放的。如果在Controller层上面再引用了Model层表示的数据,泄露将会更加严重。

另外,我这里企图作一些清理工作的close方法根本就没有时机去触发。

我们简化Controller逻辑,它只负责向View层传递Model层的数据时,在多数情况下是会降低泄露的发生。

但是,我们经常会面临这样的问题:

1 多个View之间共享数据;

2 多个Controller之间共享数据;

这时数据应该保存在哪,该何时被清理掉?

为了解决上面的问题,我希望从AngularJS中能得到一些启发,发现它的概念还是挺多的。然后找到AngularJS中依赖注入的模拟代码:

 1 var angular = function(){};
 2
 3 Object.defineProperty(angular,"module",{
 4     value:function(modulename,args){
 5         var module = function(){
 6             this.args = args;
 7             this.factoryObject = {};
 8             this.controllerObject = {};
 9         }
10         module.prototype.factory = function(name,service){
11             //if service is not a function ...
12             //if service() the result is not a object ... and so on
13             this.factoryObject[name] = service();
14         }
15         module.prototype.controller = function(name,args){
16             var _self  = this;
17             //init
18             var content = {
19                 $scope:{},
20                 scope:function(){
21                     return content.$scope;
22                 }
23             //  $someOther:{...}
24             }
25
26             var ctrl = args.pop();
27             console.log(typeof ctrl);
28             var factorys = [];
29             while(service = args.shift()){
30                 if(service in content){
31                     factorys.push(content[service])
32                 }else{
33                     factorys.push(_self.factoryObject[service])
34                 }
35
36             }
37             ctrl.apply(null,factorys);
38
39             _self.controllerObject[name] = function(){
40                 return content;
41             };
42         }
43         var m = new module();
44         window[modulename] = m;
45         return m;
46     }
47 })

测试:

 1 var hello = angular.module(‘Test‘);
 2
 3 hello.factory("actionService",function(){
 4     var say = function(){
 5         console.log("hello")
 6     }
 7     return {
 8         "say":say
 9     }
10 })
11
12 hello.controller("doCtrl",[‘$scope‘,"actionService",function($scope,actionService){
13     $scope.do = function(){
14         actionService.say();
15     }
16 }]);
17
18 hello.controllerObject.doCtrl().scope().do()

可见,AngularJS中构造的模块,控制器也是不会被释放的。

在单页应用开发中,更要警惕内存泄露问题,不然它会是性能优化的一个巨大绊脚石。

性能优化,是一个永久的话题,以后有所感悟,再来补充,持续更新!

最近在研究Sencha Touch,期待有趣的发现!

更多有关性能优化的讨论,推荐阅读:

Memory leaks

Memory leak patterns in JavaScript

Writing Fast,Memory-Efficient JavaScript

Backbone.js And JavaScript Garbage Collection

雅虎网站页面性能优化的34条黄金守则

时间: 2024-12-14 13:32:48

App之性能优化的相关文章

携程App的网络性能优化实践

本文转载至 http://kb.cnblogs.com/page/519824/ 作者: 陈浩然  来源: InfoQ  发布时间: 2015-04-29 23:42  阅读: 4018 次  推荐: 16   原文链接   [收藏] 摘要:在4月23日~25日举行的QCon全球软件开发大会(北京站)上,携程无线开发总监陈浩然分享了<移动开发网络性能优化实践>,总结了携程在App网络性能优化方面的一些实践经验.在2014年接手携程无线App的框架和基础研发工作之后,陈浩然面对的首要工作就是Ap

Android APP性能优化(最新总结)

导语 安卓大军浩浩荡荡,发展已近十个年头,技术优化日异月新,如今Android 8.0 Oreo 都发布了,Android系统性能已经非常流畅了.但是,到了各大厂商手里,改源码自定系统,使得Android原生系统变得鱼龙混杂,然后到了不同层次的开发工程师手里,因为技术水平的参差不齐,即使很多手机在跑分软件性能非常高,打开应用依然存在卡顿现象.另外,随着产品内容迭代,功能越来越复杂,UI页面也越来越丰富,也成为流畅运行的一种阻碍.综上所述,对APP进行性能优化已成为开发者该有的一种综合素质,也是开

Android App 性能优化系列结语篇

关于Android App的优化, 从第一篇的计划开始, 到内存优化的系列文结束, 不知不觉近三个月的时间, 写了十五六篇相关的博文, 算是对自己的知识的一个系统化, 也希望能给大家一些帮助.在此有对此做一个总结. 路线Android App优化1, App性能优化要怎么做在系列的开篇文中, 我们聊到了本系列的一个缘由, 和当时的一个计划, 系列也基本是朝着这个这个方向走的.2, 性能分析工具在此介绍了一些惯用的性能分析工具, 包括官方, 第三方的, 内存分析的, UI分析的, 执行时间性能分析

【腾讯Bugly干货分享】微信读书iOS性能优化

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/578c93ca9644bd524bfcabe8 "8小时内拼工作,8小时外拼成长"这是大家共同的理想.除了每天忙于工作外,我们都希望能更多地区吸收领域内的新知识与新技能,从而走向人生巅峰. Dev Club 是一个交流移动开发技术,结交朋友,扩展人脉的社群,成员都是经过审核的移动开发工程师.每周都会举行嘉宾分享,话题讨论等活动. 上一期我们邀请了腾讯SNG工程师&qu

iOS App 性能优化总结

今天简单总结一些clientapp 优化的方案和方向. 我相信开发一个app大部分团队都能够完毕,可是性能久不一样啦,和我们都写一个冒泡算法一样,我相信每一个人写的冒泡算法都不一样,这些区别就带来了性能的区别. 所以一个好的app 不止看设计.和创意 ,还要看性能. 以下我就简单说几点性能优化点: 一.首页启动速度 启动过程中做的事情越少越好(尽可能将多个接口合并) 不在UI线程上作耗时的操作(数据的处理在子线程进行,处理完通知主线程刷新节目) 在合适的时机開始后台任务(比如在用户指引节目就能够

Android App性能优化(一)之布局优化

当创建复杂布局的时候,我们会在xml 文件中添加大量的ViewGroup和View.伴随着每次迭代,View树的层次越来越深,界面加载速度越来越慢,消耗的内存也越来越多.当您的程序出现加载时短暂黑屏或横竖切换时短暂黑屏,抑或如内存溢出(OOM)之类的问题时,没准您的程序需要优化了. 那么如何让程序运行速度更快?响应更敏捷?优化布局是一个最基本的方法,本文将介绍最基本的优化布局方法. 1.使用ViewStub实现View的延迟加载. 很多情况下,xml布局文件中的部分View初始状态是设置为不显示

App的网络环境测试和性能优化

1. 网络环境测试一般是先用网络损伤模拟仪或mock工具模拟常见的七种损伤和5种网络环境,然后再国内外城市采样的方式(带宽和延时)组合测试生成报告, 下面是一些统计图 2. 采样点的选择一般都是根据自己server收集的用户信息.如果新app就要参考近品/竞品或第三方的统计数据拍脑袋 3. 从测试的角度,应该建立实时监控的web portal.其实测试的目的除了保证产品发布的质量.更重要的是为优化提供依据,所以report最后一部分都是issue list 和optmize advice,当然测

android App性能优化技巧浅谈

Android App性能优化,安卓App性能优化技巧,无论锤子还是茄子手机的不断冒出,Android系统的手机市场占有率目前来说还是最大的,因此基于Android开发的App数量也是很庞大的.那么,如何能开发出更高性能的Android App?相信是软件开发公司以及广大程序员们头疼的一大难题.今天,就给大家提供几个提高Android App性能的技巧. 高效地利用线程1.在后台取消一些线程中的动作 我们知道App运行过程中所有的操作都默认在主线程(UI线程)中进行的,这样App的响应速度就会受

App性能优化浅谈

前言 前段时间给公司的小伙伴们进行了关于app性能优化的技术分享.这里我稍微整理一下也给大家分享一下.关于性能优化这个话题非常大,涉及面能够非常广,也能够非常深入.本人能力有限,不会给大家讲特别难懂,特别底层的东西.都是我们开发能着手去做的点.大家都在讲性能优化,但对于项目经验不够丰富的朋友非常难有一个概念.做优化的时候也会比較茫然,这里我就给大家指明方向. 从何讲起? 笔者在做产品开发的时候,也遇到性能瓶颈.測试project师反馈了一些比較明显的问题,比方UI界面的过度绘制,列表滑动有明显卡