范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

支付宝客户端OOM专项优化实践(安卓篇)

  一、背景:
  作为一名Android程序开发者,在日常的代码开发工作中可能不会过多关注自己业务对内存的使用是否合理,通常情况下Android系统的内存管理机制帮我们处理好了内存的分配和回收。然而在一些异常场景下不合理的内存占用不仅会使用户应用程序运行卡顿、ANR、黑屏,在极端情况下还会发生 OOM(Out Of Memory)崩溃,极大影响用户体验造成用户流失。通常我们在对待线上的的Out of Memory的闪退都会被当做小概率事件没有被有效重视和系统分析,本文将结合支付宝线上OOM真实案例详细的介绍下常见OOM问题的根因分析和优化实践。
  在支付宝应用架构的不断演进中用户支付场景已经是最基础的使用场景,随着小程序生态的引入森林庄园等复杂h5/小程序的使用比例不断增长,同时理财、本地生活等重度业务也运行在支付宝这艘航母上,并且在每年的大促活动像双11、618、新春五福活动上线后让本就不富裕的内存空间又雪上加霜,在2020年之前支付宝的内存问题一直未被重点关注,从一组数据可以看到在过去的19-20财年中,客户端OOM闪退率暴涨了近一倍,占整体客户端闪退的45%以上,内存水位则涨幅在300M左右,按照这个趋势20年底的OOM闪退率会涨幅50%。那到底是什么原因导致的支付宝OOM问题如此严重呢?这里先抛出几个问题: 线上低端机的OOM问题是否真的比高端机更严重呢? 线上的OOM真的全是由于Java内存泄漏导致的堆内存紧张导致的吗? 线上OOM有没有可能在App堆内存宽裕,设备物理内存也宽裕的情况下发生? 二、OOM问题根因分析2.1 OOM分类
  通过以下数据我们可以看到在支付宝32位包的OOM闪退中9-11系统的占比远远超过6.x以下机器,并且在对闪退时内存统计中发现5.x以上机器闪退时端上整体内存值还有大量剩余,而虚拟内存空间几乎达到了2^32=4GB。
  众所周知32位应用程序的虚拟内存寻址上限只有 2^32=4GB,无论高端机还是低端机只要虚拟内存寻址达到这个上限就会触发系统OOM闪退,通常在logcat日志中会看到"OutOfMemoryError: pthread_create (1040KB stack) failed: Try again", "libc++abi: terminating with uncaught exception of type St9bad_alloc: std::bad_alloc","Could not mmap handle 0x67ae34d0 fd=652 (Out of memory)"等。因此按照不同的成因我们总结了OOM问题分类,只有对线上OOM问题清晰的认识和分类才能更好的感知问题变化和有针对性的治理
  2.2 虚拟内存地址指标量化
  下面将会从虚拟内存结构入手详细地讲解一下导致虚拟内存寻址空间不足的原因,从下图内存结构看出去系统占据的1G外剩下3GB内存都是如何分配?通常我们在写 Native 代码的时候,并不会直接调用内核的 API 去申请物理内存,而是使用 malloc 族的函数进行内存申请,这时候返回的指针是指向虚拟内存中的地址空间,之后在这部分地址空间真的被使用的时候,才会发生缺页中断触发真实的物理内存分配,所以通常是两层分配结构,用户态的代码申请的内存来自于内存分配器的二次分配,常见的内存分配器有 JeMalloc、TcMalloc、PtMalloc 等等。这里也解答了上面的问题,手机物理内存或javaheap内存只使用不到50%的情况下,虚拟内存可能已经打满同样会触发OOM闪退。
  三、线上监控&问题感知
  那么虚拟内存的使用应该如何度量呢?简单的方式是直接从 /proc/{pid}/status文件读取vmsize数据,但如果要做到能感知线上内存水位变化并快速定位问题还需要更细粒度的分类。 如上图所示,通过对/proc/{pid}/maps地址文件的内存分配标记进行分类可量化成20+个细粒度内存指标,在支付宝启动后的内存空间分析中可以看到在支付宝启动后留给业务的内存空间大概在1-2G左右,如果没有有效的内存监控释放手段,随着应用使用时长的增加,内存地址空间会被迅速耗尽。下面会介绍线上主要的几个内存监控方式。 3.1 支付宝全局虚拟内存水位监控
  基于虚拟内存的水位监控主要作用是感知活动或者开关变更导致的内存上涨,同时能确定业务维度的内存水位变化,通过制定告警阈值,让线上问题定位达到分钟级别。 3.1.1 典型案例:首页氛围图和腰封 Lottie 动画
  大促活动期间首页氛围图或腰封上线lottie动图引起的Libcmalloc上涨约涨20-50M,导致OOM闪退率上涨30%,相比业务上线活动首页氛围和腰封的变更影响是全局的基础水位,对OOM闪退率影响较大。下图为某次活动0点发布氛围后支付宝整体内存水位上涨40M。针对这一问题我们对所有lottie上线资源做了动态的内存检测,超过阈值则被禁止上线,同时通过线下的内存检测结果可预估lottie上线后对线上闪退率的影响
  0点活动上线内存水位上涨
  线上闪退率预估3.1.2 典型案例:大促活动
  随着渲染技术迭代新春五福,双11活动的页面动画特效复杂度越来越高,已经不再局限于2D动画,并且活动页会为支付宝内其他业务引流,当用户不断跳转页面访问时内存可能不会及时释放,导致短时间的内存叠加效应引起内存水位升高同时OOM闪退率上涨。针对这一问题,支付宝活动都会针对渲染特效设计几种降级方式,在内存水位变化幅度超过阈值或OOM闪退率上涨情况下可以对业务进行统一降级,动画->静态图,3D->2D等
  3.1.3 典型案例:Dexpatch
  Dexpatch发布引起的端整体Public-File水位上涨50M左右,由于Dexpatch本身的实现方式要在native解包和替换,导致mmap了大量内存,目前在32位包已经不再使用Dexpatch热修复。下图是线下测试结果:
  3.1.4 典型案例:uc内核解压
  uc压缩内核推动动态bundle,通过用户日志可以发现so解压后出现错误,导致用户重复进行初始化进而导致Public-Files内存上涨后触发OOM闪退。在推送回滚后,整体崩溃率下降。3.1.5 典型案例:蚂蚁庄园
  蚂蚁庄园3周年活动,生活号文章插入超大gif动图导致Gpu增幅超过300M引起线上OOM闪退告警,暴露了目前对于h5/小程序中的大图、视频和动图没有提前发现的能力。基于这个问题在对业务准入的时候增加图片视频的静态尺寸检测和动态内存检测,避免类似问题再次发生。
  小程序/H5中资源与内存使用关系梳理:高清大图内存主要分配在gpu上,随便下载了一些文章详情的图片发现大小都在1MB以内,这是因为图片都是有压缩的,但是在手机中图片显示是完全解压状态,正常情况图片的内存计算方式:png = 像素总数(图片宽×高) × 4 Byte per pixel × 1~3(渐进式、缩放缓存、纹理尺寸、mipmap限制等因子影响), 因此高清大图应控制分辨率,按需使用适当尺寸的图片,禁止超高分辨率图片gif图片内存主要分配在gpu上,由于gif采用多帧轮播的方式播放,gif所有帧内存都会加载到gpu中且无法释放,其内存计算方式是:GIF = 像素总数(图片宽×高) × 4 Byte per pixel × 总帧数 × 1~2(纹理尺寸、mipmap限制等因子影响) 典型案例:分辨率690*4746,帧数21 ,文件大小346KB直接占用GPU 210MB,该图仅最上面是动图,而全图使用gif格式并且以超长图展示,线下测试手机需要至少10s才能加载出该图。应该严格限制此类情况发生
  3.2 运行时业务维度内存异常监控
  在监控内存水位的同时,我们还要掌握每个业务生命周期内分配的内存空间是否合理,目前做法是通过开关配置一组内存指标异常值,当业务单次采样间隔内存分配超过阈值时上报相关数据,概数据帮助我们在线下推动业务对内存使用优化,同时配合线上告警机制保证业务内存使用变化时能快速感知到。 典型案例:
  线上监控到某选图打印的小程序单次的Libcmalloc增幅超过300M,导致该应用OOM闪退率是正常业务的20倍,主要原因有2点:用户在使用小程序时会选择一张手机图片,在多媒体选图组件做图片预览和大图展示时为了加快图片展示提前加载了选图前后2张,由于图片加载内存全部分配在Native导致Libcmalloc内存大幅上涨,个别用户涨幅超过1G多媒体组件没有监听小程序退出事件,因此没有及时做内存回收导致缓存的图片数据长时间在内存中保存
  3.3 闪退时刻的端上运行环境数据记录在内存问题排查中至关重要
  支付宝微镜平台提供了闪退issue的查看和分析能力,我们只需在客户端闪退时按照一定格式将端上运行时数据记录到埋点即可在平台查看到聚合后的数据临近闪退时的内存snapshot及闪退时刻内存数据记录,方便排查闪退前的内存变化趋势和闪退时内存数据聚合分析 部分maps原始地址数据,隐藏了内存中异常信息 完整的虚拟地址解析后结构,如下图 Moudles   Moudles-Ashmem   Moudles-JavaVM   Moudles-Native-Anonymous   Moudles-Native-Bss   Moudles-Native-LibcMalloc   Moudles-Native-Other   Moudles-Other Public   Public-Devices   Public-Files   Public-Gpu   Public-Threads U4Core   U4Core-BlinkGC   U4Core-PartitionAlloc   U4Core-SharedMemory   U4Core-UCMalloc   U4Core-V8 Heap典型案例:MIUI Android 10 系统字体重复占用虚拟内存,导致支付宝 App 可用的虚拟内存变少,更容易发生崩溃(发生OOM)。支付宝在20年的双十一活动的 OOM 中,小米+红米机型占比 43%,高于小米手机的市占率某外卖小程序在使用v8 worker的时候调用message port发给render进程大量消息并使用了sharedmemory导致闪退率上涨
  四、内存诊断&治理4.1 Native内存问题治理
  支付宝是一个汇集了扫码,端智能,AR,小程序容器,音视频播放等 native 代码非常多的大型应用,据不完全统计有100个so在支付宝内运行,每个 native 代码相关的模块背后都有一个专业团队在高速迭代,Java 堆内存因为有比较成熟的工具和方法论,加上 hprof 快照作为补充,定位和治理都很方便,native 内存问题一直缺乏高效稳定的工具导致问题治理难度非常大。事实上,单纯的native内存泄漏问题相对较少,更多的是因为业务逻辑不合理带来的内存使用问题,需要工具渗透到 App 运行的过程中进行监控,对工具性能和稳定性提出更高的要求。
  因此对于支付宝的 native 内存泄漏监控工具的诉求主要有以下几个方面: 接入成本:无需额外适配Android 版本,无需 root 稳定性:几乎不影响业务的稳定性问题,可以满足灰度版本开启即可 性能层面:对业务运行和启动都没有明显劣化影响,无卡顿和ANR问题 监控范围:不局限于 malloc/calloc/realloc/memalign/free,至少还能覆盖 mmap/mmap64/munmap 支撑广度:内存监控,内存泄漏,野指针检测等 1. 工具调研
  维度产品
  SoAOP(支付宝)
  malloc debug
  AddressSanitizer
  memhook(UC)
  Native Finder(手淘)
  是否需要root
  不需要
  需要
  需要
  需要
  否
  功能支撑广度
  内存泄漏、So内存值监控、堆破坏、野指针
  内存泄漏、堆破坏、野指针
  内存泄漏、堆破坏、野指针、缓冲区溢出
  内存泄漏检测
  内存泄漏、堆破坏、野指针
  堆栈能力
  有
  有
  有
  有
  有
  性能
  好
  差
  好
  较好
  好
  接入成本
  低
  高
  高
  高
  较高2. 技术方案
  支付宝的 native 内存泄漏监控工具主要包含三部分:代理实现、栈回溯和缓存管理,其中代理实现是对 malloc/calloc/realloc/memalign/free和 mmap/mmap64/munmap等函数的hook,决定了hook的准确程度至关重要,这里我们选择了比较成熟的 PLT hook 工具代表  xHook,该方案已经在业内多个内存线上方案中使用, 其稳定性可以达到上线标准。在栈回溯上使用了性能更好的libudf方案兼具了性能要求和回溯成功率,为了提高线上的使用场景,我们在使用时通过开关限制栈回溯频次
  方案细节: 首先客户端在启动后会初始化注入hook相关的服务。通过轮询扫描proc/self/maps获取支付宝包内的实时so数据,并添加so管理。hook的malloc/free/mmap/munmap/mmap64/pthread_exit/calloc/realloc监听方法回调,根据内存指针通过malloc_usable_size方法获取对应的内存和so数据,进行统计内存。
  线下方案:
  目前线下方案通过动态bundle的方式可以扫码实时加载到支付宝客户端中,通过悬浮窗的方式展示内存数据和栈回溯信息,同时也支持接口和Logcat日志方式查看数据
  3. 典型案例搜索引擎使用文件缓存聊天词汇提高搜索效率,在灰度版本中出现较多的索引文件导致虚拟内存异常升高,其中(/lib/arm/libclient_searcher.so  malloc= 103M) + (/files/search/alipay_search/allin/idx_0  mmap = 188M)= 291M内存使用
  可以从maps的地址空间看到多个mmap的文件信息
  cube上层业务社交商家群页面如果快速进出页面未及时调用释放接口导致cube native内存泄漏,cube业务本身缺少运行时的内存监控,对上游业务方的使用充分信任,导致这个灰度版本问题,在业务优化后修复
  4.2 FD内存问题治理技术方案
  __FD_SET_chk 问题大多出现在 Android 7.1.2 及之前版本原因是FD 总数超过 1024 个导致,如下图所示大多在闪退堆栈中能看到__FD_SET_chk 标识。
  线上的FD的主要监控方式是在FD总数超过900后通过/proc/{pid}/fd采集所有fd数据进行上报,并对FD名称进行聚合,以issue的形式在微镜平台展示,这样在某一类FD异常pv变化时就能找到相关变更点
  线下通过shell脚本方式可以进行测试# 注:如遇到 command not found,需要使用 homebrew 安装相应命令 export PKG=com.eg.android.AlipayGphone  function apid() {     echo $(adb shell pidof $PKG) }  # Mac 上 watch 命令需要使用 homebrew 安装(brew install watch) watch -n0.2 -d "adb shell run-as $PKG ls -l /proc/$(apid)/fd/ | cut -d" " -f10 | sed -E "s/[0-9a-fA-F]{4,}/xxx/g" | sort | uniq -c | sort -nr | head -10"
  典型案例Gpu独立进程使用的 Surface 对象未主动释放,在部分手机出现 FD泄漏导致闪退率上涨问题描述:
  主进程 anon_inode:sync_file FD 泄漏的 pv 从 1 月 24 日 9k 上涨到 2 月 3 日 50.7 万,并且持续未恢复,sync_fence 和 sync_fence_mali_fence 的issue趋势相同。问题定位:使用 NDK 提供的 API sync_file_info/sync_get_fence_info 发现了关键字 :gpu_process,SurfaceTexture。线下测试关闭 GPU 独立进程后(开关 h5_uc_gpu_process_mode=0)不再出现泄漏issue 上涨趋势大致与开启 GPU 独立进程的节奏同步根因分析:
  Gpu 进程使用的 Surface 对象,其内部的 SurfaceTexture 来自 TextureView,很多手机的实现都需要主动执行 Surface.release() 才能释放这个 SurfaceTexture 资源(待 Surface 对象被 gc finalize 也会释放),原生实现未执行主动 release 导致 SurfaceTexture 泄漏,其 native 实现有申请 sync_file 等对象,导致 fd 泄漏。
  4.3 CloseGuard资源泄露检测CloseGuard 介绍
  系统代码预埋了一些检测点,检测需要手动释放却未释放的资源。分配资源时获取调用栈,并记录对应的关闭方法的名称,GC 回收时在finalize() 方法里检测是否释放过,没有释放则上报异常(不会 crash)。原理可参考系统说明文档 CloseGuard。
  常见的可以检测的资源泄漏:各种 InputStream/OutputStream 未 closeandroid.view.Surface 未 releaseandroid.database.Cursor 未 closeandroid.app.usage.NetworkStats 未 close
  可以检测的资源依赖系统代码预埋。Android R 起系统开放了接入接口,可以将自定义的资源纳入检测手动回收必要性
  系统在 finalize 里会回收,为什么还有手动回收?finalize 回收是系统做的兜底,时机不确定,回收不及时会抬高内存峰值,当虚拟内存峰值接近 4GB,再申请内存就会 crash,即使以后能回收也晚了。未回收的资源背后可能是 java healp 内存, C/C++分配的内存,FD,mmap,GPU,Bitmap 等,不止会造成内存问题,还可能导致 FD 超限等问题线上监控
  目前已经将检测结果和缺陷平台打通,可以在版本灰度期间卡住新增的泄漏问题
  典型案例ZApacheSSLSocketFactory socket 泄露导致 pipe FD 泄露2021-05-14 14:29:12:801 E/mynet_HttpWorker:[5761:HttpManager.HttpWorker#h5#14] processException,exceptionName = IOException,code=[6] canRetry=[false] e=[java.net.SocketException: Socket closed] java.net.SocketException: Socket closed at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)     at com.android.org.conscrypt.NativeSsl.doHandshake(NativeSsl.java:387)     at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:226)     at com.alipay.mobile.common.transport.ssl.ZApacheSSLSocketFactory.createSocket(ZApacheSSLSocketFactory.java:100079)     at com.alipay.mobile.common.transport.http.ZClientConnectionOperator.connect(ZClientConnectionOperator.java:100221)     at com.alipay.mobile.common.transport.http.ZClientConnectionOperator.openConnectionCustome(ZClientConnectionOperator.java:100108)     at com.alipay.mobile.common.transport.http.ZClientConnectionOperator.openConnection(ZClientConnectionOperator.java:100193)     at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:170)     at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:124)     at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:366)     at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:587)     at com.alipay.mobile.common.transport.http.AndroidHttpClient.execute(AndroidHttpClient.java:100002) 	at com.alipay.mobile.common.transport.http.HttpWorker.doExecuteRequestByHttpClient(HttpWorker.java:100017)
  sslSocket.startHandshake 中抛出异常导致 sslSocket 对象没有机会关闭public class ZApacheSSLSocketFactory implements LayeredSocketFactory {     @Override     public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {         SSLSocket sslSocket = (SSLSocket) ZCustSSLSocketFactory.getSSLSocketFactory().createSocket(                 socket,                 host,                 port,                 autoClose         );         // ...省略部分代码         int soTimeout = socket.getSoTimeout();         try {             sslSocket.startHandshake();         } finally {             setSoTimeout(socket, soTimeout);         }         return sslSocket;     } }Inflater/okhttp.GzipSource/HttpURLConnection.getResponseCode java.lang.Throwable: Explicit termination method "end" not called at dalvik.system.CloseGuard.open(CloseGuard.java:221)     at java.util.zip.Inflater.(Inflater.java:114)     at com.android.okhttp.okio.GzipSource.(GzipSource.java:62)     at com.android.okhttp.internal.http.HttpEngine.unzip(HttpEngine.java:473)     at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:648)     at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:471)     at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:407)     at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:538)     at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)     at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:26) 	at com.alipay.android.msp.framework.helper.FileHelper.a(FileHelper.java:43)
  原因:HttpURLConnection的getInputStream 未 closeDeflater/GZIPOutputStream.java.lang.Throwable: Explicit termination method "end" not called 	at dalvik.system.CloseGuard.open(CloseGuard.java:221) 	at java.util.zip.Deflater.(Deflater.java:181) 	at java.util.zip.GZIPOutputStream.(GZIPOutputStream.java:90) 	at java.util.zip.GZIPOutputStream.(GZIPOutputStream.java:109) 	at com.alipay.mobile.mascanengine.imagetrace.ImageTracer.trace(ImageTracer.java:2161)
  原因: GZIPOutputStream未close导致Deflater未 endGZIPOutputStream 设计问题导致无法关闭 Deflaterjava.lang.Throwable: Explicit termination method "end" not called 	at dalvik.system.CloseGuard.open(CloseGuard.java:237) 	at java.util.zip.Deflater.(Deflater.java:189) 	at java.util.zip.GZIPOutputStream.(GZIPOutputStream.java:90) 	at java.util.zip.GZIPOutputStream.(GZIPOutputStream.java:109) 	at org.nanohttpd.protocols.http.response.Response.a(Unknown Source:29) 	at org.nanohttpd.protocols.http.response.Response.send(Unknown Source:291)
  原因:GZIPOutputStream 构造函数往 socket 写数据异常,导致 Deflater 没机会关闭java.net.SocketException: Broken pipe 	at java.net.SocketOutputStream.socketWrite0(Native Method) 	at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109) 	at java.net.SocketOutputStream.write(SocketOutputStream.java:141) 	at org.nanohttpd.protocols.http.response.ChunkedOutputStream.write(Unknown Source:25) 	at org.nanohttpd.protocols.http.response.ChunkedOutputStream.write(Unknown Source:2) 	at org.nanohttpd.protocols.http.response.MyOutputStream.write(Unknown Source:6) 	at java.util.zip.GZIPOutputStream.writeHeader(GZIPOutputStream.java:182) 	at java.util.zip.GZIPOutputStream.(GZIPOutputStream.java:94) 	at java.util.zip.GZIPOutputStream.(GZIPOutputStream.java:109) 	at org.nanohttpd.protocols.http.response.Response.a(Unknown Source:29) 	at org.nanohttpd.protocols.http.response.Response.send(Unknown Source:291)
  解决方案:public class GzipOutputStreamWrap extends GZIPOutputStream {      public GzipOutputStreamWrap(OutputStream outputStream, int size) throws IOException {         super(new OutputStreamWrap(outputStream), size);         if (this.out instanceof OutputStreamWrap) {             OutputStreamWrap wrap = (OutputStreamWrap) this.out;             wrap.mCacheException = false;             if (wrap.mCachedException != null) {                 this.def.end();                 throw wrap.mCachedException;             }         }     }      static class OutputStreamWrap extends FilterOutputStream {          private boolean mCacheException = true;         private IOException mCachedException = null;          public OutputStreamWrap(OutputStream out) {             super(out);         }          @Override         public void write(int b) throws IOException {             if (mCacheException) {                 try {                     super.write(b);                 } catch (IOException e) {                     mCachedException = e;                 }             } else {                 super.write(b);             }         }          @Override         public void write(byte[] b, int off, int len) throws IOException {             if (mCacheException) {                 try {                     super.write(b, off, len);                 } catch (IOException e) {                     mCachedException = e;                 }             } else {                 super.write(b, off, len);             }         }          @Override         public void write(byte[] b) throws IOException {             if (mCacheException) {                 try {                     super.write(b);                 } catch (IOException e) {                     mCachedException = e;                 }             } else {                 super.write(b);             }         }     } }五、内存优化方案
  在对内存问题治理的同时我们也在尝试进行支付宝运行时的内存优化,主要思路是通过内存监控进行全端的内存水位通知,让监听的业务方能结合自身情况给主进程释放更多的空间5.1 全局内存通知降级客户端MemoryNotice监听查询级别定义 MemoryLevel,用 int 表示,级别之间可直接比较大小Critical:内存水位很高,很容易 OOMHigh:内存水位高,可能 OOMNormal:内存水位正常监听接口final AbnormalReq abnormalReq = new AbnormalReq(); abnormalReq.type = MemoryNotice.class; Stability.getAbnormalDCApi().registerAbnormalListener(new ADCApi.AbnormalListener() {    private final Set request = Collections.singleton(abnormalReq);    @Override   public Set accepts() {     return request;   }    @Override   public void onAbnormal(Abnormal abnormal) {     if (abnormal instanceof MemoryNotice) {       int memoryLevel = ((MemoryNotice) abnormal).getMemoryLevel();       switch (memoryLevel) {         case MemoryNotice.MemoryLevel.Critical:               // 释放内存。               // 回调不在主线程执行           break;       }     }   } });主要监听业务:多媒体图片缓存服务,在内存紧张时可降低已缓存的图片内存数据Lottie内存释放,在首页腰封等展位通常使用Lottie动画展示动图,在内存紧张情况下可在首页不可见时降低缓存数据小程序容器会在Critical内存等级时提示用户当前使用的小程序可能出现异常,引导用户关闭小程序释放内存空间,避免主进程闪退Art mainspace内存空间释放监听了内存通知,当内存超过一定阈值时会触发内存压缩(详见下文)小程序业务统一降级,支付宝大促活动通常会接入统一降级能力,通过开关可直接对线上活动页的动画,特效,渲染方式等做降级,在端上内存不足时会触发统一降级的自动降级,达到降低业务内存使用的目的5.2 Art Mainspace空间压缩技术方案:
  通常情况下,我们的应用都会开启 largeHeap,来获得更大的内存上限,因为默认可用的空间只有 192M,这是由参数  dalvik.vm.heapgrowthlimit 决定的,对于大部分集团应用来说显然是不够的,但是开启 largeHeap 之后实际可用则可达到 512M = 1024 / 2 ,这也导致了应用启动的时候就会申请 1G 的地址空间,同样对于大部分应用来说,直到 abort 或者应用被杀死,都不会使用如此多的地址空间
  释放前Mainspace占有1G 释放后Mainspace占有500M效果:
  500M内存对于32位包的OOM问题降低有非常大的作用,在线上的闪退率监控中使用Mainspace释放后的版本闪退率降低了30%5.3 Gpu独立进程技术方案:gpu进程核心两个作用:
  1、剥离驱动兼容性问题到子进程,不要让他影响主进程
  2、降低内核渲染对主进程的资源依赖,降低主进程oom 引入并适配GPU独立进程 改造前:
  改造后:
  效果:
  在gpu独立进程全量开启后,线上日均8K的OOM闪退类型:GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed和sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory几乎清零。在2021年五福项目期间GPU独立进程使得支付宝Android整体闪退率下降了 65% ~73%六、总结
  支付宝内存从2020年初开始治理优化,OOM闪退率从最开始的万分之0.84降低到万分之0.14,在线上问题感知上,内存水位和业务异常内存变化可在5分钟内感知到,在问题诊断定位上可达到半小时内定位具体业务和问题根因代码,H5/小程序可在1天内修复好迭代发版。在新春五福红包等大促活动中峰值OOM闪退率也从万分之2降低到万分之0.3左右,极大的降低了客户端故障风险。
  内存优化实践大图
  同时我们总结了一套完整的内存优化实践方法论,本文主要针对线上能力和案例进行了分析,其实线下的问题定位和卡劣能力建设同样非常重要。1. 线下IDE工具和泄漏检测工具建设
  线下定位和复现工具建设,包括线下的内存水位曲线监控工具,泄漏检测工具,端上实时内存数据展示等,能帮助我们在线下快速定位到问题根因,帮助业务修复2. 版本发布内存劣化卡口
  在版本发布周期内对每日构建包进行大版本间的内存水位对比和泄漏检测,在版本发布维度将问题卡在线3. H5/小程序及动画资源变更卡口
  制定业务资源和内存使用标准在业务开发阶段引入检测手段发现异常点并提示给前端开发。在H5/小程序、Lottie等发布上线环节增加卡劣检测措施,提前发现内存问题
  目前支付宝在最近几个大版本平稳过渡到了64位包,64 位应用的虚拟内存地址空间上限是  2^39=512GB,理论上看彻底解决了32位包的虚拟内存地址不足的问题,但是线上32位包用户占比仍有约10%左右,短时间内都将保持双包并存,因此32位包用户的体验问题仍然不能放手不管。同时64位包升级后的Java堆内存不足问题和线程超限问题同样需要关注。面对支付宝庞大的线上用户和复杂的线上问题,64位升级远远不是终态,前方面临的挑战还有很多,时刻不松懈才能守住内存的底线。七、参考文献
  【手淘稳定性】Native治理利器——Native Finder
  Android 32 位应用 Native Crash Top1 Abort 的探索和解决
  链接2019年双十一Android U4内核OOM问题总结
  西瓜视频稳定性治理体系建设二:Raphael 原理及实践
  GPU独立进程(进程隔离)
  八、本文作者
  安晴、推敲
  转自支付宝质量开放平台AnTest:
  https://antest.alipay.com/ecoquality/tblog/01bwwe/03eyb2

盘点娱乐圈的出轨明星娱乐圈的夫妻档,大多数人都是前一套后面有一套。为了共同的利益和名誉,即使婚姻中的事情太多,离婚时也会选择留一些薄面。如果婚内出轨公之于众,结局会很惨。即使对方有意原谅,外界的流言蜚堆料扎实的平民主板,微星MAGB760MMORTARWIFI主板评测前言intel的13代酷睿处理器已经发布有一段时间了,不过目前市面上适配的主板只有高端的Z790系列,想要处理器超频的用户自然不会错过Z790主板,但对于那些想入手13代i3i5甚一纸对赌协议,娱乐圈的人情冷暖,在杨幂身上体现得淋漓尽致2015年,刘恺威录制节目大牌驾到,在提问环节,主持人华少问刘恺威在你眼中,杨幂最大的缺点是什么?刘恺威不假思索地给出了一个令人意外的回答。她忙啊,她太忙了。这番简短的回答,却似乎万梓良为拍戏吃吐,发掘周星驰,吵架给女友下跪,却晚年悲凉当年,万梓良与周星驰一起拍戏到凌晨3点。万梓良对周星驰说星仔,你现在去给我买10罐啤酒,10个面包,和一个西瓜。当时身为小弟的周星驰也不敢问,只好骑上一辆破车,找了好几条街,才买到美国翻拍西游记?吴彦祖饰演孙悟空,网友问过我们了吗?大家晚上好!前几天,小编刷到一篇资讯。迪士尼合拍西游记要来了,作为一个影视迷。小编也去了解了一下,结果发现确实大有来头。杨紫琼吴彦祖主演,不过在最近放出的剧照中,却引发了众多网友的94版关羽戏如人生,一生张扬,晚年落寞2022年11月1日,关羽去世。自从在94版三国演义中扮演关羽后,演员卢树明就一直没有离开过这个角色。因为表演太生动了,人们真的把他当成了关二爷。据传说,他在片场盛装打扮,有人当场七部春节档贺岁片,杀疯了,谁会是票房冠军,谁能再创票房神话?2023年元旦档落下帷幕。根据灯塔专业版数据统计,元旦三天总票房为5。5亿元。这无疑为2023年的电影市场开了一个好头。业内很多人开始对春节档充满了期待。目前,已有无名满江红流浪地S30赛季新晋野王,普攻英雄的克星,八点技巧教你快速上手盘古!S30赛季盘古迎来了半重做,能量臂状态下普攻范围更大且伤害更高,输出能力大幅提升。增强后的盘古排位胜率高达53。47,巅峰赛胜率51。27,成为了S30赛季打野位的上分黑马。本期就62岁蔡振华气质依旧,为乒乓球电影做宣传说到蔡振华刘国梁相信很多球迷不会陌生,他们都是非常出色的国乒人,蔡振华被称为国乒教父,中国乒乓球能有今天的繁荣昌盛真的离不开蔡振华,蔡振华成功带领刘国梁孔令辉重回世界之巅。虽然蔡振北京外资企业加快复工复产,去年111月引资规模已超2021全年北京新闻广播德尔格2022年订单量近5亿元人民币,近两年实现年均15持续增长北京施耐德经开区工厂恢复正常生产,到岗率达到95瓦里安北京基地今年将持续扩大产能,预计产量将增长30北京北京画院藏齐白石精品展丨荷花蜻蜓荷花蜻蜓齐白石135x34cm纸本设色题款齐璜。钤印木人(朱文)大匠之门(白文)艺术家简介齐白石(18641957)齐白石是20世纪中国最负盛名最具创造力和影响力的中国画大师。他原
3味中药,消痰浊,去湿气,除血瘀,治一切腹痛!隐痛胀痛灼痛绞痛刺痛腹部要闹事,痛可是多种多样。这种疼痛,是一种脾胃肠病。阳气虚弱,导致脏腑气机不利,痰浊,湿气,瘀血便聚而成邪,引起腹痛。通则不痛,痛则不通。因此内经早就提出了痛北京安贞医院副院长周玉杰防治高血压从上游做起来源人民日报健康客户端高血压群防群策还有很大空间。很多高血压病人来就医时已经造成了心脑肾的并发症,事实上,高血压实施降压的意义应该在上游截断,而不是在下游打捞,应当早用药早控制早干起床后头晕怎么回事?可能是低血压在捣乱,春天如何稳定好血压?最近王阿姨说,明明她每晚都好好睡了一宿,但是第二天起床时还是觉得头昏昏沉沉的,甚至还觉得头晕,她想自己是不是脑供血不足了呢。相关专家解释,如果起床后经常感觉头晕,有可能是体位性低血C罗上线新品香水梅西推800球纪念衫,足坛巨星吸金转向场外记者杨弋北京时间2023年3月28日,在2023年解放者杯和南美杯抽签仪式之前,南美足协为卡塔尔世界杯冠军得主阿根廷国家队,安排了致敬仪式。仪式上,梅西发表演讲称,是时候让南美洲球乔丹普尔和乔丹克拉克森全方位数据对比乔丹普尔和乔丹克拉克森全面数据对比。半斤八两,普尔胜在年轻,还有潜力可挖,他的上限就是克拉克森了吗?身高普尔196CM克拉克森196CM体重普尔88。4KG克拉克森88KG臂展普尔有没有身体有病年轻时就开始吃药,但是寿命活的长的人?我岳母三十八岁生产大出血,之后手脚麻木,失眠,跑医院,吃药,不见好转,紧接着血压升高,血糖血脂异常,被确诊高血压糖尿病抑郁症,每天吃药,每年多次住院,去过淮阴淮安连云港徐州南京上海新农合断缴潮发酵,农村养老保险也遇冷,农村人还怎么养老?其实应该整治,没新农合的时候,感冒住院花1000,有了住院花2000,报销下来还是出1000,不必要的就别让医生开新农合与农村养老保险确实是个好东西,那么为什么农民要断缴呢,农民真现在娶媳妇彩礼越来越高,如果有一天取消彩礼你会赞成吗?感谢邀请!赞成赞成,不但赞成,而且还要举双手赞成,只是脚踏实地紧贴路面抬不起来要不然脚也赞成。看来皇天开眼有好生之德无绝人之路,想俺八十多岁孤苦伶仃住茅草房的糟老头子今后还有机会娶美系车的优点是什么?作为合资汽车三大车系之一,美系车涵盖了通用旗下的别克雪佛兰凯迪拉克,福特旗下的福特林肯以及克莱斯勒JEEP道奇等品牌。那么美系车有哪些优点呢?终端优惠较大,性价比更高对比德系和日系20岁年轻小伙人菜瘾大痿泄不止,一个方子重回巅峰!20岁出头的小伙子,平时想法很多总是本能的用手去解决。因为自己的不自律从而萎泄不止,身体大没有以前好了。面诊了解他时间短,经常性的见色倒戈半途而泄。总是感觉精神疲惫,动不动就出汗,薛立斋医案湿热体质不一定要清湿热,这两种中成药或许有用大尹刘天锡,今天的这位患者同样是一位官员,那么官员有一个特点,就是肥甘厚味吃的特别多,并且经常喝酒,肥甘厚味容易滋生痰湿,而酒食更助湿热,所以这类人体质慢慢就会变成湿热的体质。为什