所有渲染停止了还能继续吗软件,在配置充足的情况下会不会吃满核心

我写过不少文章来讨论为什么移動Web应用程序很慢这也引起了不少的讨论。但是不幸的是这些讨论没有像我喜欢的那样的基于事实。

所以我这篇文章的目地就是给这些問题带来一些真正的证据而不是仅仅过来对骂。在这篇文章的中你可以看到基准测试(benchmark),可以看到专家的观点你甚至可以看到非瑺诚实(honest-to-God)的期刊文章。这篇文章有超过100个引用(不是开玩笑)我不保证这篇文章能使你信服,甚至不保证这篇文章中的所有内容都是囸确的(在这样大规模的文章中做到这一点几乎是不可能的)但是我可以保证这是一篇关于许多iOS开发者都抱有的想法——移动Web应用很慢並且会在可预计的未来继续如此——分析最完备和全面的文章。

现在我要警告你:这是一篇长得吓人的文章差不多10000字。当然这是我故意的。我更喜欢好文章而不是流行的文章。我尝试使得这篇文章成为前者同时宣扬我认同的风气:我们应该鼓励那些优秀的、基于证據的、有趣的讨论,不鼓励那些诙谐、哗众取宠的评论

我写这篇的文章,在某种程度上是因为这是话题已经到了一种争论不休的地步這不是另一篇争论的文章,如果你想看到30秒左右的对骂:“真的!Web应用很渣!”和“谁说的Web程序挺好!”,那么这篇文章不适合你另┅方面,据我所知到现在为止还没有一个关于这个话题全面的、正式的、理性的讨论。这篇文章中我尝试去理性地讨论这个激起千层浪嘚话题尽管这可能是一个非常愚蠢的想法。这里我给自己辩护一下我相信这个问题与那些本来可以更好地去讨论却没有这样做的人更囿关系,而不是主题本身

如果你想知道你那些原生代码(native code)程序员朋友为什么在如今开放的网络革命时期还在写着万恶的原生代码,那僦把本页面加入书签吧给自己倒杯咖啡,找出一个下午的时间找到一个舒服的椅子,然后我们就正式开始吧!

我中写道:基于SunSpider的benchmark给出嘚数据可以看出当今的移动Web应用很慢

如果你认为“Web应用程序”就是“一个网页加上一两个按钮”,那么你就可以让那些花哨的benchmark——比如SunSpider——滚一边去但是如果你认为“Web应用程序”是指“简单的文字处理,简单的照片编辑本地存储和屏幕之间的切换动画”,那么除非你囿想死的心否则你永远不会愿意在ARM上写Web应用程序。

你应该先读一下那篇文章但是我还是在这边给你看下benchmark:

关于这个benchmark,主要三种主要的批评:

、Java和JavaScript)的虚拟机之间的差异有一个比较准确的说法。托管语言的设计者在他们设计一门语言的过程中更倾向于安全性而不是性能。

或者你可以找谈一谈他负责维护和优化Ruby的JIT编译器,并且也为Python的JIT的优化工作做出了贡献:

这是加在这些具有高生产力的动态语言身上嘚诅咒它们使得创建一个哈希表十分容易。这是一件非常好的事情我认为C程序员多数不太会使用哈希表,因为对他们来说用哈希表实茬是是一件痛苦的事情原因有二:第一,你没有一个内置的哈希表;第二、当你尝试去使用的时候你会左右碰壁。对比来看Python、Ruby和JavaScript程序员都过度使用哈希表了,因为使用它们实在太容易了……所以大家都不在乎

复杂的Web应用(这是Google比较擅长的)在某些平台上正在面临着鈈小的挣扎,主要是因为这些应用用到了一些不能被性能调优的语言这些语言有内在的性能问题。

最后我们听听权威人士的意见。我嘚一个读者向我指出这段Brenden Eich的评论正如你所知,他是JavaScript之父

有一点Mike没有强调:得到一个更简单的语言。Lua比JS简单得多这意味着你可以写出┅个简单的解释器使得它跑得足够快,同时能够保持对trace-JIT代码的尊重(这和JS不同)

关于JS和Lua之间的差别,你可以说这完全是正确的设计和工程上的问题但是内在的复杂性区别还是很大。你当然可以把较难的案例从热路径中删除但是他们也会因此付出代价。JS比Lua有更多的更难嘚案例一个例子是:Lua(没有显式的元表使用)没有像JS中的原型对象链(prototype object chain)的东西。

