如何判断系统中存在Handle未胰岛素释放试验方法及结果判断导致的内存泄漏

发表于&&0条回复&&19次阅读&&&&&&
&&筑龙币+100
Java是垃圾回收语言的一种,其优点是开发者无需特意管理内存分配,降低了应用由于局部故障(segmentation fault)导致崩溃,同时防止未释放的内存把堆栈(heap)挤爆的可能,所以写出来的代码更为安全。不幸的是,在Java中仍存在很多容易导致内存泄漏的逻辑可能(logical leak)。如果不小心,你的Android应用很容易浪费掉未释放的内存,最终导致内存用光的错误抛出(out-of-memory,OOM)。一般内存泄漏(traditional memory leak)的原因是:当该对象的所有引用都已经释放了,对象仍未被释放。(译者注:Cursor忘记关闭等)逻辑内存泄漏(logical memory leak)的原因是:当应用不再需要这个对象,当仍未释放该对象的所有引用。如果持有对象的强引用,垃圾回收器是无法在内存中回收这个对象。在Android开发中,最容易引发的内存泄漏问题的是Context。比如Activity的Context,就包含大量的内存引用,例如View Hierarchies和其他资源。一旦泄漏了Context,也意味泄漏它指向的所有对象。Android机器内存有限,太多的内存泄漏容易导致OOM。检测逻辑内存泄漏需要主观判断,特别是对象的生命周期并不清晰。幸运的是,Activity有着明确的生命周期,很容易发现泄漏的原因。Activity.onDestroy()被视为Activity生命的结束,程序上来看,它应该被销毁了,或者Android系统需要回收这些内存(译者注:当内存不够时,Android会回收看不见的Activity)。如果这个方法执行完,在堆栈中仍存在持有该Activity的强引用,垃圾回收器就无法把它标记成已回收的内存,而我们本来目的就是要回收它!结果就是Activity存活在它的生命周期之外。Activity是重量级对象,应该让Android系统来处理它。然而,逻辑内存泄漏总是在不经意间发生。(译者注:曾经试过一个Activity导致20M内存泄漏)。在Android中,导致潜在内存泄漏的陷阱不外乎两种:全局进程(process-global)的static变量。这个无视应用的状态,持有Activity的强引用的怪物。活在Activity生命周期之外的线程。没有清空对Activity的强引用。检查一下你有没有遇到下列的情况。Static Activities在类中定义了静态Activity变量,把当前运行的Activity实例赋值于这个静态变量。如果这个静态变量在Activity生命周期结束后没有清空,就导致内存泄漏。因为static变量是贯穿这个应用的生命周期的,所以被泄漏的Activity就会一直存在于应用的进程中,不会被垃圾回收器回收。static&Activity&&&&&&&void&setStaticActivity()&{&&&&&&&activity&=&this;&&&&&}&&&&&&View&saButton&=&findViewById(R.id.sa_button);&&&&&saButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&setStaticActivity();&&&&&&&&&nextActivity();&&&&&&&}&&&&&});&Memory Leak 1 - Static ActivityStatic Views类似的情况会发生在单例模式中,如果Activity经常被用到,那么在内存中保存一个实例是很实用的。正如之前所述,强制延长Activity的生命周期是相当危险而且不必要的,无论如何都不能这样做。特殊情况:如果一个View初始化耗费大量资源,而且在一个Activity生命周期内保持不变,那可以把它变成static,加载到视图树上(View Hierachy),像这样,当Activity被销毁时,应当释放资源。(译者注:示例代码中并没有释放内存,把这个static view置null即可,但是还是不建议用这个static view的方法)static&&&&&&&void&setStaticView()&{&&&&&&&view&=&findViewById(R.id.sv_button);&&&&&}&&&&&&View&svButton&=&findViewById(R.id.sv_button);&&&&&svButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&setStaticView();&&&&&&&&&nextActivity();&&&&&&&}&&&&&});&Memory Leak 2 - Static ViewInner Classes继续,假设Activity中有个内部类,这样做可以提高可读性和封装性。将如我们创建一个内部类,而且持有一个静态变量的引用,恭喜,内存泄漏就离你不远了(译者注:销毁的时候置空,嗯)。private&static&Object&&&&&&&&&&void&createInnerClass()&{&&&&&&&&&class&InnerClass&{&&&&&&&&&}&&&&&&&&&inner&=&new&InnerClass();&&&&&}&&&&&&View&icButton&=&findViewById(R.id.ic_button);&&&&&icButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&&&createInnerClass();&&&&&&&&&&&&&nextActivity();&&&&&&&&&}&&&&&});&Memory Leak 3 - Inner Class内部类的优势之一就是可以访问外部类,不幸的是,导致内存泄漏的原因,就是内部类持有外部类实例的强引用。Anonymous Classes相似地,匿名类也维护了外部类的引用。所以内存泄漏很容易发生,当你在Activity中定义了匿名的AsyncTsk。当异步任务在后台执行耗时任务期间,Activity不幸被销毁了(译者注:用户退出,系统回收),这个被AsyncTask持有的Activity实例就不会被垃圾回收器回收,直到异步任务结束。void&startAsyncTask()&{&&&&&&&&&new&AsyncTask&Void,&Void,&Void&()&{&&&&&&&&&&&&&@Override&protected&Void&doInBackground(Void...&params)&{&&&&&&&&&&&&&&&&&while(true);&&&&&&&&&&&&&}&&&&&&&&&}.execute();&&&&&}&&&&&&super.onCreate(savedInstanceState);&&&&&setContentView(R.layout.activity_main);&&&&&View&aicButton&=&findViewById(R.id.at_button);&&&&&aicButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&&&startAsyncTask();&&&&&&&&&&&&&nextActivity();&&&&&&&&&}&&&&&});&Memory Leak 4 - AsyncTaskHandler同样道理,定义匿名的Runnable,用匿名类Handler执行。Runnable内部类会持有外部类的隐式引用,被传递到Handler的消息队列MessageQueue中,在Message消息没有被处理之前,Activity实例不会被销毁了,于是导致内存泄漏。void&createHandler()&{&&&&&&&&&new&Handler()&{&&&&&&&&&&&&&@Override&public&void&handleMessage(Message&message)&{&&&&&&&&&&&&&&&&&super.handleMessage(message);&&&&&&&&&&&&&}&&&&&&&&&}.postDelayed(new&Runnable()&{&&&&&&&&&&&&&@Override&public&void&run()&{&&&&&&&&&&&&&&&&&while(true);&&&&&&&&&&&&&}&&&&&&&&&},&Long.MAX_VALUE&&&&1);&&&&&}&&&&&&&View&hButton&=&findViewById(R.id.h_button);&&&&&hButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&&&createHandler();&&&&&&&&&&&&&nextActivity();&&&&&&&&&}&&&&&});&Memory Leak 5 - HandlerThreads我们再次通过Thread和TimerTask来展现内存泄漏。void&spawnThread()&{&&&&&&&&&new&Thread()&{&&&&&&&&&&&&&@Override&public&void&run()&{&&&&&&&&&&&&&&&&&while(true);&&&&&&&&&&&&&}&&&&&&&&&}.start();&&&&&}&&&&&&View&tButton&=&findViewById(R.id.t_button);&&&&&tButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&spawnThread();&&&&&&&&&&&nextActivity();&&&&&&&}&&&&&});&Memory Leak 6 - ThreadTimerTask只要是匿名类的实例,不管是不是在工作线程,都会持有Activity的引用,导致内存泄漏。oid&scheduleTimer()&{&&&&&&&&&new&Timer().schedule(new&TimerTask()&{&&&&&&&&&&&&&@Override&&&&&&&&&&&&&public&void&run()&{&&&&&&&&&&&&&&&&&while(true);&&&&&&&&&&&&&}&&&&&&&&&},&Long.MAX_VALUE&&&&1);&&&&&}&&&&&&View&ttButton&=&findViewById(R.id.tt_button);&&&&&ttButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&&&scheduleTimer();&&&&&&&&&&&&&nextActivity();&&&&&&&&&}&&&&&});&Memory Leak 7 - TimerTaskSensor Manager最后,通过Context.getSystemService(int name)可以获取系统服务。这些服务工作在各自的进程中,帮助应用处理后台任务,处理硬件交互。如果需要使用这些服务,可以注册监听器,这会导致服务持有了Context的引用,如果在Activity销毁的时候没有注销这些监听器,会导致内存泄漏。void&registerListener()&{&&&&&&&&&&&&&&&&SensorManager&sensorManager&=&(SensorManager)&getSystemService(SENSOR_SERVICE);&&&&&&&&&&&&&&&&Sensor&sensor&=&sensorManager.getDefaultSensor(Sensor.TYPE_ALL);&&&&&&&&&&&&&&&&sensorManager.registerListener(this,&sensor,&SensorManager.SENSOR_DELAY_FASTEST);&&&&&&&&&}&&&&&&&&&&View&smButton&=&findViewById(R.id.sm_button);&&&&&&&&&smButton.setOnClickListener(new&View.OnClickListener()&{&&&&&&&&&&&&&@Override&public&void&onClick(View&v)&{&&&&&&&&&&&&&&&&&registerListener();&&&&&&&&&&&&&&&&&nextActivity();&&&&&&&&&&&&&}&&&&&&&&&});&Memory Leak 8 - Sensor Manager总结看过那么多会导致内存泄漏的例子,容易导致吃光手机的内存使垃圾回收处理更为频发,甚至最坏的情况会导致OOM。垃圾回收的操作是很昂贵的开销,会导致肉眼可见的卡顿。所以,实例化的时候注意持有的引用链,并经常进行内存泄漏检查。原 文:&
Xixmy为【[分享]展柜】增加了20热度值
分享到微信朋友圈
打开微信"扫一扫",扫描上方二维码请点击右上角按钮&,选择&
志同道合的盆友,快来
和大家一起讨论吧~
分类:筑龙家话
俗人谈俗事儿,吃喝玩乐全都拿来
删除理由:
&广告/SPAM
还可以输入&120&字
还可以输入&120&字
他一定是哪里做的不够好,别替他瞒着了,告诉我们吧~
:&400-900-8066你所知道的C++内存泄漏的各种姿势?
突然想到这个问题,上知乎搜了一遍得到的都是些零零散散的。所以,那我先来一个(要求是骗得了编译器,就是能编译通过且可以运行的)SomeClass *foo=new SomeClass(...);
泄露啦~要避免这类的问题可以用智能指针auto foo=std::make_unique&SomeClass&(...);
按投票排序
曾经有人“精简”我的代码,觉得我的所有对象都继承的Object基类没有用,删掉了,然后大面积内存泄露。他自己没查出来,来问我怎么办。后来我发现他使用的类都没有虚析构函数(当然了,因为我在Object写了,其它类就都不需要声明虚析构函数了),然后用智能指针保存基类指针,子类的成员全部没析构,就泄露了。
说一个不算是泄漏,但内存被爆掉的。系统中有各种各样显式或隐式的队列,队列很基本的一个目的就是缓冲。而一旦某个队列的“消费者”由于某种原因变慢了,哪怕是慢了那么一小下,队列里积累的资源就会非常可观。比如某同学在服务器的主逻辑里加了一小段代码,把一些统计数据以 mmap 的方式写入文件,以一定的间隔通知 os 将文件的内容落地。但通知os的方式却是同步等待。平时问题不大,可一到高峰期,延时、内存都会飙上去,挺过高峰期就自动降下来了,挺不过就 oom 了。还有一个和队列相关的。一台可提供 30w QPS 的服务器,在没有限流机制的情况下,一波看起来只有 30w/s 的请求过来就可能把服务器的内存吃光。这里,更重要的原因就是请求的到达模式了。这类问题很容易把人带跑偏,以为是某种极端情况下的内存泄露,valgrind、tcmalloc 一干工具齐上阵,最后毛都查不出来。补充:极限压力下,两台负载、配置等条件相同的服务器,延时却天壤之别,也极有可能是排队造成的。
真是要多少有多少题主说智能指针是吧
std::shared_ptr&B& ptr;
B() { std::cout && "分配资源" && std::endl; }
~B() { std::cout && "释放资源" && std::endl; }
auto ptr = std::make_shared&B&();
ptr-&ptr = ptr;
longjmp作死(当然严格的说这是UB)
T() { std::cout && "分配资源" && std::endl; }
~T() { std::cout && "释放资源" && std::endl; }
jmp_buf jb;
auto f = [&jb]{ T t; longjmp(jb, true); };
if(!setjmp(jb))
C++异常抛过C:C语言:
void c_function(void(*f)(void *))
void *resource = malloc(...);
f(resource);
free(resource);
void cxx_function()
c_function([](void *p)
if(some_thing_happend)
throw some_exception;
这里的c_function不一定是你自己写的,可能是你在C++里使用的某个C库,而它需要一个callback, 而你用一个可能抛异常的C++函数作为这个callback。================日更新================果然装逼是要遭雷劈的,在知乎上告诉别人内存泄漏的姿势,然后自己完美进坑, 呵呵哒然后这是导致泄漏的代码:然后这是导致泄漏的代码:坑就是上面提到的三条之一, 细心的同学自己找找看:-)坑就是上面提到的三条之一, 细心的同学自己找找看:-)顺便安利下抄来的nua(), 表担心, 内存泄漏已经修好了, 有图为证:
比较麻烦的情景是,内存分配的职责在这个模块,释放的职责却不太清晰的情况。举个栗子,如果从网络接收到一块数据,然后根据数据生成了一个对象,再需要把这个对象当成窗口消息post出去,那么这个对象是需要new出来的,然后将指针post出去。
怒答!大型软件工程里总是会有各种各样的内存泄漏,维护起来都是泪。上面说的大部分都能靠umdh找出来,只要你把自己的内存池关掉。析构需要是虚的这是基本功。1.引用计数讨厌的是引用计数,没错COM我就是说你(指微软COM和苹果OC的引用计数,这东西什么时候要加减引用居然是按接口名字和“约定”来的,不是编译期保证的!所有编译期不保证的都是在耍流氓!这种小细节对一个不怎么仔细看书的新人来说就意味着无穷无尽的泄漏和提前析构!当年WPS开始做手机版,先搞的是iOS,结果那几位同学被OC的自动引用计数弄疯了,按他们的说法就是“怎么搞都漏”。于是改做Android。有些同学总是有洁癖,觉得用智能指针不“优雅”,喜欢自己手写add和release。然后QueryInterface这种东西,又可能套很多层if,要是两个人一合作,什么时候合代码冲突又没仔细看……工程大了,免不了对象层次很深,某个业务组来了个新人,觉得太麻烦了存下来一个引用……循环引用是什么鬼!还有就是那个OLE,没错就是肉嵌肉!别人拿着你的实例,你也拿着别人的实例。析构的时候就好玩了。你一套反激活调下来,发现没析构掉,别人还是拿着你的实例。简直就是囧囧哒。一般这么干的Client都是Office或者IE这种大工程,没pdb,没帮助,反向到头都大。至于,为什么要在Office里OLE嵌一个WPS?我也不知道,测试用例有这个……这就算好了,要是Client是国内那些什么鬼OA,简直就是……你说MFC有OLE实现啊,你们为什么不用那个?首先,微软自己就不用;其次程序猿喜欢造轮子所以实现不只那一个;最后,那实现也有Bug……当然现在都2015了还有没有我就不知道了。这东西出了问题就是,谁都放不掉,你又不知道是谁多拿了个引用计数,查起来特麻烦。我的做法是,/Od,在add和release实现里打不中断断点,直接加dbghelp,把this和调用堆栈打出来。然后写个小程序匹配add和release找出哪些add有问题……QueryInterface,正确的做法是,如果你不关心HRESULT的话:do
CComQIPtr&Interface1& interface1Ptr(object);
if (!interface1Ptr)
CComQIPtr&Interface2& interface2Ptr(interface1Ptr);
if (!interface2Ptr)
// your stuff.
} while (false);
另外,COM的聚合模型也是个坑。2.BStr在早期Windows,你的程序要是用了::SysAllocString或者CComBSTR,然后不幸崩溃了。抱歉,这东西漏的可是系统内存。直到重启之前这部分内存就再也回不来啦。3.缓冲这部分不算内存泄漏。但是现象很吓人。当时我们在做某个功能模块,大概2万行代码,是某个版本的重点功能。提测的时候,跑压力测试的同学拿着内存曲线跟以前的版本一对比,直接就来找我。“你这东西内存漏得这么厉害,不能上,赶快改赶快改,后天就发布了!!”我们压力测试是重复加载/卸载某功能模块,然后跑单元测试。我一看曲线,确实涨得好厉害。然后就祭出神器UMDH开跑。几轮下来拿到diff一分析。我们一个内存泄漏都没啊!但是内存还在涨!!重点是,这版本的其他功能都测完了,完全没问题!在看UMDH结果,发现内存都是在协议层分的。于是找到协议层的同学。“你们一直分这么多内存干嘛?”“我们要做缓存来优化性能啊。”“那为什么我都退了,还不把缓存放掉?”“放掉了我怎么优化性能……”“那就是说,我只用一次这功能,一直不关程序,你这也一直光占着这内存不干事咯?”“现在来说,是的……”然后我就散了,让测试同学跟他们扯皮。我们发布需要测试同学发邮件说测试通过才能发。4.GDI句柄我们那工程,用的是自己改过的Qt,漏GDI句柄不会假死什么的,但是会发生一件很好玩的事情:缺字。Qt原来的代码我没仔细看,不知道会不会。我们改过那,会把字体的特定字号,先画到Buffer上,然后缓存起来。下次画就不用调GDI去画了。这个过程需要创建几个GDI对象。如果GDI到Windows上限10000个,就建不出来,自然字就画不上去Buffer了。于是就看到漏字啦。每次看到群里有人喊缺字,就知道又是哪个业务又漏GDI了。至于为什么用Qt写的程序里会漏GDI,因为总是有些人觉得“Qt太笨重了,我们需要很少很轻量的东西,直接建个HWND开搞吧/我们用这个我自己写的宇宙无敌库开搞吧。”5.多进程共享内存池留坑
vector 吃了内存不吐,早年大家都用 swap 这个 trick 解决,现在 C++11 好了,可以直接 shrink_to_fit。
最近好几次掉进去的坑:打开进程句柄:processhandle=OpenProcess(.......)............用完一定要关闭,用完一定要关闭,用完一定要关闭:CloseHandle(processhandle)
说一个malloc内存管理器不释放内存的例子,不算是内存泄露,只是有些malloc实现机制就是这个样子。项目中由于需要自建索引,而索引又需要定时更新,所以就想到双指针切换的办法。一个指针指向一颗有效的索引结构,提供查询,当需要更新索引的时候,新指针先指向新内存,填充新索引,完成之后,切换指针,然后释放老索引空间结果top命令会看到,进程内存用量并没有降低。原因是一旦malloc内存管理器向内核申请整块空间之后,就会挂到自身维护的一个链表里供后续使用,但是即使上层手动调用free,也只是把用过的内存重新挂到链表里而已,并没有真正归还给内核,所以内存并没有真正被释放…
同一个对象被两个以上的指针引用时,都有可能内存泄漏。函数内分配内存,未处理传出指针时,都有可能内存泄漏。
我也说一个简单的xxx* p = new xxx[1000];...bla,bla,bla...还有其它的花式泄露内存的。例如new完了抛异常什么的。effective c++有一节专门讲避免资源泄露的
已有帐号?
无法登录?
社交帐号登录Android应用程序如何避免内存泄漏以及如何检查泄漏原因
Android应用程序如何避免内存泄漏以及如何检查泄漏原因
[摘要: Android 运用顺序若何幸免内存泄露以 及若何检讨泄露缘由 Android 的运用顺序开辟应用的 Java 说话。 Java 说话的 GC 机造使得正在堆上分 配内存以后无需再脚动的开释内存,而是守候渣滓]
应用程序如何避免内存泄漏以
及如何检查泄漏原因
的应用程序开发使用的
机制使得在堆上分
配内存之后无需再手动的释放内存,而是等待垃圾收集器来收集无用的对象以
回收它们占用的内存。同时在
的进程管理机制中每一个单独的应用程
序在启动时都会创建一个新的
进程来运行该程序,应用程序在运行中分
配的内存也会在该应用程序退出时随着进程的销毁而释放,所以
内存管理给开发人员造成的负担较轻。但应用程序还需要在内存使用上注意不
要使应用程序占用大量内存,原因有如下两点:
应用程序占用的内存越少,
可以同时放入内存程序就越多,用
户切换这些不同的程序所消耗的时间就越少,体验就越流畅。
如果应用程序在消耗光了所有的可用堆空间
),那么再试图
在堆上分配新对象时就会引起
OOM(Out&Of&Memory&Error)
异常,此时应
用程序就会崩溃退出。
所以在编写
应用程序时,仍然需要对应用程序中内存的分配和使用多
加注意,特别是在存在后台线程、使用图片作为背景、在异步任务或者后台线
上下文对象的情况下,要注意避免出现对
等类的对象长期持有无用的
,否则就会造成被引用的对象
时回收,而是长期占用堆空间,此时就发生了内存泄漏。
引用造成的泄漏
下面介绍一下
开发文档中
(Avoiding&Memory&Leak)
的一个内存泄漏的
例子,该例子说明了
应用程序中会引起内存泄漏的常见原因:长期保
对象的引用。在
应用程序中,很多操作都用到了
对象,但是大多数都是用来加载和访问资源的。这就是为什么所有的
显示控件都需要一个
对象作为构造方法的参数。在
中通常可以使用两种
Application
。当类或方法需
对象的时候常见的作法是使用第一个作为
参数。但这就意
对象对整个
保持引用,因此也就保持对
东西的引用,也就是整个
结构和它所有的资源都无法被及时的回收,而且
的长期引用是比较隐蔽的。
&@Override&&&
&protected&void&onCreate(Bundle&state)&{&&&
&&&super.onCreate(state);&&&
&&&TextView&label&=&new&TextView(this);&&&
&&&label.setText(&Leaks&are&bad&);&&&
&&&setContentView(label);&&&
当屏幕方向改变时,
系统默认作法是会销毁当前的
建一个新的
,这个新的
会显示刚才的状态。在这样做的过
系统会重新加载
用到的资源。现在假设的应用程序中有一个
类型的图片,每次旋转时都重新加载图片所用的时间较多。为
了提高屏幕旋转时
的创建速度,最简单的方法是用静态变量的方法。
&private&static&Drawable&sB&&&
&@Override&&&
&protected&void&onCreate(Bundle&state)&{&&&
&&&super.onCreate(state);&&&
&&&TextView&label&=&new&TextView(this);&&&
&&&label.setText(&Leaks&are&bad&);&&&
&&&if&(sBackground&==&null)&{&&&
&&&&&sBackground&=&getDrawable(R.drawable.large_bitmap);&&&
&&&label.setBackgroundDrawable(sBackground);&&&
&&&setContentView(label);&&&
这样的代码执行起来是快速的,但同时是错误的:这样写会一直保持着对
的引用。当一个
对象附属于一个
对象的一个回调(引用)。在上面的代码片段中,就意味着
存在着引用的关系,而
自己持有了对
对象)的引用,这个
又引用了相当多的东西。
有两种简单的方法可以避免由引用
对象造成的内存泄露。首先第一个
方法是避免
对象超出它的作用范围。上面的例子展示了静态引用的情
况,但是在类的内部,隐式的引用外部的类同样的危险。第二种方法是,使用
Application
对象。这个
对象会随着应用程序的存在而存在,而不依
的生命周期。如果你打算对
对象保持一个长期的引用,
请记住这个
application
对象。通过调用
Context.getApplicationContext()&
&Activity.getApplication().
方法,你可以很容易的得到这个对象。
非静态内部类引起的内存泄漏
语言中可以在一个类的内部定义
inner&class
,这在很多时候减少了工作
量,而且使代码更加紧凑,但是由于非静态的
inner&class
对象中包含了产生
inner&class
的包围类的引用
环境中也会可能会因为对
包围类的长期引用而造成内存泄漏。
我们修改上一个例子的代码,将
sBackground
由静态成员变量改为普通成员变
量,这样就消除了该静态对象持有
的引用所造成的泄漏。之后我们在
类中定一个非静态的内部类
,并且对该
innerInstance
。具体代码如下:
&public&class&MemoryLeak&extends&Activity&{&
&&&class&Inner&{&
&&&&&int&someM&
&&&private&static&Inner&innerI&&
&&&private&Drawable&sB&
&&&/**&Called&when&the&activity&is&first&created.&*/&
&&&@Override&
&&&public&void&onCreate(Bundle&savedInstanceState)&{&
&&&&&super.onCreate(savedInstanceState);&
&&&&&TextView&label&=&new&TextView(this);&
&&&&&label.setText(&Leaks&are&bad&);&
&&&&&sBackground&=&getResources().getDrawable(R.drawable.pic1);&
&&&&&if&(innerInstance&==&null)&{&
&&&&&&&innerInstance&=&new&Inner();&
&&&&&label.setBackgroundDrawable(sBackground);&
&&&&&setContentView(label);&
上面例子代码在旋屏时同样会引起内存泄漏,原因是
innerInstance
构造时会持有当时
的引用。当旋屏引起
重新被构造,新的
onCreate()
函数时,作为
类的静态成员变量
innerInstance
,所以并不会被重新构造,但它还保留着之前
对象的引用,造成了该
对象无法被释放。在上面的例子中
也就意味着至少一个
对象占用的内存没有被及时回收。对于上述两种
常见的内存泄漏,我们在开发中需要记住以下
的持久引用,对
的引用应该和
同的生命周期,尽量使用
application
取资源,或者构造
如果不能控制非静态的内部类的生命周期,尽量在
中避免有非
静态的内部类。同时在
中使用静态的类时如果需要引用
WeakReference
弱引用来引用
后台线程操作引起的泄漏
开发中常常需要在后台线程中执行查询或者后台操作。
身也提供了一些常见的异步任务类来简化多线程编程,譬如我们可以使用
AsyncQueryHandler
来执行后台查询任务,使用
来执行异步的后台
任务。这些
前缀的异步任务类在执行时都会在
线程之外新开了一个线
程来执行。我们在使用这些异步任务类的时,需要注意之前提到的不要将这些
异步任务类的派生类定义为
non-static&inner&class
以避免之前
inner&class&
引起的内存泄漏。除此之外,还需要注意不要在这些异步
任务类中持有
的强引用,而应该采用
WeakReference
的引用。这样就不会由于后台线程在执行时
对象无法及时
释放。例如在联系人列表
(ContactsListActivity)
中查询联系人的
QueryHandler
类,它作为
AsyncQueryHandler
的派生类主要用来执行查询联系
人数据库的操作,联系人较多的话查询操作用时就会较长,所以采用
AsyncQueryHander
在后台线程中执行查询。在该类的定义中就通过以下两点来
消除内存泄漏:
QueryHander
static&inner&class
non-static&inner&
对象持有的
ContactsListAcitivty
对象的引用
WeakReference
ContactsListAcitivity
这样就保证了该后台查询线程不会持用
ContactsListActivity
的强引用,从而
保证了即使后台查询线程正在执行的情况下,
ContactsListAcitivity
键或者旋转屏幕时也能够被
&private&static&class&QueryHandler&extends&AsyncQueryHandler&{&
&&&protected&final&WeakReference&ContactsListActivity&&mA&
&&&protected&boolean&mLoadingJoinSuggestions&=&&
&&&@Override&
&&&protected&void&onQueryComplete(int&token,&Object&cookie,&Cursor&
cursor)&{&
&&&&&final&ContactsListActivity&activity&=&mActivity.get();&
&&&&&if&(activity&!=&null&&&&!activity.isFinishing())&{&
与我们正常使用的强引用类型不同
,上述示例代码中
WeakReference
ContactsListActivity
的引用计数,在使用
WeakReference
变量时,也需要判断其
返回的结果是否为
ContactsListActivity
对象是否还存在。在
语言中,有以下四种引用类
Strong&Reference(
:通常我们编写的代码都是
Strong&Ref
此对应的是强可达性,只有去掉强可达,对象才被回收。
Soft&Reference&(
:对应软可达性,只要有足够的内存,就一直
保持对象,直到发现内存吃紧且没有
Strong&Ref
时才回收对象。一般可
用来实现缓存,通过
java.lang.ref.SoftReference
Weak&Reference&(
更弱,当发现不存在
时,立刻回收对象而不必等到内存吃紧的时候。通过
java.lang.ref.WeakReference
java.util.WeakHashMap
Phantom&Reference&(
:根本不会在内存中保持任何对象,你只
Phantom&Ref
本身。一般用于在进入
finalize()
方法后进行特殊
的清理过程,通过
java.lang.ref.PhantomReference
关于这四种不同的类型,可以查看
垃圾回收机制与引用类型
一文中的详细说
执行后台任务时,也要注意将其定义为
static&inner&
,并且在其
doInBackground()
函数中检查
isCancelled()
从而可以尽快的退出后台线程。
开发手册中说明如果需要在后台执行
任务时,尽量采用
并按照其规范重载相应的钩子函数。但
中传统上使用
来开起新的线程并执行后台任务的作法也是
可以的,只是
并不提倡这样作,特别是在应用程序中通过
台执行耗时较长的任务。例如下面的示例代码:
&new&Thread()&{&
&@Override&
&&&public&void&run()&{&
&&&&&//&do&something&&
&Runnable&task&=&new&Runnable()&{&
&&&public&void&run()&{&
&&&&&//&do&something&&
&Thread&t&=&new&Thread(task);&
&t.start();&
上述直接使用
类创建线程来执行后台任务有很大的局限性,
界面的更新必须是在
主线程中进行,所以如果后台线程中
界面中的元素(例如更新进度条)就必须通过
息,反而增加了代码的复杂性。如果使用标准的
,则只需要实现
onProgressUpdate()
钩子函数,就可以在后台任务执行的同时动态的更新
面。由于是
onProgressUpdate()
钩子函数是在
主线程中执行,在该钩子函
数中可以安全的操用
界面。而且
&onCancelled()
onPostExecute()
钩子函数中操用
界面元素以通知用户后台任务的执行结
果,上述直接采用
类的方法仍然必须通过
才能更新用户界面。
线程之间通过
通信引起的内存泄漏
中线程之间进行通信时最常用的作法是通过接收消息的目标线程所持
对象来创建
对象,然后再向目标线程发送该
目标线程中
handleMessage()
时会根据相应
应不同功能。另外一种作法是通过
对象向目标线程直接发送
对象来执行该
对象中不同的功能代码。在通过
进行通信时如
果不注意,也很有可能引起内存泄漏。例如线程之间通过
对象进行通讯的例子。
&private&Drawable&sB&
&private&Message&&
&@Override&
&protected&void&onCreate(Bundle&savedInstanceState)&{&
& &super.onCreate(savedInstanceState);&
&&&TextView&label&=&new&TextView(this);&
&&&label.setText(&Leaks&are&bad&);&
&&&sBackground&=&getResources().getDrawable(R.drawable.pic1);&
&&&label.setBackgroundDrawable(sBackground);&
&&&setContentView(label);&
&&&OtherThread&t&=&new&OtherThread(&other&thread&);&
&&&t.start();&
&&&Handler&h&=&new&Handler(t.getLooper())&{&
&&&&&public&void&handleMessage(Message&msg)&{&
&&&&&&&Log.d(TAG,&&msg&is&&&+&msg.toString());&
&&&&&&&Log.d(TAG,&&Thread&&&+&Thread.currentThread().getId()&
&&&&&&&&&+&&&handled&message&);&
&&&&&msg&=&h.obtainMessage(101);&
&&&&&h.sendMessage(msg);&
&&&&&TimeUnit.SECONDS.sleep(3);&
&&&&&h.post(new&Runnable()&{&
&&&&&&&public&void&run()&{&
&&&&&&&&&Log.d(TAG,&&hahaha&);&
&&&&&&&&&try&{&
&&&&&&&&&&&TimeUnit.SECONDS.sleep(30);&
&&&&&&&&&}&catch&(InterruptedException&e)&{&
&&&&&&&&&&&//&TODO&Auto-generated&catch&block&
&&&&&&&&&&&e.printStackTrace();&
&&&&&&&&&}&
&&&&&&&&&Log.d(TAG,&&Thread&&&+&Thread.currentThread().getId()&
&&&&&&&&&&&&&+&&&waken&up&);&
&&&}&catch&(InterruptedException&e)&{&
&&&&&//&TODO&Auto-generated&catch&block&
&&&&&e.printStackTrace();&
&private&static&class&OtherThread&extends&HandlerThread&{&
&&&OtherThread(String&name)&{&
&&&super(name);&
上述例子代码模拟了
线程同另一线程
OtherThread
讯的情景,
线程会通过发送
对象来委托
OtherThread
来执行不同的任务,这时
代表的任务是轻量级的任务,
其执行速度很快,这里就是打出两行
对象发送的任务是重
量级的任务,这个任务会执行
秒。在上述存在两处会引起内存泄漏的地方,
成员变量在将
OtherThread
是指向这个已发送的
OtherThread
能够很快的处理这一
对象任务并随后释放对该
的引用。但是在
线程中,在发送完该
后会长时间的执行其它任务
这里用休眠
秒代表执行其它任务
秒过程中由于
成员变量仍然是指向之前已经完成的
这就使得该
对象迟迟不能释放。如果
带有大量的信息的话,
那么其占用的内存就一直不能被
。另一处会引起内存泄漏的地方是发送给
OtherThread
的是非靜态的
Runnable&inner&class
的引用。由于该
执行的任务比较重,所以在
OtherThread
OtherThread
任务时用户旋转了手机屏幕,那么旋屏后已经无用的
秒,也就是
中所有占用内存在
秒中无法被
修正上述内存泄漏的问题,需要对例子中的代码做如下修改:
sendMessage
完成之后显示的将
成员变量置为
&msg&=&h.obtainMessage(101);&
&h.sendMessage(msg);&
类型的内部类,或者不用
进行线程间通讯而是将
的中的功能代码转移到
OtherThread
handlerMessage
OtherThread
对象来进行通讯。
其它常见的引起内存泄漏的原因
ListAdapter
函数中没有使用参数中的
convertView.&
public&View&getView(intposition,&
ViewconvertView,ViewGroupparent)&
提供每一个
对象。初始时
&BaseAdapter&
中根据当前
的屏幕布局实例化一定数量的
对象缓存起来。
当向上滚动
原先位于最上面的
对象会被回收
然后被用来构造新出现的最下面的
。这个构造过程就是由
方法完成的
,getView()
&View&convertView
就是被缓存起来的
化时缓存中没有
&convertView
。由此可以看出
果我们不去使用
convertView,
而是每次都在
&getView()
中重新实例化一
即浪费资源也浪费时间
也会使得内存占用越来越
大,直到最后引发
还没有实现
concurrent&
上下滚动时就会由于虚拟机执行
的滚动,这时给用户的感觉就是界面不够流畅。
对象不在使用时调用
&recycle()
释放内存,有时我们会手工的操
对象比较占内存
当它不在被使用
Bitmap.recycle()
方法回收此对象的像素所占用的内
查询得到的
未能及时关闭。
工具的使用
如果在应用程序中发生了
异常导致的错误,我们需要首先定位一下是否是
由于自身程序的原因。方法是重新打开之前出错退出的应用程序,然后在控制
台中运行:
&$adb&shell&
然后在打开的
终端中执行:
&#procrank&
就会输出类似下面的列表:
PID&&&&&&Vss&&&&&&Rss&&&&&&Pss&&&&&&Uss&cmdline&&&&&&&&&&&&&&&&&&&&&&&&&&&
935&&&68544K&&&42160K&&&19570K&&&15840K&system_server&&&&&&&&&&&&&&&&&&&&&
1002&&&37600K&&&35124K&&&14912K&&&12804K&oms.home&&&&&&&&&&&&&&&&&&&&&&&&&&
1221&&&33828K&&&33828K&&&12259K&&&&9440K&com.android.phone&&&&&&&&&&&&&&&&&
2537&&&31916K&&&31916K&&&11510K&&&&9324K&com.android.Browser&&&&&&&&&&&&&&&
2956&&&28768K&&&28768K&&&&9034K&&&&7152K&com.hiapk.market&&&&&&&&&&&&&&&&&&
...&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
854&&&&&268K&&&&&268K&&&&&&89K&&&&&&84K&/system/bin/servicemanager&&&&&&&&
859&&&&&444K&&&&&444K&&&&&&86K&&&&&&28K&/system/bin/sdm&&&&&&&&&&&&&&&&&&&
920&&&&&268K&&&&&268K&&&&&&85K&&&&&&80K&/system/bin/cbmon&&&&&&&&&&&&&&&&&
883&&&&&404K&&&&&404K&&&&&&84K&&&&&&28K&/system/bin/sdm&&&&&&&&&&&&&&&&&&&
857&&&&&276K&&&&&276K&&&&&&81K&&&&&&76K&/system/bin/debuggerd&
其中最左一例是当前正在运行所有进程的
,接下来的四个字段分别为
&Virtual&Set&Size&
虚拟耗用内存(包含共享库占用的内存)
Resident&Set&Size&
实际使用物理内存(包含共享库占用的内存,如果有两个
进程使用一个占用的空间为
,那么这两个进程的
就会分别增加
&Proportional&Set&Size&
实际使用的物理内存(比例分配共享库
占用的内存,如果有两个进程使用一个占用的空间为
,那么这两个进
就会分别增加
&Unique&Set&Size&
进程独自占用的物理内
存(不包含共享库占用的内存)在上面列表中找到之前应用程序所对应的进程
后,再重复之前引起
的操作,如果发现该进程使用的内存不断的增加
段),那么该进程对应的应用程序就很可能存在内存泄漏。
步定位了内存泄漏之后,可以通过
插件来分析一下该进程中内
存使用情况,定位一下内存泄漏的原因。具体的作法如下:
将开发机通过
连接至电脑,或者打开
上的虚拟机,编译应用程序
的最新版本,之后
到开发机上。
列表中点选嫌疑应用程序的
进程,再点击
视图界面中最上方一排图标中的“Update&Heap”
图标。再点击
视图中的“Cause&GC”按钮,此时虚拟机会执行一次
,来清除不被引用的对象。
执行之前引起
错误的操作,在操作时观察
一行占用内存的大小是否一直增长,在操作时可以随时点击
视图界面中最上方一排图标中的“Dump&HPROF&file”按钮来产
生内存使用情况快照。
如果应用程序所占用的空间一直增长,而且中间的
操作也无法减少内
存占用,或者应用程序在操作过程中引起了
,这时就需要分析之前
视图的饼状图中查看当前内存的占用情况,通常
占用大量空间,但这是正常的。点击占用了大量内存的可疑对象,再选
择“path&to&GC&root”,查看哪些对象持有对该可疑对象的引用造成其
。或者切换到
dominator&tree
视图中,查看按占用内存从大
到小排序的对象列表中是否有可疑的对象,如果有的话再右键点击该对
象,选择“Path&to&GC&root”,选择“Execulde&weak/soft&
references”,查看是哪些对象持有对该可疑对象的引用。
如果已经定位了是哪一个类的对象引起的内存泄漏,但是需要知道关于
这些对象更多的信息,可以使用
查询语言来查询
中各个对象的其它信息来更准确的定位内存泄漏的原因。例如我们可以
界面中输入如下
&SELECT&*&FROM&com.test.MemoryLeak.MemoryLeak&
再点击界面工具栏中的
运行按钮来执行
语句,此语句执行结果中就会列如
MemoryLeak
类的对象,我们再对列表中的对象执行“Path&to&GC&
root”&来查询是哪些对象让其无法释放。如果
MemoryLeak
中还有一个成员变
来标识该对象的
,我们可以通过如下
语句来查询各个对象的
&SELECT&s.mId&FROM&com.test.MemoryLeak.MemoryLeak&s&
语句可以有更多方法来定位内存泄漏的原因,关于
的详细文档,
帮助文件中的
Memory&Analyzer
一节中的说明。
ABCDEFGHIJKLMNabcdefghijklmn!@#$%^&&*()_+.一三五七九贰肆陆扒拾,。青玉案元夕东风夜放花千树更吹落星如雨宝马雕车香满路凤箫声动玉壶光转一夜鱼龙舞蛾儿雪柳黄金缕笑语盈盈暗香去众里寻他千百度暮然回首那人却在灯火阑珊
感谢关注 Ithao123Android频道,是专门为互联网人打造的学习交流平台,全面满足互联网人工作与学习需求,更多互联网资讯尽在 IThao123!
Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码中解脱出来;它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁、富于表达力。
Hadoop是一个由Apache基金会所开发的分布式系统基础架构。
用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。
Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。
Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。
产品设计是互联网产品经理的核心能力,一个好的产品经理一定在产品设计方面有扎实的功底,本专题将从互联网产品设计的几个方面谈谈产品设计
随着国内互联网的发展,产品经理岗位需求大幅增加,在国内,从事产品工作的大部分岗位为产品经理,其实现实中,很多从事产品工作的岗位是不能称为产品经理,主要原因是对产品经理的职责不明确,那产品经理的职责有哪些,本专题将详细介绍产品经理的主要职责
IThao123周刊}

我要回帖

更多关于 胰岛素释放试验方法及结果判断 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信