当前位置: 首页 > news >正文

Android平台上的Native内存分析

Android平台上的Native内存分析

https://blog.csdn.net/xoyojank/article/details/82876508

文章目录

    背景
    UE4的内存统计
        memreport
        MemoryProfiler2
        LLM
    Android进程内存
    DDMSMemoryAnalyzer

背景

UE4游戏在Android上的进程内存占用(PSS)很让人困惑, 没有一个清晰直观的方式可以统计到每一部分的内存占用. 所以在做内存分析的过程中顺手做了一个统计工具, 可以从系统底层统计UE4在Android的所有内存分配(包括Graphics部分).
在这里插入图片描述
UE4的内存统计

UE4本身提供了3种内存分析方式:

    memreport
    MemoryProfiler2
    LLM

下面分别做一下说明
memreport

游戏中console command输入”memreport-full”可以保存内存报告在Saved/Profiling/MemReports目录下, 就是一个文本文件, 长这样:
在这里插入图片描述
里面有各种类型的内存统计数据, 如Texture, RTT, VB/IB, Particle, Component等, 每一种都有比较详细的信息. 缺点是引擎只会从某个角度做统计, 对于一些统计代码没有覆盖到的内存是没有相关信息的, 而且各个统计类型之间是有重叠, 无法把各个子项加起来就能还原出内存的总体占用分布.
MemoryProfiler2

引擎代码中打开USE_MALLOC_PROFILER宏, 编译后使用”mprof start/stop/snapshot”命令来dump内存日志, 再使用MemoryProfiler2.exe打开查看:
在这里插入图片描述
MemoryProfiler2的统计是基于UE4的内存分配器, 通过记录每次内存操作的调用堆栈, 然后进行累加统计, 使用GUI工具按照树形结构显示出来, 能够非常直观地进行分析. 缺点是会影响内存分配的效率, 而且记录的信息量太大, 中低端Android手机很难保存成功, 抓取到后工具的分析效率也比较慢. 如果一些内存没有通过UE4的内存分配器进行分配的话, 那是统计不到的, 比如一些第三方库或者组件.
LLM

LLM是4.18才加入的功能, 使用-LLM命令行参数启动才生效, 当时只有PC和主机平台可以用, 游戏中使用”stat LLM/LLMFULL”打开实时查看:
在这里插入图片描述
这种统计方式是基于scope的方式, 类似CPU性能分析的原理, 执行某段代码前记录一下, 执行后再统计对比一下, 这样可以做到没有遗漏, 而且也不会影响游戏的执行性能. 缺点是只是按模块或者分类的统计, 没有更细节的信息指导优化.
Android进程内存

使用dumpsys meminfo命令可以查看Android进程的内存占用信息:

adb shell dumpsys meminfo <package_name|pid> [-d]

    1

一般输出的信息像这样:
在这里插入图片描述
基中Gfv dev, EGL mtrack, GL mtrack是新版本Android才加入的, 之前版本都被统计在Native Heap中. 对比memreport就会发现, Unknown部分基本上与UE4自身统计到的内存一致, 那是因为UE4底层调用了mmap/munmap来进行内存分配, 所以没有被系统统计到Native Heap中:

FMalloc* FAndroidPlatformMemory::BaseAllocator()
{
    const FPlatformMemoryConstants& MemoryConstants = FPlatformMemory::GetConstants();
    // 1 << FMath::CeilLogTwo(MemoryConstants.TotalPhysical) should really be FMath::RoundUpToPowerOfTwo,
    // but that overflows to 0 when MemoryConstants.TotalPhysical is close to 4GB, since CeilLogTwo returns 32
    // this then causes the MemoryLimit to be 0 and crashing the app
    uint64 MemoryLimit = FMath::Min<uint64>( uint64(1) << FMath::CeilLogTwo(MemoryConstants.TotalPhysical), 0x100000000);

#if PLATFORM_ANDROID_ARM64
    // todo: track down why FMallocBinned is failing on ARM64
    return new FMallocAnsi();
#else
    // [RCL] 2017-03-06 FIXME: perhaps BinnedPageSize should be used here, but leaving this change to the Android platform owner.
    return new FMallocBinned(MemoryConstants.PageSize, MemoryLimit);
#endif
}

