要了解几个概念:
- ManagedHeap 表示的是Mono所使用的托管堆内存,C#上任何的申请托管的内存都会在这个上面申请。
- ManagedHeap.UsedSize 表示这个托管堆上已经使用的内存大小
- ManagedHeap.reservedUnUsedSize 表示托管堆上未使用的内存大小
当我们申请内存时,如果ManagedHeap.reservedUnUsedSize的内存不够用,Mono会向os申请内存。并且这个内存会扩大ManagedHeap的大小,并且这个内存不会回落。
我们可以通过Profiler.GetMonoHeapSize()获取Mono堆上的总内存和mono_gc_get_heap_size是一个意思。
下面写了一个测试用例:
-
using UnityEngine;
-
using System;
-
using System.Runtime.InteropServices;
-
using System.Text;
-
public class TestManagedHeap : MonoBehaviour
-
{
-
public int arraysize = 1024 * 1024 * 200;//条件触发延迟申请的内存1
-
public int arraysize_frame = 1024 * 1024 * 10;//条件触发延迟申请的内存2
-
[DllImport("mono.dll")]
-
public static extern long mono_gc_get_used_size();
-
[DllImport("mono.dll")]
-
public static extern long mono_gc_get_heap_size();
-
//private StringBuilder builder = new StringBuilder(256);
-
// Use this for initialization
-
void Start()
-
{
-
}
-
// Update is called once per frame
-
void Update()
-
{
-
if (Input.GetKeyDown(KeyCode.U))
-
{
-
byte[] array = new byte[arraysize];
-
array = null;
-
Debug.Log("申请200M");
-
printMemory();
-
}
-
if (Input.GetKeyDown(KeyCode.A))
-
{
-
byte[] array = new byte[arraysize_frame];
-
Debug.Log("申请10M");
-
printMemory();
-
}
-
if (Input.GetKeyDown(KeyCode.B))
-
{
-
Debug.Log("调用GC");
-
GC.GetTotalMemory(true);
-
printMemory();
-
}
-
}
-
void printMemory()
-
{
-
long usedsize = mono_gc_get_used_size();
-
long heapsize = mono_gc_get_heap_size();
-
long reservedsize = heapsize - usedsize;
-
builder.Length = 0;
-
//builder.AppendFormat("使用内存:{0},剩余内存{1},托管堆内存{2}",usedsize,reservedsize,heapsize);
-
//print(builder.ToString());
-
Debug.Log("使用内存=" + usedsize * 1.0f / 1024 / 1024 + "M");
-
Debug.Log("剩余内存=" + reservedsize * 1.0f / 1024 / 1024 + "M");
-
Debug.Log("托管堆内存=" + heapsize * 1.0f / 1024 / 1024 + "M");
-
}
在测试中会发现,当申请了200M的内存后,再申请10M的内存。堆上的内存mono_gc_get_heap_size并没有减少,这是因为,在Mono堆上的内存在应用程序运行期间是只增不减的,即使GC也不会把申请的内存还给os,而是设置成了未使用状态。
Memory is allocated in heap blocks. More can allocated if it cannot fit the data into the allocated block. Heap blocks will be kept in Mono until the app is closed. In other words, Mono does not release any memory used to the OS (Unity 3.x). Once you allocate a certain amount of memory, it is reserved for mono and not available for the OS. Even when you release it, it will become available internally for Mono only and not for the OS. The heap memory value in the Profiler will only increase, never decrease.
上面是摘自Unity官方API对Profiling对Mono Memory的解释。
鉴于这个原因,在app开发时需要注意什么呢?
- 不要一次性申请过大的内存;
- 切换场景等操作时可以自己手动调用GC.Collect
///