mono中的内存泄漏和WeakReference(弱引用)的使用


我们大家有时候在做 unity 项目的时候,游戏后期最常见的问题,应该就是内存泄漏了。 
其实这里面有一些小细节,就可能在你不知不觉间导致内存泄漏的发生。 
这里说说释放对象时容易发生的问题。

先来看一段代码:

using UnityEngine;
using System;
using System.Collections;

public class MemoryLeak : MonoBehaviour {

	private float m_countdown = 10;
	private bool m_turnOn = false;

	TestB m_tb;
	TestA m_ta;

	// Use this for initialization
	void Start()
	{
		m_turnOn = true;

		m_tb = new TestB();
		m_ta = new TestA(m_tb);
	}

	// Update is called once per frame
	void Update()
	{
		if (m_turnOn == false)
			return;

		if (m_countdown >= 0)
		{
			m_countdown -= Time.deltaTime;
		}
		else
		{
			m_turnOn = false;

			m_ta = null;

			GC.Collect();
			Debug.LogError("清理内存");
		}
	}

	public class TestA
	{
		private long[] m_array;

		TestB m_tb;

		public TestA(TestB tb)
		{
			m_array = new long[100000000];

			m_tb = tb;
			m_tb.TestFun();
		}
	}
	public class TestB
	{
		private long[] m_array;

		public TestB()
		{
			m_array = new long[100000000];
		}

		public void TestFun()
		{
			Debug.LogError("测试方法");
		}
	}
}

代码内容十分的简单,我就不说了,我们来看用 Profiler 工具所截取的内存用量显示

gc前:

这里写图片描述
我们看到了,Mono内存是 2.99G (别问我mono内存是什么。。。)

gc后:

这里写图片描述
我们看到了,经过一次gc后,内存降到了 2.24G ,这时候,也许有人该说了,内存这不是降低了吗,没错呀╮(╯▽╰)╭。我要告诉你,少年,你高兴的太早了!

现在,我们再来看一段代码:

using UnityEngine;
using System;
using System.Collections;

public class MemoryLeak_new : MonoBehaviour {

	private float m_countdown = 10;
	private bool m_turnOn = false;

	WeakReference m_wrf_b;
	TestA m_ta;

	// Use this for initialization
	void Start()
	{
		m_turnOn = true;

		TestB tb = new TestB();
		m_wrf_b = new WeakReference(tb);

		m_ta = new TestA(m_wrf_b);
	}

	// Update is called once per frame
	void Update()
	{
		if (m_turnOn == false)
			return;

		if (m_countdown >= 0)
		{
			m_countdown -= Time.deltaTime;
		}
		else
		{
			m_turnOn = false;

			m_ta = null;

			GC.Collect();
			Debug.LogError("清理内存");
		}
	}

	public class TestA
	{
		private long[] m_array;

		WeakReference m_wrf;

		public TestA(WeakReference wrf)
		{
			m_array = new long[100000000];

			m_wrf = wrf;

			TestB tb = (TestB)m_wrf.Target;
			tb.TestFun();
		}
	}
	public class TestB
	{
		private long[] m_array;

		public TestB()
		{
			m_array = new long[100000000];
		}

		public void TestFun()
		{
			Debug.LogError("测试方法");
		}
	}
}

现在,让我们再来做一遍之前的过程。

gc前:

这里写图片描述

gc后:

这里写图片描述

所以这一切是为什么呢?

主要原因就是因为我们new出来的这个对象,其实在c#中被称作强引用对象。 
举例来说就是,在A对象依赖于B对象时,如果我们把B释放了,你会认为我们连A也一起释放了。 
其实这只是你的错觉,真实情况是你释放了B,但是A是强引用类型,结果你的A找不到依赖了,变成内存泄漏了。。。

解决这个问题有2种办法,一种就是: 
你在释放B的同时,把A也一起干掉,像第一段代码的gc那里改成这样:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">m_tb = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;
m_ta = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

或者用第2段代码里的方法: 
正所谓有强就有弱,WeakReference应该是c#从java那里学来的,叫做弱引用对象,原理我就不说了,你去查查就明白了。 
WeakReference就是专门用来解决因为这种依赖关系造成的gc释放问题。 
当然WeakReference也不是万能的,有时可能gc已经把对象释放了,但是你new出来的WeakReference是不会释放的,结果你还用它去取存放在里面的对象,那时你取到的可能就是个null了。这时候,你就需要再重新把对象创建出来。 
所以请结合你自己系统的底层架构,合理,正确的去使用弱引用对象。


题外话: 
虽然这是一个我们写代码时,经常会范的错误,但是据说相同的问题,如果是在微软的.net下,虽然可能会晚一些,但是泄漏的那块内存最终还是会被回收的,最新版本的 mono 好像也行。(当然具体的我不清楚,也懒得去试了) 
但是因为unitymono版本太低了,记得官方说是 2.2 ,所以泄漏的内存就收不回来了,囧rz。 
不过进行Scene的切换,会有助于释放一些我们无法管理的内存,这一点是可以充分利用起来的。

所以还是希望unity能够把mono版本给升一下,虽然这个希望十分的渺茫,残念~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值