引言

大量的用户每天在Android设备上使用Facebook,滚动新闻Feed流页面,包括个人资料,活动,页面和组,与他们关心的人员和信息进行互动等一系列行为。 所有这些不同的Feed类型都由Android Feed Platform小组创建的平台提供支持,因此我们对Feed平台进行的任何优化都可能提高我们的应用程序的性能。 我们专注于页面的滚动性能,因为我们希望用户在滚动他们的Feed流页面时有一个平滑的体验。

为了帮助我们实现这一点,我们有几种自动化工具,可以跨不同的场景和不同的设备在Feed平台上运行性能测试,测量代码在运行时内存使用,帧速率等方面的运行情况。 其中一个工具Traceview显示了我们的程序对Long.valueOf()函数的调用次数相对较多,这导致对象在内存中累积并导致应用程序卡顿停止等。 这篇文章描述这个问题,我们权衡了各种潜在解决方案之后,对改进Feed流平台而进行的一系列优化。

便利性带来的缺点

我们从Traceview的一个方法分析报告中注意到:facebook的app对Long.valueOf()函数的大量调用。之后,我们进行了又进一步的测试,证实了当我们滚动新闻列表时,Long.valueOf()方法的调用会意外升高。

当我们查看堆栈时,我们发现这个函数没有被直接的从Facebook的代码调用,而是隐式地由编译器插入的代码。 在分配长整型对象的原始长值时调用此函数。 Java支持对象和原始的简单类型(例如,整数,字符),并提供了一种在它们之间无缝转换的方式。 这种方式称为自动装箱,因为它将基本类型装箱为相应的类型的对象类型。 虽然这是一个方便的开发功能,但是它同时也创建了开发人员不知道的新对象。

在对一个示例应用程序的堆栈中发现Long对象有大量的存在; 虽然每个对象本身都不大,但是存在的大量的Long对象占据了应用程序在堆中的大部分内存。 对于运行Dalvik的设备来说,会有很大的影响。 与Android的ART运行时环境不同,Dalvik没有一代间垃圾回收机制,造成很多小对象的垃圾回收效率很低。 当我们滚动新闻Feed流,会造成Long对象数量增加,垃圾收集将导致应用程序卡顿来从内存中清除未使用的对象。 积累的对象越多,垃圾收集器将越来越频繁地暂停应用程序,导致卡顿使得户体验不佳。

幸运的是,Traceview和Allocation Tracker等工具可以帮助我们找到这些函数调用的位置。 在查看了这些自动装箱事件的根源之后,我们发现大多数成因都是:将Long类型的基本类型数据插入HashSet 数据结构中造成。 (我们使用这个数据结构存储新闻Feed的哈希值,稍后检查某个哈希是否已经在Set中。)HashSet提供对具体feed的快速访问。 由于哈希计算并存储在一个原始的长变量中,然而我们的HashSet仅适用于对象,所以当调用set.put(Hash)时,我们会得到不可避免的自动装箱。

作为一个解决方案,可以使用基本数据类型而不是对象类型的Set实现,但是结果并不像我们预期的那么简单。

目前的解决方案

有几个现有的Java库为原始数据类型提供了Set实现。 几乎所有这些类库都是10多年前创建的,当时在移动设备上运行的唯一的Java是J2ME。为了确定可行性,我们需要在Dalvik / ART下进行测试,并确保它们在资源更受限的移动设备上表现良好。 我们创建了一个小型测试框架来帮助将这些库与现有的HashSet进行比较。 结果表明,这些库中的一些库具有比HashSet更快的运行时间,并且具有较少的Long对象,但是它们仍然在内部分配了很多Long对象。 例如,Troow库中的一部分TLongHashSet在测试时分配了大约2 MB的对象,共有1,000个item

对其他的类库进行测试,包括PCJ和Colt, 显示了类似的结果。

现有的解决方案不符合我们的需求。 我们考虑是否可以创建一个新的Set实现,并针对Android进行优化。 在Java的HashSet中,使用单个HashMap来实现一个相对简单的实现。

public class HashSet<E> extends AbstractSet<E> implements Set<E>, ... {transient HashMap<E, HashSet<E>> backingMap;    ...@Override public boolean add(E object) {return backingMap.put(object, this) == null;    }@Override public boolean contains(Object object) {return backingMap.containsKey(object);    }...
}