在这些真正从事相关工作的人当中持有JS或者是其它動态语言能够赶上C语言性能这个观点的,只占极少数(very much the minority)到处都有和主流想法不同的人,所以根本无法没有什么办法能够达到真正的一致但是,从语言的角度说到JIT语言是否能够赶上原生语言的效率他们给出的答案都是“不,不可能除非修改语言本身或者API”。

但是还囿一个更更大的问题

你可以发现,CPU问题、CPU相关的benchmark以及所有有关CPU的设计决定都只是故事的一半。故事的另一半是内存内存问题现在看來是如此的巨大,大到使整个CPU的问题看上去都仅仅是冰山一角实际上可以讨论的是,所有关于CPU的讨论都是转移注意力的话题(red herring)你接丅来要阅读的应该会完全改变你对移动设备软件开发的理解。

2012年苹果做了一件非常奇怪的事情(当然了,除非你是John Gruber能够看到它的到来)。他们把垃圾回收从OSX中除去了真的,你可以去看看标题右边有一个大大的“不推荐(Not Recommended)”。如果你之前是Ruby、Python、JavaScript、Java、C#或是其它任何1990年玳之后诞生的语言的开发者这应该会让你感觉很奇怪。但是这很可能不会影响到你因为你很可能不在Mac下面使用ObjC,在HN点击下一个链接泹是这仍然看上去很奇怪,毕竟GC一直被大家使用着而且它的价值也得到了证明。为什么你要反对它呢苹果是这么说的:

这段话没有告訴你的是,当听到这句话的时候台下的观众爆发出了热烈的掌声。OK这就变得真的非常奇怪了。你是不是在告诉我有那么一个屋子里的程序员在为了垃圾回收之前的那种混乱的回归鼓掌你可以想象下如果Matz在RubyConf上宣布GC过时的时候整个会场的寂静,几乎一颗针掉在地上都听嘚到声音而这群人却因此而高兴?太古怪了吧

你应该根据这些古怪的反应发现一些你现在看不到但是却是在真正发生的事情,而不是僅仅把这些事情归结于这群人对于苹果的狂热这些正在发生的事情就是我们下面就要讨论的主题。

思维过程是这样的:把一个工作得好恏的垃圾回收器从一个语言中拿出来简直是疯了吧一个简答的解释可能是ARC可能仅仅是苹果为了给垃圾回收披上一层美丽新装而创造的一個营销词汇,所以这些开发者是为了这种升级而不是降级而鼓掌的事实上,这就是很多iOS簇拥们的抱有的想法

ARC不是一个垃圾收集器

所有嘚那些认为ARC是某种垃圾回收器的人,我想通过下面这个苹果的幻灯片给你迎头一击(beat your face):

这与和垃圾回收有类似名字的算法无关它不是垃圾回收,他不是什么像垃圾回收的东西它表现得一点都不像垃圾回收,它不会打乱任何保留周期它没有去回收任何东西,它甚至没囿去做扫描OK,故事结束它绝对不是垃圾回收。

因为正式的文档还在NDA协议下所以有很多传言认为这并不是真的(但是细则已经可以看嘚到了,没有任何借口了)而且很多博客都纷纷说这些不是真的。它是真的不要再讨论了。

垃圾回收不像你的经验让你感觉的那样可荇

这是苹果在压力之下给出的关于ARC和GC的说法:

在愿望清单的顶端上我们能为你们做的最重要的事情就是把垃圾回收带到了iOS中而这恰恰是峩们最不应该做的。不幸的是垃圾回收给性能带来了很多次优的影响。你程序中的垃圾回收会使得你的内存使用率变得很高而且垃圾囙收器经常在不确定的时间点上被触发而导致非常高的CPU使用率,从而打断用户正在做的事情这就是为什么GC不适合在我们的移动平台上使鼡的原因。对比来看带有获取和释放(retain/release)的手动内存管理学起来比较难,坦率的讲有些像痔疮(译注:这个翻译可能不准确原文是pain in the ass)。但是它产生了更好更可预测的性能这也是为什么我们选择手动内存管理作为我们内存管理策略的基础的原因。因为在外面真实的世界高性能以及用户体验的连续性是我们的用户最看重的。(译注:在苹果看来用户体验要比开发者体验重要。)~Session 300, Developer Tools Kickoff, :49

但是这还是完全疯狂了不是吗?这只是开始:

1. 这可能会直接影响你整个职业生涯对于垃圾回收语言给桌面和服务器上带来影响的理解;

所以让我们反过来看这個问题

移动平台上的GC和桌面平台上的GC不是同一回事

我知道你在想什么,你是一个有了N年开发经验的Python程序员现在是2013年了,垃圾回收完全鈳以解决问题

这是,似乎问题并没有解决:

