vb netjava 抛出异常常后,会不会有内存泄漏

Release编译模式下,事件是否会引起内存泄漏问题初步研究
来源:博客园
题记:不常发生的事件内存泄漏现象
想必有些朋友也常常使用事件,但是很少解除事件挂钩,程序也没有听说过内存泄漏之类的问题。幸运的是,在某些情况下,的确不会出问题,很多年前做的项目就跑得好好的,包括我也是,虽然如此,但也不能一直心存侥幸,总得搞清楚这类内存泄漏的神秘事件是怎么发生的吧,我们今天可以做一个实验来再次验证下。
可以,为了验证这个问题,我一度怀疑自己代码写错了,甚至照着书上(网上)例子写也无法重现事件引起内存泄漏的问题,难道教科书说错了么?
首先来看看我的代码,先准备2个类,一个发起事件,一个处理事件:

class A
public event EventHandler ToDoS
public A()
}

public void RaiseEvent()
ToDoSomething(this, new EventArgs());
}

public void DelEvent()
ToDoSomething = null;
}

public void Print(string msg)
Console.WriteLine("A:{0}", msg);
class B
byte[] data = null;
public B(int size)
data = new byte[size];
for (int i = 0; i & i++)
data[i] = 0;
}

void PrintA(object sender, EventArgs e)
((A)sender).Print("sender:"+ sender.GetType ());
}

然后,在主程序里面写下面的方法:

static void TestInitEvent(A a)
var b = new B(100 * 1024 * 1024);
a.ToDoSomething += b.PrintA;
}

 这里将初始化一个 100M的B的实例对象b,然后让对象a的事件ToDoSomething 挂钩在b的方法PrintA 上。平常情况下,b是方法内部的局部变量,在方法外就是不可访问的,但由于b对象的方法挂钩在了方法参数 a 对象的事件上,所以在这里对象 b的生命周期并没有结束,这可以稍后由对象 a发起事件,b的 PrintA 方法被调用得到证实。
PS:有朋友问为何不在这里写取消挂钩的代码,我这里是研究使用的,实际项目代码一般不会这么写。
为了监测当前测试耗费了多少内存,准备一个方法
getWorkingSet,代码如下:

 static void getWorkingSet() 
using (var process = Process.GetCurrentProcess()) 
Console.WriteLine("---------当前进程名称:{0}-----------",process.ProcessName);
using (var p1 = new PerformanceCounter("Process", "Working Set - Private", process.ProcessName))
using (var p2 = new PerformanceCounter("Process", "Working Set", process.ProcessName))
Console.WriteLine(process.Id);
//注意除以CPU数量
Console.WriteLine("{0}{1:N} KB", "工作集(进程类)", process.WorkingSet64 / 1024);
Console.WriteLine("{0}{1:N} KB", "工作集 ", process.WorkingSet64 / 1024);
// process.PrivateMemorySize64 私有工作集 不是很准确,大概多9M 
Console.WriteLine("{0}{1:N} KB", "私有工作集 ", p1.NextValue() / 1024); //p1.NextValue()
//Logger("{0};内存(专用工作集){1:N};PID:{2};程序名:{3}", 
DateTime.Now, p1.NextValue() / 1024, process.Id.ToString(), process.ProcessName);
Console.WriteLine("--------------------------------------------------------");
Console.WriteLine();
}

 
下面,开始在主程序里面开始写如下测试代码:

getWorkingSet();
A a = new A();
TestInitEvent(a);
Console.WriteLine("1,按下任意键开始垃圾回收");
Console.ReadKey();
GC.Collect();
getWorkingSet();

看屏幕输出:

---------当前进程名称:ConsoleApplication1.vshost-----------
4848
工作集(进程类)25,260.00 KB
工作集 25,260.00 KB
私有工作集 8,612.00 KB
--------------------------------------------------------

1,按下任意键开始垃圾回收
---------当前进程名称:ConsoleApplication1.vshost-----------
4848
工作集(进程类)135,236.00 KB
工作集 135,236.00 KB
私有工作集 111,256.00 KB

程序开始运行后,正好多了100M内存占用。当前程序处于IDE的调试状态下,然后,我们直接运行测试程序,不调试(Release),再次看下结果:

---------当前进程名称:ConsoleApplication1-----------
7056
工作集(进程类)10,344.00 KB
工作集 10,344.00 KB
私有工作集 7,036.00 KB
--------------------------------------------------------

1,按下任意键开始垃圾回收
---------当前进程名称:ConsoleApplication1-----------
7056
工作集(进程类)121,460.00 KB
工作集 121,460.00 KB
私有工作集 109,668.00 KB
--------------------------------------------------------