向HashSet添加新item意味着将其添加到内部HashMap,其中对象是关键字,而HashSet的实例是该值。 要检查对象成员身份,HashSet将检查其内部HashMap是否包含对象作为键。 可以使用Android优化的map和相同的原则来实现HashSet的替代方案。

引进LongArraySet

你可能已经熟悉了LongSparseArray,它是Android支持类库中的一个类,用作使用long类型作为key的map。 使用示例

LongSparseArray<String> longSparseArray = new LongSparseArray<>();
longSparseArray.put(3L, "Data");
String data = longSparseArray.get(3L); // the value of data is "Data"

LongSparseArray的工作方式与HashMap不同。 当调用mapHashmap.get(KEY5)时,下图说明了如何在HashMap中找到该值:

当使用HashMap上的键检索值时,它使用密钥的哈希值作为索引访问数组中的值,即O(1)时间复杂度的的直接访问。 对LongSparseArray进行相同的调用如下所示:

LongSparseArray使用二分搜索,运行时间为O(log N)的时间复杂度操作搜索排序密钥数组的密钥值。 数组中的键的索引值用于查找values数组中的值。

HashMap分配一个大数组,以避免hash冲突,但是这样导致搜索速度较慢。 LongSparseArray分配两个小数组,使其内存占用更小。 但是为了支持其搜索算法,LongSparseArray需要在连续的内存块中分配其内部数组。 添加更多的item将需要在当前空间不足的情况下分配新的数组。 LongSparseArray的工作原理使得它在保存超过1,000个项目时效率下降,这些差异对性能有更重要的影响。 (您可以在官方文档中了解有关LongSparseArray的更多信息,并通过观看Google的简短视频。)

由于LongSparseArray的键是原始long类型,所以我们可以使用与HashSet相同的方法创建一个数据结构,使用LongSparseArray作为内部映射而不是HashMap。

建立LongArraySet

新的数据结构更加合理。通过使用与之前相同的测试框架,我们将新的数据结构与HashSet进行了比较。 每个数据结构都通过添加X个item进行测试,检查每个item的存在,然后删除所有item。 我们使用不同数量的item(X = 10,X = 100,X = 1,000 …)运行测试,并平均每个item完成每个操作所花费的时间。

运行时结果(时间显示为纳秒):


我们看到使用新数据结构的contains和delete方法的运行时效率改进。 另外,随着数组中item数的增加,添加新item花费更多时间。 这与我们已经知道的LongSparseArray是一致的 ,当item数量超过1,000时,它与HashMap的表现不一样。 在我们的用例中,我们只处理了数百个item,所以这是一个我们愿意做的权衡。

我们也看到了内存使用有很大的改善。 在查看堆转储和分配跟踪报告时,我们注意到对象分配的减少。 下面是当添加1,000个item进行20次迭代时,HashSet和LongArraySet实现的并行分配报告:

除了避免所有Long对象分配之外,LongSparseArray更具有内存效率,在这种情况下的分配减少了约30%。

结论

通过了解其他数据结构如何工作,我们能够为我们的需求创建一个更优化的数据结构。 垃圾收集器必须工作的越少,这样丢帧的可能性就越低。 使用新的LongArraySet类和类似的IntArraySet作为原始int数据类型,我们能够在整个应用程序中减少大量的对象内存分配。

这个案例研究表明了我们选择数据结构的重要性。虽然这种解决方案对于所有用例来说并不完美,因为这种实现对于非常大的数据集来说较慢,但是还可以继续对我们的代码进行优化。

你可以在下面网址找到两个数据结构的源代码。 我们很高兴继续努力应对挑战,优化我们的Feed平台,并与社区分享我们的解决方案。

https://code.facebook.com/posts/973222319439596