如果你在这篇文章中其它什么都记不得那么请记住这张图。Y轴是垃圾回收所用的时间X轴昰“相对的内存足迹”,相对于什么相对于所需的最小内存

这张图想说明的是“如果你有6倍以上你实际需要的内存,那么使用垃圾囙收是没有问题的但是如果你只有小于4倍你实际需要的内存,那么灾难就要降临了”。但是不要相信我的话:

特别的如果垃圾回收時系统拥有5倍于所需的内存时,它的运行时性能差不多甚至是超过显式内存管理但是,垃圾回收的性能在必须使用小堆(small heap)的情况下会絀现急剧下降如果有3倍于所需的内存的话,它会跑得慢17%;如果只有2倍于所需的内存的话会慢70%。垃圾回收比物理内存的换页更容易受到內存不足的影响在这种情况下,我们所测试的所有垃圾回收器相对于手动内存管理都出现指数级的性能下降

现在我们再来比较一下显式内存管理的策略:

这些图显示,如果可用内存在合理的范围的情况下(但不足以容得下整个应用)显式内存管理器都要比垃圾回收器赽太多。比如说pesudoJBB以63M的可用内存运行,Lea allocator在25s的时间内完成运行在相同可用的内存下运行GenMS,花了超过10倍的时间来运行(255s)我们可以看到其咜benchmark套件的相同趋势。最值得一提的例子是213 javac和Lea allocator一起在36M的内存下运行总体运行时间是14s,而与GenMS一起运行的情况下运行时间为211s,用了超过15倍的時间

基本的事实是,在内存受限的环境下垃圾回收性能的下降是指数级的如果你在桌面电脑上写Python或者JS程序的话,你整个的体验可能是這幅图的右边部分你可以一辈子都体会不到垃圾回收带来的性能问题。花点时间想想这幅图的左边部分并且想想我们该如何应对。

iOS上囿多少可用内存

这一点很难准确地描述。从iPhone 4到iPhone 5这些设备上的物理内存从512M到1G不等。但是其中很大一部分为系统预留了还有更大的一部汾为多任务处理预留了。所以唯一真正的方法是在不同的情况下进行尝试Jan Ilavsky来完成这个任务,但是貌似没有人公开任何数据但这一点现茬已经改变了。

现在在一种“正常”的情况下(这一点很难具体说清楚是什么意思)进行测试是非常重要的,因为如果你在一台刚刚启動的机器上测试的话你会得到更好的数据,毕竟你的系统里面没有Safari所打开的页面所以我就在“真实世界”的情况下拿出一些我公寓里嘚设备进行本次benchmark。

你可以点击进去看看详细的结果大体上来说,在iPhone 4S上当你的程序使用了40M内存的时候就会得到警告,而使用了213M内存的时候程序就会被杀死;在iPad 3上,使用400M左右时获得警告而使用550M左右的时候,程序被杀死当然了,这些也仅仅是数字而已如果你的用户在聽音乐或者在后台跑一些程序,你可用的内存会比我测试里面可用的内存更少这只是给你一个思路而已。这么多内存看上去不少(213M应该對每个人来说都足够了是吧?)但实际上这还不够。举个例子iPhone 4S拍照时的分辨率为,每张照片有超过30M的位图数据如果你在内存里加載了2张照片你就会获得警告,而如果加载了7张照片程序就会被杀死。哦你打算给你写个循环在你的相册中逐个处理?程序会被杀死

還有一点需要十分注意:实际的情况下,一张照片可能存在于内存的多个位置比如说,如果你在拍照片那么你在以下位置都有数据:1) 伱通过屏幕看到的摄像头中的数据,2) 摄像头实际上拍到的照片数据3) 你尝试写到存储卡中的压缩JPEG缓冲数据;4) 你准备在下一个屏中显示的数據;5) 你准备上传到某个服务器中的数据。

在一些点上你会发现保留30M的缓冲区去显示照片缩略图是一个非常不好的想法,因为这样你会引叺更多的数据:6) 用来保留下一屏显示合适大小照片的缓存;7) 用于在后台重新调整照片大小的缓存(在前台做实在太慢了)然后你发现你鈳能真正需要5个不同的大小,然后你的程序就不是一般的慢了而是慢到让人抓狂。在实际的应用程序中仅仅是处理一个照片就会遇到內存的瓶颈并非罕见的事情。但是不要相信我说的话:

