关于GC的原理和 Unity 中如何针对 GC 进行优化的建议

2021-11-23  本文已影响0人  太刀
例牌美女

1. 应用程序内存结构

应用程序内存空间通常划分为五个部分

1.1 静态/全局存储区

存放全局和静态变量,静态分配的,在程序执行的最开是分配,后面不会再增长

1.2 常量存储区

存储程序中的常量

1.3 代码段

存放程序执行代码的内存区域,在程序运行之前就已经确定了,通常是只读的,当多个进程运行同样的程序时,可以使用同一个代码段

1.4 栈

栈是一块连续的内存区域,栈的容量由系统规定,栈底地址在程序初始化时就确定了。栈通常用来存储局部变量、函数的参数和返回值

1.5 堆

堆主要用来存放动态分配的对象,堆的大小由系统内存/虚拟内存的上限决定

2. 内存管理方式

主流的内存管理方式包括三种:手动管理、引用计数和垃圾回收(Garbege Collect,GC)

2.1 手动管理

2.2 引用计数

2.3 垃圾回收(GC)

3. .Net/Java 中 GC 的原理

3.1 堆内存划分区域

堆内存区域划分

将堆内存划分为年轻代、老年代和永久代

3.2 内存分配过程

当需要为一个对象分配内存时,过程大概是

3.3 GC 触发时机

以下几种情况会触发 GC:

3.4 GC 过程和算法

3.4.1 年轻代基于复制的 GC 算法

执行在年轻代的 GC 也称之为 MinorGC,过程如下

3.4.2 老年代标记-清除的 GC 算法

执行于老年代的 GC,也成为 Major GC,其中一种算法是标记-清除算法,过程如下

3.4.3 老年代标记-整理的 GC 算法

另外一种算法改进了标记-清除算法的问题,称为标记-整理算法,过程如下

3.4.3 GC时如何判断对象是否存活
3.4.4 哪些对象是根对象

可以作为根对象来进行可达性判定的对象包括:

3.4.5 Minor GC,Major GC 和 FullGC

Minor GC 是发生在年轻代中的 GC,触发较为频繁,Major GC 是发生在老年代中的 GC,相对不频繁,触发 FullGC 时会先执行 Minor GC,然后执行 Major GC

4. Unity 中的 GC

4.1 Unity 中GC的特性

4.2 Unity 中关于 GC 优化的建议

4.2.1 减少对象的大小

合理安排类或结构体的字段声明顺序,以优化其对象的内存布局进而减少对象大小,结构体可以使用 StructLayout 属性

关于结构体,结构体本身是值类型。若结构体中不包含引用类型时,针对结构体的 new 操作不会造成 GCAlloc,但若结构体中包含引用类型字段,如 string 或数组等,那么在对结构体执行 new 操作时会产生 GC Alloc

4.2.2 降低内存分配的频次

也就是尽量减少 GCAlloc

5. 查看 Android 应用的内存情况

4.2.3 在适当的时机主动调用 GC.Collect

例如在场景切换显示加载界面时调用,用户无感知

4.2.4 关于调试日志的字符串参数

正式版本中使用 unityLogger.logEnabled = false 仅仅只是不打日志,但字符串已经被分配内存,GC Alloc 还是产生了。解决方法是使用 Conditional 特性来处理日志输出,在正式版本中不要生成打印相关的代码,也不会有字符串的生成,减少了 GCAlloc

上一篇 下一篇

猜你喜欢

热点阅读