我胡汉三又回来啦!~
内存交换
标准交换在内存与备份存储之间移动进程,备份存储通常是快速磁盘,它应足够大,以容纳所有用户的所有内存映像的副本,并且它应提供对这些存储器映像的直接访问。
移动设备不支持任何形式的内存交换。
- 移动设备通常采用闪存,而不是空间更大的硬盘作为它的永久存储。导致的空间约束是移动操作系统设计者避免交换的原因之一;
- 闪存写入次数的限制;
- 内存与闪存之间的吞吐量差;
当空闲内存降低到一定阈值以下时:
- iOS:不釆用交换,而是要求应用程序自愿放弃分配的内存。只读数据(如代码)可从系统中删除,以后如有必要再从闪存重新加载。已修改的数据(如堆栈)不会被删除。然而,操作系统可以终止任何未能释放足够内存的应用程序。
- Android:不支持交换,釆用类似 iOS 使用的策略。如果没有足够可用的空闲内存,则它可以终止进程。然而,在终止进程之前,Android 将其应用程序状态写到闪存,以便它能快速重新启动。
iOS支持内存压缩,Android不可以。
Android 内存单位为Page,大小一般为4k,以分配单位回收,分为用户态和内核态,不可越界访问。
Low Memory Killier:内存杀手。
Android服务优先级(从高到低),当内存不够时从优先级低的开始销毁:
- Native
- System 当杀到这一层级的时候手机重启
- Persistent
- Foreground 前台应用,具体表现为APP闪退
- Perceptible
- Service
- Home 桌面
- Previous 上一个使用的APP
- Cached 后台APP
Unity内存
按类型分为 Native Memory 和 Managed Memory
Editor与Runtime不同
- Editor模式会尽可能的加载资源(启动较慢)
- Runtime按需加载
Unity封装了C++中与申请内存相关的函数,并添加了Label用于统计。
Audio
DSP Buffer
- 过大会导致声音有延迟
- 过小会使CPU负担上升
Force to mono
双声道改单声道(尽量,绝大多数声音不需要双声道)
Compress Format
Code Size
慎用模板泛型,
AssetBundle
Type Tree
当Unity版本与APP版本一致时可以关掉
-
- 减小内存
- 减小包大小
- Build和Runtime变快
压缩方式
LZ4 推荐
LZMA 弃用
extra
不建议每个文件打一个AssetBundle包
因为它们有相同的“头”,仅数据部分不同,容易浪费空间。
Resources
通过红黑树进行索引,每次启动游戏都要分析加载红黑树,拖慢启动时间。其次整个游戏生命周期都会存在与内存中。
Texture
Upload Buffer
Read&Write
没有特殊需求就关掉write
Mipmap
同样没有特殊需求就关掉
Mesh
Read&Write
Compresssion
Managed内存
VM内存池
VM会返还内存给OS
条件:一个block连续6次GC没被访问到
Unity GC 算法
貌似不采用CLR的GC机制_(:з」∠)_(参见另一篇文章,传送门)
Boehm GC
旧版垃圾回收方式,新版Unity已弃用。
不分代(速度更快)
不压缩(因为一些历史原因。。跟mono有关)
Incremental GC(渐进式GC)
Unity2019.3之后使用,新一代垃圾回收算法。
主要目的为了解决旧版GC导致主线程卡顿(旧版GC时会暂停主线程)的问题,分帧解决,且分代
GC机制考量
- 回收能力
- 暂停时长
- 碎片化
- 额外消耗
- 可扩展性
- 可移植性
内存碎片化
内存碎片通常分为内部碎片和外部碎片:
- 内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就产生了内部碎片,通常内部碎片难以完全避免;
- 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用的内存区域。
先加载消耗大内存的资源,再加载小内存
僵尸内存
- 无用内存
- 没有释放
- 可通过代码管理和性能工具分析
SetActive
TODO。。。
都说要少用,但是不清楚机制,有机会看一下Unity源码 Orz
优化建议
- 使用Destroy,别用null
- Class/Struct
- 少于8个字节的数据可用Struct代替Class(存疑)
- 池中池
- 闭包和匿名函数
- 少用,在IL2CPP里这些都会new class
- 协程
- 用的时候再产生,不用的时候及时终止
- 不要当线程使用
- 配置表
- excel、json等,按需加载,不然会很占用内存空间
- 单例模式 Singleton
- 慎用,生命周期较长,占用内存