可以看到在Release 编译模式下,内存还是没法回收。
分析下上面这段测试程序,我们只是在一个单独的方法内挂钩了一个事件,并且事件还没有执行,紧接着开始垃圾回收,但结果显示没有回收成功。这个符合我们教科书上说的情况:对象的事件挂钩之后,如果不解除挂钩,可能造成内存泄漏。
同时,上面的结果也说明了被挂钩的对象 b 没有被回收,这可以发起事件来测试下,看b对象是否还能够继续处理对象a 发起的事件,继续上面主程序代码:

 Console.WriteLine("2,按下任意键,主对象发起事件");
Console.ReadKey();
a.RaiseEvent();//此处内存不能正常回收
getWorkingSet();

结果:

2,按下任意键,主对象发起事件
A:sender:ConsoleApplication1.A
---------当前进程名称:ConsoleApplication1-----------
7056
工作集(进程类)121,576.00 KB
工作集 121,576.00 KB
私有工作集 109,672.00 KB
--------------------------------------------------------

 这说明,虽然对象 b 脱离了方法 TestInitEvent 的范围,但它依然存活,打印了一句话:A:sender:ConsoleApplication1.A
是不是GC多回收几次才能够成功呢?
我们继续在主程序上调用GC试试看:

Console.WriteLine("3,按下任意键开始垃圾回收,之后再次发起事件");
Console.ReadKey();
GC.Collect();
a.RaiseEvent();//此处内存不能正常回收
getWorkingSet();

结果:

3,按下任意键开始垃圾回收,之后再次发起事件
A:sender:ConsoleApplication1.A
---------当前进程名称:ConsoleApplication1-----------
7056
工作集(进程类)14,424.00 KB
工作集 14,424.00 KB
私有工作集 2,972.00 KB
--------------------------------------------------------

果然,内存被回收了!
但请注意,我们在GC执行成功后,仍然调用了发起事件的方法
a.RaiseEvent();并且得到了成功执行,这说明,对象b 仍然存活,事件挂钩仍然有效,不过它内部大量无用的内存被回收了。
注意:上面这段代码的结果是我再写博客过程中,一边写一遍测试偶然发现的情况,如果是连续执行的,情况并不是这样,上面这端代码不能回收成功内存。这说明,GC内存回收的时机,的确是不确定的。
继续,我们注销事件,解除事件挂钩,再看结果:

 Console.WriteLine("4,按下任意键开始注销事件,之后再次垃圾回收");
Console.ReadKey();
a.DelEvent();
GC.Collect();
Console.WriteLine("5,垃圾回收完成");
getWorkingSet();

结果:

4,按下任意键开始注销事件,之后再次垃圾回收
5,垃圾回收完成
---------当前进程名称:ConsoleApplication1-----------
7056
工作集(进程类)15,252.00 KB
工作集 15,252.00 KB
私有工作集 3,196.00 KB
--------------------------------------------------------

内存没有明显变化,说明之前的内存的确成功回收了。
 
为了印证前面的猜测,我们让程序重新运行并且连续执行(Release模式),来看看执行结果:

---------当前进程名称:ConsoleApplication1-----------
4280
工作集(进程类)10,364.00 KB
工作集 10,364.00 KB
私有工作集 7,040.00 KB
--------------------------------------------------------

1,按下任意键开始垃圾回收
---------当前进程名称:ConsoleApplication1-----------
4280
工作集(进程类)121,456.00 KB
工作集 121,456.00 KB
私有工作集 109,668.00 KB
--------------------------------------------------------

2,按下任意键,主对象发起事件
A:sender:ConsoleApplication1.A
---------当前进程名称:ConsoleApplication1-----------
4280
工作集(进程类)121,572.00 KB
工作集 121,572.00 KB
私有工作集 109,672.00 KB
--------------------------------------------------------

3,按下任意键开始垃圾回收,之后再次发起事件
A:sender:ConsoleApplication1.A
---------当前进程名称:ConsoleApplication1-----------
4280
工作集(进程类)121,628.00 KB
工作集 121,628.00 KB
私有工作集 109,672.00 KB
--------------------------------------------------------

4,按下任意键开始注销事件,之后再次垃圾回收
5,垃圾回收完成
---------当前进程名称:ConsoleApplication1-----------
4280
工作集(进程类)19,228.00 KB
工作集 19,228.00 KB
私有工作集 7,272.00 KB
--------------------------------------------------------

View Code
这次的确印证了前面的说明,GC真正回收内存的时机是不确定的。
 
