Android内存
Posted study_zhxu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android内存相关的知识,希望对你有一定的参考价值。
android内存
垃圾回收器的职责
1、分配内存
2、确保任何能被引用的对象保留在内存中
3、回收不能通过引用关系找到的对象的内存
相关概念
垃圾回收GC
垃圾回收器中有一个进程来做上面的这些事情,这个进程查找对象引用的关系并释放内存,这个进程就是常说的GC
Heap和Stack
Heap内存称为堆内存,指Java运行环境用来分配给对象和JRE类的内存,是引用的内存空间
Stack内存称为栈内存,是对Thread而言的,它保存线程中方法中短期存在的变量和Heap中对象的引用等
GC针对的是Heap内存,因为Stack内存相当于是随用随销的。
GC ROOT
GC Root可以理解为Heap内存之外的对象,通常包含如下
System Class 系统Class Loader加载的类,如Java运行环境中rt.jar类
Thread运行的线程
JNI中的本地全局变量
同步监控器 任何调用wait()或notify()方法,或者是synchronized的东西
Java本地实例 运行在Thread的stack中方法创建的对象
JVM内存区域
Young Generation
新生代,所有new的对象,该区域的内存管理使用小GC
分为三部分:Enden,Survivor 0和Survivor 1部分
- Old Generation
老年区,新生代中执行小粒度的GC幸存下来的老对象,该区域内存管理使用大GC
- Permanent Generation
持久代,包含应用的类/方法信息以及JRE库的类和方法信息 GC流程
Android系统启动
1.启动电源以及系统启动
当电源按下时,引导芯片代码开始从预定义的地方(固化在ROM)开始执行,加载引导程序到RAM中,开始执行
2.引导程序
引导程序是Android程序运行开始前的一个小程序,引导程序是运行的第一个程序,主要是针对主板和芯片的。引导程序不是Android操作系统的一部分。
引导程序执行分为两个阶段,第一阶段:检测外部的RAM以及加载对第二段有用的程序。第二阶段:引导程序设置网络、内存等。
这些都是运行内核必要的,为了达到特殊的目的,引导程序可以根据配置参数或者输入数据设置内核。
3.内核
内核启动时设置缓存、被保护的存储器、计划列表、加载驱动。当内核完成系统设置后它首先在系统文件中寻找init文件,然后启动root进程或者系统的第一个进程。
4.init进程
init是第一个进程,它是root进程的父进程。init进程有两个责任,一是挂载目录,二是运行init.rc脚本。
5.Zygote
在Java中,不同的虚拟机实例会为不同的应用分配不同的内存。如果Android系统为每一个应用启动不同的虚拟机就会消耗大量的内存以及时间。为此Android系统创造了Zygote,Zygote让虚拟机共享代码、低内存占用和最小时间启动称为可能。Zygote是一个虚拟器进程在系统引导的时候启动。Zygote预加载以及初始化核心类库。在Java虚拟机中,每一个实例都有它自己的核心类库文件和堆对象的copy。
ServiceManager和Zygote进程是Android的基础,Zygote进程起来了才会建立真正的Android运行空间。
Zygote建立完成后利用Socket通讯,接收ActivityManagerService的请求,fork应用程序。
6.系统服务System Server
Android的所有服务循环框架都是建立在SystemServer上的,其中通过AddService来添加服务到ServiceManager中。
7.流程
Linux系统通过init在启动若干守护进程后,就启动了Android的runtime和Zygote,Zygote再启动虚拟机、系统服务,系统服务再启动完本地服务后,有启动若干的Android服务,并完成ServiceManager的注册工作,最后整个系统启动完成。其中Zygote孵化器为各个进程以复制的方式用最小的代价实现了虚拟机。
内存泄露
Android中导致内存泄露的原因有单例导致的内存泄露,内部类导致的内存泄露,匿名内部类导致的内存泄露,静态内部类导致的内存泄露等。
1.单例导致的内存泄露
创建单例类,代码如下
public class SingleManager private Context mContext ; private static SingleManager instance ; //初始化需要Context上下文对象 private SingleManager (Context context) this.mContext = context ; public static SingleManager getInstance(Context context) if(instance == null) synchronized(SingleManager.class) if(instance == null) instance = new SingleManager(context); return instance ;
在activity中调用,调用方式如下
public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //调用,直接传入this对象 SingleManager.getInstance(this);
配置好LeakCanary,然后直接点击返回按钮,LeakCanary会报异常,如图
通过LeakCanary可以看到,报出了内存泄露的异常,是由于单例引起的内存泄露。
在activity中使用单例时传入的参数是this对象,该this是activity,当点击返回按钮退出activity时,activity本应该被销毁,但是因为该activity被单例引用,所以不会被销毁,导致activity不会被回收,这就导致了内存泄露,那么我们如何避免这种情况产生呢。内存泄露是由于activity没有没回收引起的,所以我们可以不引用activity,可以直接引用application对象。代码修改如下。
public class SingleManager private Context mContext ; private static SingleManager instance ; //初始化需要Context上下文对象 private SingleManager (Context context) this.mContext = context.getApplicationContext() ; public static SingleManager getInstance(Context context) if(instance == null) synchronized(SingleManager.class) if(instance == null) instance = new SingleManager(context); return instance ;
通过在单例中将context对象转成application对象就可以解决该问题了。无论传入的是activity还是application,在单例中我们使用的都是application对象。
2.Handler导致的内存泄露
在Java中非静态内部类和匿名内部类都持有外部类对象,所以如果在activity中创建非静态内部类或者匿名内部类,所以如果activity被关闭但是内部类还被其他对象持有时,activity不会被销毁,这就导致了内存泄露。具体示例如下
public class MainActivity extends AppCompatActivity private Handler handler = new Handler() @Override public void handleMessage(Message msg) switch(msg.what) case 0: //TODO break; case 1: //TODO break; default: break; ; @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); handler.sendEmptyMessage(0);
在activity中创建一个handler匿名内部类,在onCreate()方法中调用sendEmptyMessage()方法,系统会发送消息到消息队列中,该消息包含一个target,该target对象是activity中匿名handler对象,这时候如果消息队列中消息过多,该消息暂时没有被处理,同时activity被关闭,这时就会引起内存泄露。
如何解决该问题呢?
可以通过弱引用WeakReference来解决该问题。
修改代码如下
public class MainActivity extends AppCompatActivity private static class MyHandler extends Handler //使用弱引用 WeakReference<MainActivity> wrf ; public MyHandler (MainActivity activity) if(activity != null) wrf = new WeakReference<MainActivity>(activity) ; @Override public void handleMessage(Message msg) super.handleMessage(msg); if(wrf == null)return ; //使用弱引用来获取activity MainActivity activity = wrf.get(); @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyHandler handler = new MyHandler(this) ; handler.sendEmptyMessage(0); @Override protected void onDestroy() super.onDestroy(); MyApplication.getRefWatcher().watch(this);
3.内部类导致的内存泄露
内部类引起的内存泄露示例如下
public class MainActivity extends AppCompatActivity //内部类 class InnerClass //静态内部类对象 private static InnerClass innerClass ; //创建内部类对象 private void createInnerClass() innerClass = new InnerClass() ; @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); createInnerClass();
activity中创建内部类,并且使用静态对象持有该内部类对象,被static修饰的变量声明周期是整个源程序的生命周期,与activity生命周期无关,所以当activity被关闭时innerClass变量不会被清空,依然存在内存中,innerClass变量持有内部类对象,间接的持有外部类的MainActivity对象。导致MainActivity不会被回收,导致出现内存泄露,LeakCanary检测如下
4.匿名内部类导致的内存泄露
匿名内部类导致内存泄露如下示例如下
public class MainActivity extends AppCompatActivity void startAsyncTask() new AsyncTask<Void, Void, Void>() @Override protected Void doInBackground(Void... params) while(true); .execute(); @Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startAsyncTask();
在activity创建匿名内部类,匿名内部类中执行线程,当activity退出时,线程依然在执行,这个匿名内部类依然存在,匿名内部类只有外部类activity,这就导致activity不会被回收,导致内存泄露。
LeakCanary检测如下
QQ交流群
微信公众号:Android在路上,欢迎关注
以上是关于Android内存的主要内容,如果未能解决你的问题,请参考以下文章