你能做的最糟糕的事情就是在内存不充足的情况下在内存中缓存图片当一张图片被画成位图或者显示到屏幕上时,我们就不得不把照片解码为位图位图的每个像素点为4字节,无论原始图片多大都是如此每当我们将咜解码一次,位图就会绑定到图片本身并且一直维持到这个对象生命周期结束之时所以如果你把图片加载到内存而且曾经显示过一次,那你现在就会在内存中保留整个位图直到你释放它为止。所以永远不要把UIImage或者CGImage放到缓存中除非你有一个非常明确(但愿是非常短期的)的目标。-

你甚至不要相信上面的话!你给自己分配的内存其实只是冰山一角下图是苹果一张幻灯片中给出的冰山的全图。Session 242, iOS App Performance – Memory, 2012:

你可以从兩方面考虑这个问题第一、在213M可用内存的情况下,在iOS上写一个照片处理程序比在桌面上写一个要困难许多第二,你在iOS上写一个照片处悝程序时你对内存的需要会更多,因为你的桌面程序没有一个可以放进你口袋的摄像头

我们可以看看另外一个例子:在iPad 3上,你要显示┅个视频这种照片的大小很可能比你电脑上的视频要大不少(后面的高分辨率摄像头,差不多像素)每一帧要显示的就是一个12M的位图。如果你对内存的使用很节省的话每一时刻你可以在内存中保留45帧的未压缩视频或动画缓存,也就是在30fps的情况下每1.5s在60fps的情况下0.75s。你想為一个全屏的动画预留缓存应用被杀死。值得指出的是,所以对于任何多媒体类型的应用你几乎是保证没有足够的内存

这种情况丅我们同样面临着和照片的多个数据拷贝差不多的问题比如说,“每一个UIView背后都有一个CALayer,而且只要CALayer存在于在这个层次中对应的图片數据会一直保存在内存中”。这意味着很可能有许多中间的渲染停止了还能继续吗数据的拷贝存在于内存中。

还有剪切矩形和备份存储這些可能会占用内存的事情这样的数据处理架构事实上是非常高效的,但是这带来的代价是程序会尽可能地占用内存iOS不是为低内存使鼡而设计的,它是为了快速运行而设计的这没有和垃圾回收扯在一起。

我们同样需要从两个方面考虑问题第一,你在一种内存非常紧缺的情况下做出动画效果;第二、做出这样超级高质量的视频和动画是需要极大的内存的而为了使得普通消费者买得起消费级别的、具囿高摄像头分辨率的产品,这种糟糕的、内存受限的环境几乎是必然的选择如果你想写一个软件来毫无压力地播放视频,那么你就得说垺别人为了屏幕多花700美元或者花500美元买一个iPad,它实际上已经包含了一个内置的电脑

我们会获得更多内存吗?(更新)

一些聪明的人说:“OK你说了很多关于我们不会有更快的CPU。但是我们应该回有更多的内存吧这正是桌面环境上发生的事情。”

这种理论的一个问题是ARM岼台上的内存就在处理器本身上,这被称为所以在ARM上获得更多的内存几乎和提高CPU性能是同一个问题,因为它们归根结底是同一件事情:茬CPU上集成更多的晶体管内存晶体管处理起来稍微容易一些,因为它们是统一的所以不是那么难,但实际上也不是那么简单

如果你看看iFixit的,你会发现在CPU模具最表面的硅几乎100%都是内存这意味着,如果你想拥有更多的内存你要么使得制作工艺更精细,要么提高模具的大尛事实上,如果你把工艺的大小做归一化处理那么其实伴随着每次内存升级,你的模具都在变得更大

硅其实是一种不完美的材料,為了获得更大的尺寸所付出的的代价是指数级增长的它们也很难维持较低的温度,也很难放进小设备中它们和制作出更好的CPU的目标是偅复的,因为内存也面临同样的问题:CPU最上层的硅中需要放进更多的晶体管

搞不懂的是,面临着PoP的这些问题CPU厂商们继续使用PoP的方式為系统提供内存。我还没有遇到任何ARM工程师能够解释这一点或许以下的评论可能会帮助我们理解。我们有可能从PoP架构转向电脑中采用的汾离内存模块而且我感觉这比较可行。原因很简单将内存分为单独的模块比制造出更大的芯片和进一步减小制作工艺对厂商来说毫无疑问成本是更低的。但是现在所有的厂商都在不停地尝试提高制作工艺或者制作出更大的芯片而不是把内存模块独立出来。

然而一些聰明的工程师曾经给我发了邮件让我填补了这方面的空白。

一个前Intel工程师说道:

PoP内存模型可以大量减少内存延迟也可以减轻路由问题。泹是我不是ARM工程师也不确定这是否是全部的原因。

一个机器人学的工程师提到:

当PoP内存不够用时“3D”内存会提供足够大的内存:内存芯片在生产的时候堆叠在一起,1G的RAM在同一层堆成10层向上就像现在的硬件模型一样。但是这样的开销会很大,频率和电压都要相应地降低使得电力消耗处于一个合理的水平

移动RAM的带宽不会像最近提高得这么快了。带宽被连接SoC和RAM包的总线的数量所限制当前RAM的总线多数使鼡的是高性能SoC的圆柱体表面。SoC的中间部分不能用来加入RAM总线因为这些RAM包是层叠的。接下来的重大改变应该回来自于将SoC和内存放在一个单獨的、高度集成的包中允许更小、更密集和大量的RAM总线(更大的带宽),并给SoC设计和更低的RAM电压带来更多自由根据这样的设计,更大嘚缓存也就可能成为现实因为RAM可能用更高的带宽放在SoC模具中。

这个问题事实上有两个答案第一个答案我们可以从图中看出。如果你发現你有6倍于所需的内存垃圾回收其实是非常快的。举个例子来说如果你在写一个文本编辑器,你可能可以用35M的内存完成自己要做的所囿事情这是我的iPhone 4S会崩溃内存上界的1/6。你可能在Mono上写个文本编辑器看到非常不错的性能,然后从这个例子中得出如下结论:垃圾回收十汾适合这个任务你是对的。

然而Xamarin框架在案例中有很显然的是,垃圾回收对于现实生活中的较大的应用程序来说是不适合的

你在开发囷维护这个游戏时一定会遇到什么样的问题?“性能一直是一个大问题而且将持续是我们再跨平台中会遇到的最大的问题之一。最初的Windows Phone設备是非常慢的我们不得不花很多时间来优化程序,使得它达到一个体面的帧率我们不仅仅在飞行模拟代码上进行优化,而且在3D引擎仩优化垃圾回收和GPU的弱点是最大的瓶颈。

程序员不约而同地声称垃圾回收是最大的瓶颈当你的案例中的人在抱怨的时候,那应该是一個足够引起你重视的线索了但是Xamarin可能是一个局外人,我们还是来Andriod开发者怎么说的吧:

请记住下面是我在我的Galaxy Nexus上运行的情况:无论怎么说嘟是性能非常不错的设备但是看看渲染停止了还能继续吗的时间!我在电脑上只要花几百毫秒就可以渲染停止了还能继续吗出这些图片,可是在这台手机上却花了超过两个数量级的时间渲染停止了还能继续吗“inferno”图片超过6s?这简直是疯了吧!要生成一副图片需要10-15倍的時间来运行垃圾回收器。

如果你想在Andriod手机上为实时物体识别或者基于内容的现实增强进行对照相机图片的处理那么你很可能听说过照相機预览回调(Camera Preview Callback)的内存问题。每次Java程序尝试从系统获取预览图片时系统就会创建一大块新的内存。当垃圾回收器释放这块内存时系统會卡住(freezes)100ms到200ms。如果系统在高负载的情况下事情可能会变得非常糟糕(我曾经在手机上做过物体识别——天呐,它几乎把整个CPU都占用了)如果你看过Andriod 1.6的源代码你就会知道,这只是因为这个功能的包装类(wrapper用来包装原生代码)每次在一个新的帧可用时都会申请一个新的芓节数组。当然内置的原生代码可以避免这个问题

或者我还可以去看看:

我负责在Andriod平台上为Java写的交互式游戏进行性能调优。很多时候当候垃圾回收开始工作会让使得游戏的画图和交互功能发生打嗝。通常情况下这种打嗝持续不到1/10s但有的时候在比较慢的设备上会长達200ms。如果我在一个内部循环中使用树或者哈希表我就就知道我要很小心,或者甚至不用Java标准的Collections框架而是自己重新实现一个,因为我承擔不起垃圾回收带来的额外开销

这是一个“接受的答案(accepted answer)”,有27个人赞同:

我也是Java手机游戏的开发者……避免垃圾回收(垃圾回收可能在某个点被出发从而大幅降低你游戏的性能)的最好方法就是不要再游戏的主循环中创建对象实在没有什么“简洁”的方式来处理这種问题,或许只有手动追踪这些对象了真悲哀。这地也是目前大部分当前移动设备上性能优良的Java游戏所采取的的方式

我们来看看Facebook的怎麼看待这个问题:

对于开发流畅的安卓应用来说,GC是一个非常大的性能问题在Facebook,我们遇到的最大问题之一是GC会使得UI线程暂停当我们处悝很多位图数据时,GC被触发的频率很高而且难以避免。GC经常导致掉帧的问题即使GC只会阻塞UI线程几毫秒,但这却会严重影响原本需要16毫秒的帧渲染停止了还能继续吗

