内存优化

内存溢出和内存泄漏
内存溢出:就想杯子里得水满了,就溢出了。内存溢出就是分配的内存被用光了,不够用了。

内存泄露:就如同杯子里面有石子,导致杯子里面的一部分空间没有被利用,在APP中内存泄露就是指该被回收的内存没有被回收,导致一部分内存一直被占着,可利用内存变少了。当泄露过多 时,可利用的内存越来越少,就会引起内存溢出了。

内存优化分为:【一个本质和三个知识点】 
本质:对象的引用未被释放,导致对象本身无法被有效的回收。 
三个知识点:内存泄漏、内存溢出、内存优化工具。

【内存泄漏】 

1.单例造成的内存泄漏

因为单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。所以当需要传入一个context,应传入Application的Context

例如:public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context.getApplicationContext();
}
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
}
return instance;
}
}

2.匿名内部类/非静态内部类创建静态实例造成的内存泄漏

原因:内部类持有对象引用,导致无法释放,比如各种回调 
优化:保持生命周期一致,改为静态实例+对象的弱引用方式(WeakReference)

例如:public class MainActivity extends AppCompatActivity {
private static TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mManager == null){
mManager = new TestResource();
}
//...
}
class TestResource {
//...
}
}
正确的做法为:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请使用ApplicationContext 。

3.Handler造成的内存泄漏

Handler持有Activity的引用,其发送的Message中持有Handler的引用,当队列处理Message的时间过长会导致Handler无法被回收 

正确的写法:public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}

@Override
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);
}

@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
优化:静态实例+弱引用(Weakrefrence)方式 ,销毁对象时候清空队列里的Message使用mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有消息和所有的Runnable。当然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();来移除指定的Runnable和Message。

4.线程造成的内存泄漏

原因:线程持有对象的引用在后台执行,与对象的生命周期不一致 
优化:静态实例+弱引用(Weakrefrence)方式,使其生命周期一致
例如:new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(10000);
}
}).start();
上面的Runnable是一个匿名内部类,因此它们对当前Activity都有一个隐式引用。如果Activity在销毁之前,任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏。正确的做法还是使用静态内部类的方式
正确写法:static class MyRunnable implements Runnable{
@Override
public void run() {
SystemClock.sleep(10000);
}
}
//——————
new Thread(new MyRunnable()).start();
5.资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。当我们不再使用Bitmap对象的时候一定要执行recycler方法,这里需要指出的是当我们在代码中执行recycler方法,Bitmap并不会被立即释放掉,其只是通知虚拟机该Bitmap可以被recycler了。在加载网络图片的时候,使用软引用或者弱引用并进 行本地缓存
6. 使用了属性动画或循环动画
在Activity中使用了属性循环动画,在onDestroy()方法中未正确停止动画

7.集合操作不当引发的内存泄漏 
原因:集合只增不减 
优化:有对应的删除或卸载操作

内存溢出: 
原因: 
1.内存泄漏长时间的积累 
2.业务操作使用超大内存 
优化: 
1.调整图像大小后再放入内存、及时回收 
2.不要过多的创建静态变量,由于static声明变量的生命周期其实是和APP的生命周期一 样的,有点类似与Application。如果大量的使用的话,就会占据内存空间不释放,积少成多也会造成内存的不断开销,直至挂掉。static的合理 使用一般用来修饰基本数据类型或者轻量级对象,尽量避免修复集合或者大对象,常用作修饰全局配置项、工具类方法、内部类

针对static的解决方案:

   <1>应该尽量避免static成员变量引用耗费过多的实例,比如Context。

   <2>Context尽量使用ApplicationContext,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。

   <3>使用WeakRefrence代替强引用。比如可以使用WeakRefrence<Context>mContextRef;

3.String字符串优化

最常见的例子就是当你要频繁操作一个字符串时。使用StringBuffer代替String。

还比如:使用int数组而不是Integer数组。

4.UI视图优化

减少视图层级,可以有效的减少内存消耗,因为视图是一个树形结构,每次刷新和渲染都会遍历一次

(1)ViewStub标签可以使UI在特殊情况下,只管效果类似于设置View的不可见性,但是其最大意义在于被整各标签所包裹的vIEWS在默认状态下不会占用任何内存空间。

(2)Merge

单独将<merge/>标签做个介绍,是因为它在优化UI结构时起到很重要的作用。目的是通过删减多余或者额外的层级,从而优化整个Android Layout结构。核心功能就是减少冗余的层次从而达到优化UI的目的

(3)include

可以通过这个标签直接加载外部的xml到当前结构中,是复用UI资源的常用标签

(4)尽量使用相对布局

5.ListView优化

(1)Item布局,层级越少越好,使用hierarchyview工具查看优化

(2)复用convertView

(3)使用ViewHolder

(4)item中有图片时,异步加载

(5)快速滑动时,不加载图片

(6)item中有图片时,应对图片进行适当的压缩

(7)实现数据的分页加载

6.性能优化工具的使用

MAT,LearkCanary,Memory Monitor,Allocation Tracking,Heap Tool,hierarchyviewer布局检测工具

内存优化工具: 
Square:LeakCanary、MAT等

原文地址:https://www.cnblogs.com/evolutionoflicorice/p/10191866.html