编译器的优化
精简下之前的测试代码,仅初始化事件对象然后就GC回收,看看结果:

getWorkingSet();
A a = new A();
TestInitEvent(a);
 getWorkingSet();

Console.WriteLine("4,按下任意键开始注销事件,之后再次垃圾回收");
Console.ReadKey();
a.DelEvent();
GC.Collect();
Console.WriteLine("5,垃圾回收完成");
getWorkingSet();
Console.ReadKey();

 结果:

---------当前进程名称:ConsoleApplication1-----------
6576
工作集(进程类)10,344.00 KB
工作集 10,344.00 KB
私有工作集 7,240.00 KB
--------------------------------------------------------

---------当前进程名称:ConsoleApplication1-----------
6576
工作集(进程类)121,500.00 KB
工作集 121,500.00 KB
私有工作集 110,292.00 KB
--------------------------------------------------------

4,按下任意键开始注销事件,之后再次垃圾回收
5,垃圾回收完成
---------当前进程名称:ConsoleApplication1-----------
6576
工作集(进程类)19,788.00 KB
工作集 19,788.00 KB
私有工作集 7,900.00 KB
--------------------------------------------------------

符合预期,GC之后内存恢复到正常水平。
将上面的代码稍加修改,仅仅注释掉GC前面的一句代码:a.DelEvent();

getWorkingSet();
A a = new A();
TestInitEvent(a);
 getWorkingSet();

Console.WriteLine("4,按下任意键开始注销事件,之后再次垃圾回收");
Console.ReadKey();
//a.DelEvent();
GC.Collect();
Console.WriteLine("5,垃圾回收完成");
getWorkingSet();
Console.ReadKey();

再看结果:

---------当前进程名称:ConsoleApplication1-----------
4424
工作集(进程类)10,308.00 KB
工作集 10,308.00 KB
私有工作集 7,040.00 KB
--------------------------------------------------------

---------当前进程名称:ConsoleApplication1-----------
4424
工作集(进程类)121,256.00 KB
工作集 121,256.00 KB
私有工作集 7,592.00 KB
--------------------------------------------------------

4,按下任意键开始注销事件,之后再次垃圾回收
5,垃圾回收完成
---------当前进程名称:ConsoleApplication1-----------
4424
工作集(进程类)19,436.00 KB
工作集 19,436.00 KB
私有工作集 7,600.00 KB
--------------------------------------------------------

大跌眼镜:居然没有发生大量内存占用的情况!
看来只有一个可能性:
对象a 在GC回收内存之前,没有操作事件之类的代码,因此可以非常明确对象a 之前的事件代码不再有效,相关的对象b可以在
TestInitEvent(a); 方法调用之后立刻回收,这样就看到了现在的测试结果。
如果不是 Release 编译模式优化,我们来看看在IDE调试或者Debug编译模式运行的结果(前面的代码不做任何修改):

---------当前进程名称:ConsoleApplication1.vshost-----------
8260
工作集(进程类)25,148.00 KB
工作集 25,148.00 KB
私有工作集 9,816.00 KB
--------------------------------------------------------

---------当前进程名称:ConsoleApplication1.vshost-----------
8260
工作集(进程类)136,048.00 KB
工作集 136,048.00 KB
私有工作集 112,888.00 KB
--------------------------------------------------------

4,按下任意键开始注销事件,之后再次垃圾回收
5,垃圾回收完成
---------当前进程名称:ConsoleApplication1.vshost-----------
8260
工作集(进程类)136,692.00 KB
工作集 136,692.00 KB
私有工作集 112,892.00 KB
--------------------------------------------------------

这一次,尽管仍然调用了GC垃圾回收,但实际上根本没有立刻起到效果,内存仍然100多M。
 
最后,我们在发起事件挂钩之后,立即解除事件挂钩,再看下Debug模式下的结果,为此仅仅需要修改下面代码一个地方:

static void TestInitEvent(A a)
var b = new B(100 * 1024 * 1024);
a.ToDoSomething += b.PrintA;
a.ToDoSomething -= b.PrintA;
}

然后看在Debug模式下的执行结果:

---------当前进程名称:ConsoleApplication1.vshost-----------
8652
工作集(进程类)26,344.00 KB
工作集 26,344.00 KB
私有工作集 9,452.00 KB
--------------------------------------------------------

---------当前进程名称:ConsoleApplication1.vshost-----------
8652
工作集(进程类)135,628.00 KB
工作集 135,628.00 KB
私有工作集 10,008.00 KB
--------------------------------------------------------