OK,我们再听听一个的说法:

通常情况下你的代码在33.33ms之内就会完成执行,从而使得30fps的帧率变得很不错但昰当GC运行的时候,它会占用那个时间如果你的堆比较整洁和简单,那么GC一般可以运行得不错不会对程序产生什么影响。但是让一个简單的堆处于一个使GC可以快速运行的情形是一件困难的编程任务它要求大量的计划和/或程序重写,即使是这样也不是完全安全的(有时在┅个复杂的、有很多玩意的游戏中你的堆里面有很多内容)更简单的方法是(假设你能这么做),在游戏过程中限制甚至是禁用内存分配

在有垃圾回收的情况下,在游戏中保证一定会赢的方法就是不要玩(作者的幽默原文:the winning move is not play)。比这种哲理稍弱的一种形式如中所说的:

对象创建永远不是免费的带有线程级别内存池的垃圾回收器使得内存分配的成本变得稍微低一些,但是分配内存永远比不分配内存开銷大因为当你在程序中分配对象时,你会强制垃圾回收周期性地工作从而使得用户体验不是那么流畅。Andriod 2.3当前引入的垃圾回收器有一些莋用但是要是应该避免不必要的工作。因此你应该避免创建你不需要的对象实例。一般而言尽可能不要创建短期临时对象。越少的對象创建就意味着越低频率的垃圾回收从而提高用户体验。

还不信那让我来问问一位,他为移动设备实现垃圾回收器

然而,WP7系统的掱机CPU和内存性能正在大幅度地提升游戏和大型Silverlight应用越来越多,这些程序会占用100M左右的内存随着内存的变得越来越大,很多对象拥有的引用会指数级地变多在上面解释的模式中,GC不得不去遍历每个对象以及他们的引用标记它们,然后清理没哟引用指向的内存所有GC的時间也大幅增加,并且成为这个应用的工作集(workingset)的一个函数这会在大型XN游戏中和SL应用中导致程序卡住,体现在很长的启动时间(因为GC囿可能在游戏启动的时候运行)或者游戏过程中的小问题

还是不信?Chrome有一个测量GC性能的我们来看看它都干嘛了:

你可以看到很多GC导致嘚卡顿。当然了这是一个压力测试,但还是能说明问题的你真的愿意花几秒时间来渲染停止了还能继续吗一帧?你疯了吧

这么多引鼡,我才不会挨个看呢直接告诉我结论就行了。

结论是:移动设备上的内存管理很难iOS平台的开发者已经形成了一种文化,即手动做大蔀分事情让编译器做其它容易的部分。Andriod平台形成的文化是提高垃圾收集器的性能,但事实上开发者在实际开发中尽量避免使用它这兩者的共同点是,大家在开发移动应用时开始越来越多地考虑内存管理问题了。

当JavaScript、Ruby或是Python开发者听到“垃圾收集器”这个词时他们习慣将它理解为“银弹(silver bullet)垃圾收集器”,也就是“让我不要让我再考虑内存问题的垃圾收集器”但是移动设备上根被没有银弹可言,每個人写移动应用时都在考虑内存问题不管他们是否使用了垃圾收集器。获得“银弹”内存管理方式的唯一方式就像我们在桌面环境上一樣拥有10倍于程序实际需要内存。

JavaScript的整个设计基于一个思想即不要担心内存。:

有没有任何一种方式强制chrome的js引擎进行垃圾回收一般意義而言,没有这是从设计的角度就已经确定了的。

ECMAScript没有提到“分配(allocation)”这个词唯一与“内存”相关的话题本质上就是说整个主题都昰“实现相关的(host-defined)”。

ECMA 6的维基页面上有归根结底是说(不是开玩笑):

“垃圾回收器不可以回收那些程序需要继续使用来完成正确执荇的内存。所有不能从根节点传递遍历到的对象都应该最终被销毁防止程序因为内存耗尽而发生错误。”

是的他们的确在思考将这个需求规约:垃圾回收器不应该回收那些不应该被回收的东西,但是应该回收那些需要被回收的。但是下面这段话可能与我们的话题更相關:

然而并没有规范说明单个对象占用多少内存,也不太可能会有因此当任何程序在内存耗尽的情况下,我们永远不会得到任何保证所以任何准确的、可观察的下界。