void* FAndroidPlatformMemory::BinnedAllocFromOS(SIZE_T Size)
{
    void* Ptr = mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
    LLM(FLowLevelMemTracker::Get().OnLowLevelAlloc(ELLMTracker::Platform, Ptr, Size));
    return Ptr;
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

如果把FMallocBinned替换成FMallocAnsi, 那么所有的内存都会被统计进Native Heap, 这时就可以使用Android的Malloc Debug进行内存分析了:
在这里插入图片描述
DDMSMemoryAnalyzer

注: 这个方法只在MiPad(Android4.4)上测试过, 最新版本Android的DDMS已经没有这个功能了.

    1

    首先保证/system/lib/libc_malloc_debug_leak.so存在, 如果没有, 需要自行提取相应版本并拷贝过去
    然后setprop libc.debug.malloc 1, 开启系统级的Malloc Debug(也可以指定进程)
    C:\Users[yourname].android\ddms.cfg, 加一行””native=true”
    启动ddms.bat, “Snapshot Current Native Heap Usage”, 可以抓取一个带有调用堆栈地址的txt文件
    在这里插入图片描述
    在这里插入图片描述

有了堆栈地址, 我们可以通过arm-linux-androideabi-addr2line.exe把地址翻译成函数名. 由于txt中保存的是内存地址, 而addr2line需要的是so的相对地址, 所以我们需要获取libUE.so的内存地址, 做个减法, 才能使用addr2line进行翻译. 使用以下命令可以找到libUE4.so的内存地址:

adb shell pmap -x [PID]

    1

比如下图, libUE4.so的内存地址是70226000:
在这里插入图片描述
有了so的相对地址, 就可以使用addr2line进行翻译了:

arm-linux-androideabi-addr2line.exe -C -s -f -e libUE4.so [ADDRESS]

    1

但是txt中记录的是每次调用的堆栈, 次数非常多, 每一次的分配大小都比较小, 不具备实际优化的参考价值, 所以把所有的堆栈地址合并进行内存的累加统计, 以树形结构自上而下查看, 内存占用就变得非常直观了, 这就是DDMSMemoryAnalyzer的基本思想:
在这里插入图片描述
同样的方法, 也适用于Unity引擎:
在这里插入图片描述

————————————————
版权声明:本文为CSDN博主「xoyojank」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xoyojank/article/details/82876508

 

 

 

 

 

 

 

相关文章:

  • Unity 通过解析libil2cpp.so文件查看Android Crash崩溃原因
  • Android native memory leak detect (Android native内存泄露检测)
  • Android死锁初探
  • Unity Profile 自己使用
  • iOS性能分析-Xcode Instruments Allocations 分析APP内存使用情况
  • UE 手游在 iOS 平台运行时内存占用太高?试试这样着手优化
  • UE4 Sequence学习笔记
  • UE4中的相机操控方案
  • 对ue4 sequence的学习和理解
  • [gdc19]《战神4》中的全局光照技术
  • 5 种避免使用 C# lock 关键字的方法
  • [玩转UE4动画系统>功能模块] 之 Advanced Locomotion System V4 的工具函数及工具宏详解
  • Lua5.4新特性
  • 探究光线追踪技术及UE4的实现 -- good
  • Unity游戏项目性能优化总结
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • Java超时控制的实现
  • MYSQL 的 IF 函数
  • php ci框架整合银盛支付
  • rabbitmq延迟消息示例
  • Rancher如何对接Ceph-RBD块存储
  • ReactNativeweexDeviceOne对比
  • springboot_database项目介绍
  • 阿里云应用高可用服务公测发布
  • 笨办法学C 练习34:动态数组
  • 猴子数据域名防封接口降低小说被封的风险
  • 计算机常识 - 收藏集 - 掘金
  • 看完九篇字体系列的文章,你还觉得我是在说字体?
  • 通过git安装npm私有模块
  • 系统认识JavaScript正则表达式
  •  一套莫尔斯电报听写、翻译系统
  • 用jquery写贪吃蛇
  • 怎样选择前端框架
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • #if 1...#endif
  • ${ }的特别功能
  • (env: Windows,mp,1.06.2308310; lib: 3.2.4) uniapp微信小程序
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (四)汇编语言——简单程序
  • (学习日记)2024.02.29:UCOSIII第二节
  • (一)基于IDEA的JAVA基础12
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • .libPaths()设置包加载目录
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .net php 通信,flash与asp/php/asp.net通信的方法
  • .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例
  • .NET/C# 使窗口永不获得焦点
  • .Net环境下的缓存技术介绍
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题
  • .net项目IIS、VS 附加进程调试
  • .NET序列化 serializable,反序列化
  • @Transactional注解下,循环取序列的值,但得到的值都相同的问题