Android内存管理(1)WRANGLING DALVIK: MEMORY MANAGEMENT IN ANDROID PART 1
from :
http://www.raizlabs.com/dev/2014/03/wrangling-dalvik-memory-management-in-android-part-1-of-2/
WRANGLING DALVIK: MEMORY MANAGEMENT IN ANDROID (PART 1 OF 2)
Posted MARCH 4, 2014 by JOE MAHON
Update: Part 2 is now up! Check it out!
There are some people who believe this myth that you don’t need to worry about managing memory while writing Android applications. It’s a self-contained Java environment, right? What’s the worst that could happen?
Well, it’s true – the Android OS, through the Dalvik runtime1 (now being superceded in some cases with ART), doesn’t have to worry about your app crashing the whole system due to poor memory management. But, alas, that doesn’t mean that your app doesn’t have to deal with managing its memory: Dalvik will be happy to kick you out of execution if you screw up your heap allocation, and your users will start leaving negative reviews about all the crashes they’re getting.
By the way, what we’re looking for throughout this exploration of potential memory problems is a crash called an OutOfMemoryError
(more notoriously and affectionately known as anOOM
): thrown by the application when you try to allocate something past the capacity of the heap. It’s important to note that this can happen at any time, caused by any object, so that doesn’t provide a very good reference of what’s actually the cause of the crash (probably something you’ve “leaked” into memory earlier).
Simply, you’re going to have to face it and trace it back to its source. But where do you start?
LESSON #1: YOU’RE NOT GOING TO FIND IT BY ACCIDENT
Don’t trust your own test devices to fail in all of the remarkably innovative ways your users’ devices will manage to fail.
Here are several tips:
- Understand potential sources of issues: if you know what not to do, you never need to know how to fix it.
- Plan for a lot more QA than you ever expected.
- Very thorough unit testing.
- Run analyses on builds before shipping (we’ll cover this next week, in Part 2).
- Utilize prayer, lucky rabbit feet, or indomitable willpower in the face of the inevitable flood of negative reviews when you ignore this issue.
The best way to effectively pass timely, event-driven data around in Android ecosystems can be debated without end. And, in fact, it often is. But, that’s not the purpose of this post.
One method is attaching an implementation of an interface definition to the object sending the data, and holding that implementation within the receiver object.
At Raizlabs, one of our go-to simple-syntax elements is an interface called EventListener
:
public interface EventListener<EventResponseType> {
public void onEvent(EventResponseType response);
}
EventListener
might be used to receive data from a separate object, like an Activity class which wants to get notifications when a given Object is updated successfully:
public void onResume() {
super.onResume(); SomeObject object = new SomeObject(); object.setSuccessListener(new EventListener<Boolean>() {
public void onEvent(Boolean response) {
Log.d(TAG_NAME, "Valid response? "+response);
}
}); SomeObjectManager.getSingleton().addObject(object);
}
And, in SomeObject, we might see the following, indicating that our data has been saved successfully:
public void saveData(Data newData) {
this.data = newData;
this.successListener.onEvent(true);
}
Which would notify the EventListener instance that we created in the Activity’s onResume
method that the event has successfully been completed.
Now, for those well-seasoned memory wranglers, seeing the immediate danger of this kind of pattern must be super easy. But, for the rest of us, we’re going to have to keep an eye out for problems like these. As it turns out, we just leaked an Activity. And most Android devices don’t need many Activities before you hit an OOM
.
LESSON #2: STALK YOUR REFERENCES
You’re going to have to keep an eye on what you reference in the course of developing an Android app. For iOS developers reading this, you don’t have to worry about retain cycles, since Dalvik’s GarbageCollector will parse the entire map of referenced objects. But, you may accidentally hold strong references to objects you no longer need, and in that case, the GarbageCollector will pass over those unneeded objects. For example, a Context that is no longer on screen, or a bitmap that won’t be displayed again for a while).
Anytime you find yourself creating a reference (that is, any time you assign a variable to an Object), think about what you might need to do to prevent a memory leak.
If the reference is to an object that is instantiated inside your own class, you usually2 don’t have to worry about it. That is, unless you’re sharing strong references with a different object. If that’s the case, then you must manage their references more manually.
For instance, in the example above: we’ve attached a reference to our Activity instance to some object, presumably persistent, and in a manager somewhere. The point is, the Activity doesn’t know that the lifespan of SomeObject
will end when the instance of the Activity ends. If that object remains in memory, it will hold that Activity in memory as well, even after the Activity has been visually destroyed (a user going back, or rotating the device, or some other lifecycle-ending event). So, what we need to make sure to do, is remove that reference inonDestroy()
or similar end-of-lifecycle-method:
public void onDestroy() {
super.onDestroy(); SomeObject objectFromBefore = SomeObjectManager.getSingleton().getOurObject();
objectFromBefore.setSuccessListener(null);
}
LESSON #3: SURVIVAL OF THE FITTEST REFERENCES
Android contains a few different possible types of references. Each level of strength indicates how the system’s GarbageCollector will interact with that Object.
The reference you’re inevitably used to using is a Strong reference. This is what you get when you do:
String myVariable = new String("Hello world");
A strong reference means the GarbageCollector will ignore the instantiated object “Hello World” as long as the “pointer” myVariable references it. Once you set myVariable = null;
, that String can be swept up out of memory at any time. (Not that it matters anymore, since you no longer have a reference to that string! You just have a reference to null
).
The rest of the reference types are actually a part of the SDK, and they are all created in the same general way. We’ll use a SoftReference to demonstrate:
String myStrongVariable = "Hello World";
SoftReference<String> myReference = new SoftReference<String>(myStrongVariable);
A Reference object will give you access to that “Hello World” string, but at any point the class could decide to reassign myStrongVariable
, like so:
myStrongVariable = "Your string is in danger";
Now that object we have a Reference to, “Hello World”, might be GarbageCollected at any time. You can try to access it:
String testString = myReference().get();
But you can’t be sure testString
is not null
. So, how do each of the non-Strong reference types stack up?3
- SoftReference: if there are only SoftReferences to an Object, the reference will generally be held on to as long as there’s memory for it to fit.
- WeakReference: if there are only WeakReferences to an object, it will be purged from memory at the next GarbageCollection cycle.
- PhantomReference: the weakest and most enigmatic reference, that
get()
method will always returnnull
– you can never access the Object it references (even when the Object still exists, and has other references). Realistically, you should probably never be using these.4
…With great knowledge comes great responsibility.
Just because you now know the difference between these references, doesn’t mean you should be throwing them around as a solution to memory management without a very good reason.
The use of the Reference classes is usually a sign of a dirty design pattern. The first thing most people think of when they learn about SoftReference is, “Oh, what a good idea for designing a cache system!” But, while a decent idea, that’s an official Android no-no.
LESSON #4: “THERE IS NO GARBAGECOLLECTOR, NEO”
Occasionally, I’ll see a memory-related StackOverflow answer that suggests, as a production-environment solution, the line:
System.gc();
And when this happens, I cry myself to sleep.
The Android GarbageCollector is not some pet that you should whistle for when you want it to come play. It’s slow, and heavy-handed, and totally and completely capable of doing a great job without your input.
When an activity lifecycle ends (e.g. pressing back, or rotating the device) – that is, onceonDestroy()
has finished – it should have no references whatsoever still pointing to it. Otherwise, the GarbageCollector will politely ignore it, thinking you’re not done with the Activity object, and you’ll wind up with some massive memory leak.
OK, this should be in the most tiny font we have available, but… once in a while, and we’re talking every four or five blue moons at the most, you may use System.gc()
. But, if you can’t write at least twelve reasons that’s your only solution, you should probably re-think your memory management design.
LESSON #5: DON’T HOLD ON TO REFERENCES TO ACTIVITIES. EVER.
This also applies to Fragments, Views, Resources and anything else tightly associated with a Context. These kinds of objects are sprawling metropolises of references to absolutely everything. We can get more into investigating the runtime memory map of your app a bit later, but until then, the tangled web we Context-based objects weave is insidious.
If you accidentally hold a reference to a View beyond its lifetime (i.e., you “leak” it), it has a reference to the Activity context that created it, and in turn, every other View, Fragment, Dialog, and so forth associated with that activity.
And if this happens, you’ll not be able to get it out of memory. So, you can kiss handful after handful of MBs goodbye each time this happens.5 If you’re ever having memory trouble while developing an Android application, the very first thing to check for is leaked Activities.
LESSON #6: IT CAN HAPPEN TO YOU…
I wouldn’t be writing this if it wasn’t something we recently ran into. Fortunately, we caught the problem before shipping to the Play Store, but it was entirely possible that it could have made it through.
Our problem? We had tripped over the fine line between abstraction and obfuscation.
Story time!
We had a bunch of activities that wanted to know when someone logged in; so, when the Activity was created, we would instantiate a class variable of that type EventListener
, and add it as a reference to the singleton class (as in, an object that is never GarbageCollected in the lifespan of the app) that managed our user authentication. Then, of course, we would remove the reference in the Activity’s onDestroy()
method like good little memory managers.
Alas, the key we had missed: we use many fragments. Occasionally, we have to reference the parent activity from a child fragment. When one of our fragments wanted to get that login notification, it simply retrieved its parent activity, and called the method:
public void addLoginListener(EventListener loginListener);
But, that method’s implementation was designed to add the listener straight on to the singleton that managed authentication. We never bothered removing it, because that method looked like it was adding a reference to the Fragment to its parent Activity – which would allow the Fragment to be GarbageCollected at the same time as that Activity. Since this method was just a convenient proxy to add a reference to the authentication singleton, we wound up with a leaked reference to the Fragment, and, in turn, a reference to that Fragment’s context (its parent Activity) and all of its references in turn.
WAIT, THAT’S IT?
Yes, this concludes Part 1, wherein we’ve gone over some details of how memory can be leaked. If you’re wondering how to fix all the problems you never knew existed until just now, we’ll get into how to analyze your app’s memory usage in Part 2.
USEFUL LINKS:
Android内存管理(1)WRANGLING DALVIK: MEMORY MANAGEMENT IN ANDROID PART 1的更多相关文章
- PythonStudy——内存管理之垃圾回收 Memory management Garbage collection
内存管理 引用计数:垃圾回收机制的依据 # 1.变量的值被引用,该值的引用计数 +1# 2.变量的值被解绑,该值的引用计数 -1# 3.引用计数为0时就会被垃圾回收机制回收 标记清除:解决循环引用问题 ...
- Android内存管理(2)HUNTING YOUR LEAKS: MEMORY MANAGEMENT IN ANDROID PART 2
from: http://www.raizlabs.com/dev/2014/04/hunting-your-leaks-memory-management-in-android-part-2-of- ...
- [Android Memory] Android内存管理、监测剖析
转载自:http://blog.csdn.net/anlegor/article/details/23398785 Android内存管理机制: Android内存管理主要有:LowMemory Ki ...
- Android内存管理机制之一:low memory killer
转载自http://www.miui.com/thread-29268-1-1.html 准备写这个专题之前,心里是有点忐忑的.首先Android内存管理机制相当复杂,想要讲清楚比较困难:其次对于绝大 ...
- Android 内存管理之优化建议
OOM(OutOfMemory)转:http://hukai.me/android-performance-oom/ 前面我们提到过使用getMemoryClass()的方法可以得到Dalvik He ...
- Android 内存管理分析(四)
尊重原创作者,转载请注明出处: http://blog.csdn.net/gemmem/article/details/8920039 最近在网上看了不少Android内存管理方面的博文,但是文章大多 ...
- 浅谈Android内存管理
最近在网上看了不少Android内存管理方面的博文,但是文章大多都是就单个方面去介绍内存管理,没有能全局把握,缺乏系统性阐述,而且有些观点有误,仅仅知道这些,还是无法从整体上理解内存管理,对培养系统优 ...
- Android——内存管理基础
内存收集概念 内存垃圾收集器(garbage collector) 概念:自定内存管理. 功能:分配内存.保证所有被引用的对象还在内存中.可以释放在运行的代码中不再引用的对象的内存. 垃圾收集器避免了 ...
- Android 内存管理(二)
很多开发者都是从j2me或j2ee上过来的,对于内存的使用和理解并不是很到位,Android开发网本次给大家一些架构上的指导,防止出现豆腐渣工 程的出现.Android作为以Java语言为主的智能平台 ...
随机推荐
- MyEclipse2015 编写js报 'Calculating completion proposals..' has encountered a problem.
前言:编写js(按点后)弹出这个鬼东西,百度不到..估计是破解有问题.只有换版本了. 版本:MyEclipse 2015 stable 1.0 详细错误信息 解决:换成2.0版本
- JSP页面批量选择&全选操作&选择回显
效果如下: js验证部分: 页面body部分: 附:控制器Controller中验证批量选择条件回显:
- c++11 pod类型(了解)
啥是POD类型? POD全称Plain Old Data.通俗的讲,一个类或结构体通过二进制拷贝后还能保持其数据不变,那么它就是一个POD类型. 平凡的定义 .有平凡的构造函数 .有平凡的拷贝构造函数 ...
- 服务端发送xml请求java代码示例
/** * */ package com.autoyol.pay.cmb.core; import java.io.ByteArrayOutputStream; import java.io.IOEx ...
- 下拉菜单得经典写法html5
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- POJ 1845 Sumdiv (求某个数的所有正因子的和)
题意: 求A^B的所有正因子的和,最后模9901的结果. 思路: 若对一个数n进行素数分解,n=p1^a1*p2^a2*p3^a3*...*pk^ak那么n的所有正因子之和sum=(1+p1+...+ ...
- POJ 1665
#include<iostream>//chengdacaizi 08.11.12 #include<iomanip> #define p 3.1415927 using na ...
- java web页面 base
<base href="<%=basePath%>"> <base src="<%=basePath%>"> 主 ...
- php获取网页header信息的4种方法
php获取网页header信息的方法多种多样,就php语言来说,我知道的方法有4种, 下面逐一献上. 方法一:使用get_headers()函数 推荐指数: ★★★★★ get_header方法最简单 ...
- java控制反转与依赖注入
1.简介 依赖注入和控制反转,目的是为了使类与类之间解耦合,提高系统的可扩展性和可维护性,下面通过一个例子来引入这一概念. 2.案例 1)一般情况下的类耦合 Main.java public clas ...