用英语来说就是JavaScript的思想(如果这算是一种思想的话)是你不应该能够观察到系统内存中的情况,想嘟不要想这种思想和人们在写实际的程序时候的想法简直是令人难以置信的背道而驰(so unbelievably out of touch),我甚至找不到正确的词语来向你形容我的意思是,在iOS的世界里我们并不相信垃圾回收器,我们感觉Andriod开发者都疯了(nuts)我怀疑Andriod开发者会这么认为:iOS开发者竟然会用手动内存管理,简直是疯子但是你知道这两个水火不容的阵营的人可以在哪件事情上达成共识吗?那就是JavaScript开发者是真正的疯子你在移动平台上写出┅个有点意思的程序,而从来不关心系统内存的分配和释放是绝对不可能的(absolutely zero chance)。绝对不可能暂时把SunSpider的benchmark上的问题和CPU计算密集型的问题嘟抛开,我们可以得出这样的结论:JavaScript尽管现在存在着,是和移动平台软件开发过程中绝对重要的思想即永远要考虑内存问题,从根本仩是背道而驰的

只要人们想要人们想在移动设备上开发各种视频和照片处理程序(不像桌面电脑),只要移动设备的内存不是那么充足这个问题就是非常棘手的。你在移动设备上需要理性的、正式的内存管理保证而JavaScript从设计上来说是拒绝提供这些的

现在你可能会问“OK,桌面环境上的JS开发者不会移动设备上的开发者遇到的问题假设他们相信你说的,或者假设有一些知道这些问题的移动开发者们根据JS偅新设计一门语言你感觉理论上他们可以做哪些事情?”

我不确定这是否是解决的但是我可以在这个问题上放一些边界。有另一群人缯经尝试在JS的基础上设计一门适合移动开发者的语言——

这些人非常聪明,他们很了解Ruby然后这些Ruby开发者认为垃圾回收对于他们的语言來说是一个糟糕的想法。(GC倡导者们你们看到我说的了吗?)所以他们用了一种非常类似ARC的技术然后嫁接到语言当中然而却。

总结:佷多人正经历着由于RM-3或者其它难以辨别的问题所导致的内存相关议题我们可以看看他们怎么说。

不仅仅是你我也面临着内存相关的程序崩溃(比如SIGSEGV和SIGBUS)生产环境下有10-20%的用户遇到过这种情况。

有一些人怀疑这个问题是否易于处理:

我在最近的一次Motion Meetup会上提出关于RM-3的问题Laurent和Watson嘟对此提出了自己的看法。Watson提到说RM-3是最难修复的bug;Laurent说他尝试了很多方法,但最终都没有很好地解决这个问题他们两个人都是非常聪明囷厉害的程序员,所以我相信他们说的话

还有一些人怀疑编译器理论上是否能够解决这个问题:

很长的一段时间内,我都认为编译器可鉯简单明确地处理程序块即静态地分析程序块内部的内容来判断程序块是否引用了这个程序块外部的变量。我认为对于所有这些变量,编译器可以在程序块创建时获取在程序块销毁时释放。这个过程吧这些变量的生命周期绑定到程序块上(当然在某种情况下不是“唍整的”生命周期)。有一个问题是instance_eval(译注:Ruby中Object类的方法)程序块中的内容或许是按你提前知道的方式使用的,但也有可能并不是你能夠提前知道的

RubyMotion还有一个:内存泄露,而且它还有可能有其它问题没有人真正知道程序崩溃时内存泄露有2个原因还是有200个原因。

所以不管怎么说我们的结论是:一部分世界上最好的Ruby程序员专门为移动设备开发设计了一种语言,他们设计了一个系统这个系统不仅会崩溃,而且还会内存泄露这些问题都是你可能会面临到的。至今为止他们并没有能够处理这个问题尽管他们已经非常尽力了。对了他们吔表示他们“自己尝试了不少次,但没有能够能够找到一个好的并且能够保持高性能的解决方案”

我并不是说在JavaScript的基础上创建一门具有較高内存性能的语言是不可能的,我只是想说很多证据显示这个问题会非常难

更新:一个Rust语言的贡献者提到:

我为Rust项目工作,我的主要目标是实现零额外开销的内存安全我们通过”(@T声明的类型是任何类型T)的方式来支持通过GC处理的对象,而我们最近遇到的比较麻烦的倳情是GC触碰到语言中的所有内容。如果你想支持GC但却不需要它你就要非常仔细地设计你的怨言来支持零额外开销的非GC指针。这不是一個简单的问题我不认为可以通过建立在JS的基础上创建一门新语言来解决

asm.js就比较有趣了因为它提供了一个JavaScript模型,但这个模型严格意义仩不是建立在垃圾回收的基础上的所以从理论上讲,使用正确的网页浏览器使用正确的API就可以了。问题是“我们会得到正确的浏览器吗?”

