内存
JAVA是在JVM所虚拟出的内存环境中运行的,内存分为三个区:堆、栈和方法区。
栈(stack):是简单的数据结构,程序运行时系统自动分配,使用完毕后自动释放。优点:速度快。
堆(heap):用于存放由new创建的对象和数组。在堆中分配的内存,一方面由java虚拟机自动垃圾回收器来管理,另一方面还需要程序员提供修养,防止内存泄露问题。
方法区(method):又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
Java GC
GC可以自动清理堆中不在使用(不在有对象持有该对象的引用)的对象。
在JAVA中对象如果再没有引用指向该对象,那么该对象就无从处理或调用该对象,这样的对象称为不可到达(unreachable)。垃圾回收用于释放不可到达的对象所占据的内存。
对android来说,内存使用尤为吃紧,最开始的app进程最大分配才8M的内存,渐渐增加到16M、32M、64M,但是和服务端相比还是很渺小的。如果对象回收不及时,很容易出现OOM错误。
内存泄露
什么是内存泄露?程序通过new分配内存,在使用完毕后没有释放,造成内存占用。这块内存不受GC控制,无法通过GC回收。
主要表现在:当一个对象已经不再使用,本该被回收的,但是另外一个正在使用的对象持有它的引用从而就导致对象不能被回收。这种对象存在堆内存中,就产生了内存泄漏。
危害?内存泄漏对于app没有直接的危害,即使app有发生内存泄漏的情况,也不一定会引起app崩溃,但是会增加app内存的占用。内存得不到释放,慢慢的会造成app内存溢出。解决内存泄漏目的就是防止app发生内存溢出。
内存泄露主要表现的当Activity在finish的时候,由于对象持有对Activity的引用,造成Activity没有被及时回收。总结了下大致有5种情况造成内存泄露,(1)static变量、匿名类的使用 (2)线程执行处理(3)各种监听回调处置(4)Bitmap等回收处置(5)集合类只有增操作却没有减操作。
常见情况
1)外部类持有Activity的静态引用
public class MainActivity extends AppCompatActivity {
static Activity activity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CommUtil commUtil = CommUtil.getInstance(this);
}
public class CommUtils {
private static CommUtils instance;
private Context context;
private CommUtils(Context context) {
this.context = context;
}
public static CommUtils getInstance(Context context) {
if (instance == null) {
instance = new CommUtils(context);
}
return instance;
}
}
2)异步执行耗时任务期间时,Thread、AsyncTask、TimeTask持有的Activty进行finish时,Activity实例不会被回收。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new AsyncTask() {
@Override
protected String doInBackground(String... params) {
for (int i = 0; i
try {
Log.e("MainActivity2", "dddd" + i + MainActivity2.this.getLocalClassName());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
}
}.execute();
}
3)Handler内部类造成内存泄露。
Handler为非静态内部类时会隐式持有当前activity引用。当Activity被 finish()时,若Handler有未处理完或延迟的消息(主要是Handler牵扯到线程问题),会造成activity不能被回收。
MyHandler myHandler = new MyHandler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myHandler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 50 * 1000);
}
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}
解决办法:在Activity生命周期结束前,确保Handler移除消息(mMyHanlder.removeCallbacksAndMessages(null);)或者使用静态Handler内部类。
如:使用了弱引用替代强引用.
static MyHandler myHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myHandler = new MyHandler(this);
}
static class MyHandler extends Handler {
WeakReference mActivityReference;
MyHandler(Activity activity) {
mActivityReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
final Activity activity = mActivityReference.get();
if (activity != null) {
//....
}
}
}
建议熟悉下:强引用(StrongReference)、软引用(SoftReference)、弱引用(WeakReference)、虚引用(PhantomReference)
4)匿名内部类的使用。
public class DemoActivity extends AppCompatActivity {
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
runnable默认会持有DemoActivity的引用。若Activity被finish的时候,如线程在使用runnable,则会造成内存泄露。
5)构造Adapter时没有使用缓存的 convertView
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView == null)
convertView = View.inflate(this, R.layout.item_layout, false);
view = convertView;
return view;
}
6) 当使用了BraodcastReceiver、Cursor、Bitmap等资源时,若没有及时释放,则会引起内存泄漏。
7)集合类的不当使用。
更多内存泄露可以通过检测工具发现。检测工具主要有MAT、Memory Monitor 、Allocation Tracker 、Heap Viewer、LeakCanary
内存溢出
什么是内存溢出?out of memory。 程序向系统申请的内存空间超出了系统能给的。内存泄露很容易引起OOM。
内存抖动
内存抖动是指在短时间内有大量的对象被创建或者被回收的现象,主要是循环中大量创建、回收对象。这种情况应当尽量避免。