Facebook安卓Feed流的内存优化实践相关推荐

  1. 朋友圈微博feed流,推拉实践

    上一篇<feed流拉取,读扩散,究竟是啥?>关于feed流的拉取还是推送,只写了一半"拉取",今天把另一半"推送"(写扩散)的坑填完. 为了对比&q ...

  2. 微信 Android 终端内存优化实践

    前言 内存问题是软件领域的经典问题,平时藏得很深,在出现问题之前没太多征兆.而一旦爆发问题,问题来源的多样.不易重现.现场信息少.难以定位等困难,就会让人头疼不已. 微信在过去 N 多的版本迭代中,经 ...

  3. Android 内存优化实践与总结

    本文为腾讯 Bugly 公众号投稿,版权归原作者所有,未经作者同意,请勿转载. 原文地址:https://mp.weixin.qq.com/s/2MsEAR9pQfMr1Sfs7cPdWQ [CSDN ...

  4. 百度APP iOS端内存优化实践-大块内存监控方案

    ‍ 01 背景 ‍内存不足引发的APP崩溃通常称为OOM(Out Of Memory),iOS端无法捕获OOM异常,也得不到任何堆栈信息,给我们排查和解决问题带来很多困扰.引起OOM的原因归根结底就是 ...

  5. App内存优化-实践

    多进程的优点 系统为每个应用分配一定大小的内存,从之前的 16M 到 32M.48M,甚至更高.但毕竟有限. 进程是资源分配的基本单位.也就是说,一个应用有对个进程,那这个应用可以获得更多的内存. 所 ...

  6. 「性能优化系列」APP内存优化理论与实践

    当一个应用同时运行越来越多的任务以及复杂的业务,Android系统的内存管理机制已经无法满足内存的释放与回收,为了应用的稳定性与性能,去控制内存的创建和回收就成为了一个重要的命题. 本篇文章主要涉及内 ...

  7. 华为云PB级数据库GaussDB(for Redis)揭秘第六期:Feed流场景中的应用

    本文分享自华为云社区<华为云PB级数据库GaussDB(for Redis)揭秘第六期:Feed流场景中的应用>,原文作者:高斯Redis官方博客. 一.背景 GaussDB(for Re ...

  8. 深入探索Android内存优化

    前言 成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~. 本篇是Android内存优化的进阶篇,难度会比较大,建议对内存优化不是非常熟悉的前仔细看看在 ...

  9. 内存优化 · 基础论 · 初识 Android 内存优化

    [小木箱成长营]内存优化系列文章: 内存优化 · 工具论 · 常见的 Android 内存优化工具和框架 内存优化 · 方法论 · 揭开内存优化神秘面纱 内存优化 · 实战论 · 内存优化实践与应用 ...

  10. 深入探索 Android 内存优化(炼狱级别)

    前言 成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~. 本篇是 Android 内存优化的进阶篇,难度可以说达到了炼狱级别,建议对内存优化不是非常熟 ...

最新文章

  1. idea terminal中文乱码_Terminal优雅的办公带来超高的效率
  2. outlook2010客户端无法预览及保存word,excel问题
  3. Linux中断不能进行任务调度,关中断是否禁止任务调度?关中断能作为互斥吗?...
  4. 32位存储环境下整数范围为什么是[-2^31,2^31-1]?
  5. 从地理围栏看物联网安防
  6. Clojure的引用类型:var,ref,agent和atom
  7. Docker下安装Anaconda
  8. JavaScript从父页面获取子页面的值(子页面又如何访问父页面)
  9. 解决SqlServer添加维护计划时出现“代理XP组件已作为此服务器安全配置的一部分被关闭。系统管理员可以使用……”
  10. 2、Linux多线程,线程的分离与结合
  11. 如何彻底关闭FF新推荐弹出广告
  12. JAVA 连接SAP Jco3
  13. python列表元组字典
  14. php获取qq空间,使用php批量抓取QQ空间相册链接
  15. 俄罗斯方块c语言程序方案设计,c语言俄罗斯方块游戏程序方案设计书报告.doc
  16. Endnote: 如何区分中英文期刊
  17. NXP LPC1768最小系统板硬件介绍
  18. tecplot选择变量
  19. iphone充电图_哪些iPhone具有无线充电功能?
  20. 测者的测试技术笔记:Screenplay 模式(Journey 模式)

热门文章

  1. [转]显卡帝揭秘3D游戏画质特效
  2. Activity设置竖屏显示
  3. 晶振波形不是正弦波_求助各位!有源晶振出来的波形是方波还是正弦波?
  4. 2021认证杯 第二阶段 思路加代码
  5. 虚拟服务器有没有加入bt端口,bt端口映射怎么做?
  6. 史上最详细教你制作“U盘启动盘”重装Windows10系统
  7. android 死亡阴影,英雄无敌3死亡阴影
  8. VirtualBox 安装 增强功能
  9. Memory Limited Persistent Message Queue
  10. 在大学里我们应该学习什么