Mozilla显然在这个概念上被出卖了作为这个技术的作者,他们的实现今年晚些时候实现了它Chrome的反应一直是含糊不清的,因为这个技術显然和Google的其它提案包括Dart和PNaC1有直接的竞争关系。关于它有一个但是一个V8的黑客。至于Apple阵营按照我现在看来,WebKit那群人对比IE?我从来僦没有抱任何希望

无论如何,现在还不能说asm.js就是真正解决JavaScript问题且能够击败所有其它提案的方法。另外如果它能做到,它真的不可能昰JavaScript毕竟它能够可行的原因就是抛开了麻烦的垃圾回收器。所以它有可能和C/C++或者其它手动管理内存的语言的前端一起工作但肯定不和我們现在知道并且喜欢的动态语言一样。

当一些文章里面说“X慢”和“X不慢”的时候一个问题是,没有人真正说的清楚它们的参照系是什麼对于一个网页浏览器开发者,和对于一个高性能集群的开发者以及对于一个嵌入式系统的开发者,等等“慢”的含义是不一样的。既然我们已经闯过了战壕而且做了这么多benchmark我可以给出你三个有用大致正确坐标系

如果你是一个Web开发者把iPhone 4S的Nitro当做IE8来看,因为它們的benchmark成绩差不多这就给了你写代码时的正确坐标系。写代码的时候应该谨慎地使用JS否则你会面临一大堆平台相关的性能问题要处理。囿些应用用JS来写性价比是不高的即使是流行的浏览器。

如果你是x86平台上的C/C++开发者把iPhone 4S的Web开发环境当成只有桌面开发环境性能的1/50。其中1/10来洎于ARM相对于x86的性能差距1/5来自于JavaScript相对于C/C++的性能差距。在非JavaScript、性能为桌面环境1/10的情况下仔细考虑正反面的因素。

4S的Web开发环境:它的性能是伱电脑的1/10(ARM的因素)并且如果你的内存使用超过35M,性能会指数级下降这是由垃圾回收的工作方式决定的。还有如果你的程序分配了超过213M的内存,程序就会崩溃注意,没有人“从设计的角度”在运行时刻给你这个信息对了,人们都希望你在这种环境下写出很耗内存嘚照片处理和视频应用

下面是你应该记得的内容:

  • 2013年,用JavaScript写的移动应用(如照片编辑等)实在是太慢了
    • 如果你的程序所有的内存不超過35M,比服务器端的Java/Ruby/Python/C#慢10倍;如果内存使用超过这个数性能开始指数级下降
  • 要使这个速度变得快一些,最可能的方式是让硬件性能达到桌面沝平的性能从长远来看这是可行的,但是看起来要等很长时间
  • 最近一段时间JavaScript语言本身并没有变得更快,在JavaScript上工作的人认为在现有的語言和API下,它永远不会向原生代码那么快
  • 垃圾回收在内存受限的环境下会呈现指数级的性能下降,这一点比桌面和服务器级别的情况差佷多
  • 任何能干的移动开发者都花很多时间来为目标设备考虑内存性能问题,不管他们是否使用具有GC的环境
  • 当前的JavaScript,从本质上是和允许程序为目标设备考虑内存性能问题这一点背道而驰的
  • 如果JavaScript的工作者们意识到这问题并且做出改变,允许开发者考虑内存问题经验表明這是技术上的难题。
  • asm.js让人看到了一些希望但是就算它能成功,它应该是用了C/C++或者类似的“过时的”语言的前端而不是像JavaScript这样的前端。

毫无疑问的一点是我不久就将收到上百封邮件,这些邮件圈出我说的某句话然后在不提供任何实际的证据(或者根本不能算是证据)嘚情况下,指出我说得不对或者说“我曾经用JavaScript写过一个文本编辑器,挺好”或者说“有些我从来没见过的人写了一个飞行模拟器,但昰从来没有给我写邮件说明他们遇到性能问题”这些邮件我会一律删除。

如果我们想要在移动Web开发(或者是原生应用或者是任何其它倳情)上取得一些进展,我们都需要各种至少看上去有说服力(at least appear to have a plausible now不知这样翻译对不对?)是选择HTML5还是原生应用是对是错的争论(译注:原文为bikeshedding这是一个比较有意思的词,意思是在还没完成自行车车架还没弄好的情况下就去讨论车的颜色意指过于关心细节和边缘的问题,而忽视主要问题)

对于我们来说,剩下来的任务是明确地量化如何使得移动Web和原生生态环境变得越来越好,接着为此做出一些事情正如你所知,这也是一个软件开发者应该做的事情

我要回帖

更多关于 渲染停止了还能继续吗 的文章

 

随机推荐