4,按下任意键开始注销事件,之后再次垃圾回收
5,垃圾回收完成
---------当前进程名称:ConsoleApplication1.vshost-----------
8652
工作集(进程类)33,768.00 KB
工作集 33,768.00 KB
私有工作集 10,008.00 KB
--------------------------------------------------------

符合预期,内存占用量没有增加,所以此时调用GC回收内存都没有意义了。
疑问:
一定需要解除事件挂钩吗?
不一定,如果发起事件的对象生命周期比较短,不是静态对象,不是单例对象,当该对象生命周期结束的时候,GC可以回收该对象,只不过,该对象可能要经过多代才能成功回收,并且每一次回收何时才执行是不确定的,回收的代数越长,那么最后被回收的时间越长。
所以,如果发起事件的对象不是根对象,而是附属于另外一个生命周期很长的对象,不解除事件挂钩,这些处理事件的对象也不能被释放,于是内存泄漏就发生了。
为了避免潜在发生内存泄漏的问题,我们应该养成不使用事件就立刻解除事件挂钩的良好习惯!
需要在程序代码中常常写GC回收内存吗?
不一定,除非你非常清楚要在何时回收内存并且肯定此时GC能够有效工作,比如像本文测试的例子这样,否则,调用GC非但没有效果,可能还会引起副作用,比如引起整个应用程序的暂停业务处理。
总结
使用事件的时候如果不在使用完之后解除事件挂钩,有可能发生内存泄漏,
GC内存回收的时机的确具有不确定性,所以GC不是救命稻草,最佳的做法还是用完事件立即解除事件挂钩。
如果你忘记了这个事情,也请一定不要忘记发布程序的时候,使用Release编译模式!
免责声明:本站部分内容、图片、文字、视频等来自于互联网,仅供大家学习与交流。相关内容如涉嫌侵犯您的知识产权或其他合法权益,请向本站发送有效通知,我们会及时处理。反馈邮箱&&&&。
学生服务号
在线咨询,奖学金返现,名师点评,等你来互动2015年2月 Java大版内专家分月排行榜第二2014年3月 Java大版内专家分月排行榜第二
2014年9月 Java大版内专家分月排行榜第三2014年6月 Java大版内专家分月排行榜第三2014年2月 Java大版内专家分月排行榜第三2013年11月 Java大版内专家分月排行榜第三2013年10月 Java大版内专家分月排行榜第三
匿名用户不能发表回复!|
每天回帖即可获得10分可用分!小技巧:
你还可以输入10000个字符
(Ctrl+Enter)
请遵守CSDN,不得违反国家法律法规。
转载文章请注明出自“CSDN(www.csdn.net)”。如是商业用途请联系原作者。90838人阅读
编程技术(1)
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。memory leak会最终会导致out of memory!内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。&&&& 内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。一个盘子用尽各种方法只能装4个果子,你装了5个,结果掉倒地上不能吃了。这就是溢出!比方说栈,栈满时再做进栈必定产生空间溢出,叫上溢,栈空时再做退栈也产生空间溢出,称为下溢。就是分配的内存不足以放下数据项序列,称为内存溢出.&&& 以发生的方式来分类,内存泄漏可以分为4类:&1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。&2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。&3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。&4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。&从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到&
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:263878次
积分:1459
积分:1459
排名:千里之外
原创:33篇
转载:18篇
评论:41条
(6)(3)(1)(11)(3)(8)(10)(7)(2)android(18)
这是我在网站上看到的,转移了过来,也希望更多人看到,话不多说,直接往下读吧:
对于内存泄漏,我想大家在开发中肯定都遇到过,只不过内存泄漏对我们来说并不是可见的,因为它是在堆中活动,而要想检测程序中是否有内存泄漏的产生,通常我们可以借助LeakCanary、MAT等工具来检测应用程序是否存在内存泄漏,MAT是一款强大的内存分析工具,功能繁多而复杂,而 LeakCanary则是由Square开源的一款轻量第三方内存泄漏检测工具,当它检测到程序中有内存泄漏的产生时,它将以最直观的方式告诉我们该内存泄漏是由谁产生的和该内存泄漏导致谁泄漏了而不能回收,供我们复查。
最近腾讯bugly也推出了三篇关于Android内存泄漏调优的文章,有兴趣的可以看看:
为什么会产生内存泄漏?
当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。
内存泄漏对程序的影响?
内存泄漏是造成应用程序OOM的主要原因之一!我们知道Android系统为每个应用程序分配的内存有限,而当一个应用中产生的内存泄漏比较多时,这就难免会导致应用所需要的内存超过这个系统分配的内存限额,这就造成了内存溢出而导致应用Crash。
Android中常见的内存泄漏汇总
单例造成的内存泄漏
单例模式非常受开发者的喜爱,不过使用的不恰当的话也会造成内存泄漏,由于单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。如下这个典例:
public class AppManager {
private static AppM
private AppManager(Context context) {
this.context =
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:
1、传入的是Application的Context:这将没有任何问题,因为单例的生命周期和Application的一样长
2、传入的是Actity的Context:当这个Context所对应的Activity退出时,由于该Context和Activity的生命周期一样长(Activity间接继承于Context),所以当前Activity退出时它的内存并不会被回收,因为单例对象持有该Activity的引用。
所以正确的单例应该修改为下面这种方式:
public class AppManager {
private static AppM
private AppManager(Context context) {
this.context = context.getApplicationContext();
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
这样不管传入什么Context最终将使用Application的Context,而单例的生命周期和应用的一样长,这样就防止了内存泄漏
非静态内部类创建静态实例造成的内存泄漏
有的时候我们可能会在启动频繁的Activity中,为了避免重复创建相同的数据资源,可能会出现这种写法:
public class MainActivity extends AppCompatActivity {
private static TestResource mResource =
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mManager == null){
mManager = new TestResource();
class TestResource {
这样就在Activity内部创建了一个非静态内部类的单例,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而又使用了该非静态内部类创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。正确的做法为:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请使用ApplicationContext
Handler造成的内存泄漏
Handler的使用造成的内存泄漏问题应该说最为常见了,平时在处理网络任务或者封装一些请求回调等api都应该会借助Handler来处理,对于Handler的使用代码编写一不规范即有可能造成内存泄漏,如下示例:
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
private void loadData(){
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
这种创建Handler的方式会造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏,所以另外一种做法为:
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextV
private static class MyHandler extends Handler {
private WeakReference&Context&
public MyHandler(Context context) {
reference = new WeakReference&&(context);
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText(&&);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象,这样虽然避免了 Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该移除消息队列中的消息,更准确的做法如下:
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextV
private static class MyHandler extends Handler {
private WeakReference&Context&
public MyHandler(Context context) {
reference = new WeakReference&&(context);
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText(&&);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
使用mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有消息和所有的Runnable。当然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();来移除指定的 Runnable和Message。
线程造成的内存泄漏
对于线程造成的内存泄漏,也是平时比较常见的,如下这两个示例可能每个人都这样写过:
//——————test1
new AsyncTask&Void, Void, Void&() {
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
}.execute();
//——————test2
new Thread(new Runnable() {
public void run() {
SystemClock.sleep(10000);
}).start();
上面的异步任务和Runnable都是一个匿名内部类,因此它们对当前Activity都有一个隐式引用。如果Activity在销毁之前,任务还未完成,那么将导致Activity的内存资源无法回收,造成内存泄漏。正确的做法还是使用静态内部类的方式,如下:
static class MyAsyncTask extends AsyncTask&Void, Void, Void& {
private WeakReference&Context& weakR
public MyAsyncTask(Context context) {
weakReference = new WeakReference&&(context);
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
MainActivity activity = (MainActivity) weakReference.get();
if (activity != null) {
static class MyRunnable implements Runnable{
public void run() {
SystemClock.sleep(10000);
//——————
new Thread(new MyRunnable()).start();
new MyAsyncTask(this).execute();
这样就避免了Activity的内存资源泄漏,当然在Activity销毁时候也应该取消相应的任务AsyncTask::cancel(),避免任务在后台执行浪费资源。
资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
1、对于生命周期比Activity长的对象如果需要应该使用ApplicationContext
2、在涉及到Context时先考虑ApplicationContext,当然它并不是万能的,对于有些地方则必须使用Activity的Context,对于Application,Service,Activity三者的Context的应用场景如下:
**其中:**NO1表示Application和Service可以启动一个Activity,不过需要创建一个新的task任务队列。而对于Dialog而言,只有在Activity中才能创建
3、对于需要在静态内部类中使用非静态外部成员变量(如:Context、View ),可以在静态内部类中使用弱引用来引用外部类的变量来避免内存泄漏
4、对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:
将内部类改为静态内部类静态内部类中使用弱引用来引用外部类的成员变量
5、对于不再需要使用的对象,显示的将其赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null6、保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:6913次
排名:千里之外
原创:24篇
<font color="#FF053
(2)(1)(2)(1)(2)(4)(1)(4)(4)(4)(5)

我要回帖

更多关于 vb.net 抛出异常 的文章

 

随机推荐