当前位置: 首页 > 科技观察

Android内存泄露的八种可能

时间:2023-03-13 00:08:41 科技观察

Java是一种垃圾回收语言。它的好处是开发者不需要专门管理内存分配,减少了部分故障(segmentationfault)导致的应用程序崩溃,防止未释放的内存栈(堆)可能过度拥挤,所以写的代码更安全。不幸的是,Java中仍然存在很多逻辑漏洞,很容易导致内存泄漏。如果您不小心,您的Android应用程序很容易浪费未释放的内存,最终导致内存不足(OOM)错误。传统内存泄漏的原因是:当对象的所有引用都已释放时,对象还没有被释放。(译者注:游标忘记关闭等)逻辑内存泄漏的原因是:当应用程序不再需要这个对象时,对该对象的所有引用还没有被释放。如果持有对象的强引用,垃圾收集器就无法回收内存中的对象。在Android开发中,最容易出现内存泄漏问题的就是Context。比如Activity的Context中包含了大量的内存引用,比如ViewHierarchies等资源。Context一旦泄露,也就意味着泄露了它指向的所有对象。Android机器内存有限,内存泄露过多很容易导致OOM。检测逻辑内存泄漏需要主观判断,尤其是在对象的生命周期不明确的情况下。好在Activity有明确的生命周期,很容易找到泄漏的原因。Activity.onDestroy()被视为Activity生命的结束。在编程上,应该销毁,或者Android系统需要回收内存(译者注:当内存不够时,Android会回收看不见的Activity)。如果执行到这个方法,栈中仍然存在对Activity的强引用,垃圾回收器将无法将其标记为回收内存,而我们的初衷是回收它!结果是Activity存在于其生命周期之外。Activity是重量级对象,应该由Android系统处理。然而,逻辑内存泄漏总是在不经意间发生。(译者注:我曾经试过一个导致20M内存泄漏的Activity)。在Android中,导致潜在内存泄漏的陷阱不外乎两种:全局进程(process-global)的静态变量。无论应用程序的状态如何,这个怪物都持有对Activity的强引用。活动生命周期之外的线程。不会清除对Activity的强引用。查看您是否遇到过以下情况。StaticActivities在类中定义了一个静态的Activity变量,并将当前运行的Activity实例赋值给这个静态变量。如果Activity生命周期结束后不清除这个静态变量,就会造成内存泄漏。因为静态变量贯穿于应用程序的生命周期,泄漏的Activity会一直存在于应用程序进程中,不会被垃圾回收器回收。staticActivityactivity;voidsetStaticActivity(){activity=this;}ViewsaButton=findViewById(R.id.sa_button);saButton.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){setStaticActivity();nextActivity();}});内存泄漏1——静态ActivityStaticViews在单例模式下也会出现类似的情况。如果Activity被频繁使用,在内存中保留一个实例是非常实用的。如前所述,强行延长Activity的生命周期是相当危险和不必要的,无论如何也不应该这样做。特例:如果一个View初始化消耗的资源比较多,并且在Activity的生命周期内保持不变,可以做成静态的加载到视图树(ViewHierachy)中。像这样,当Activity被销毁时,它应该释放资源。(译者注:示例代码并没有释放内存,只是将这个静态视图设置为null,但是还是不推荐使用这个静态视图的方式)staticview;voidsetStaticView(){view=findViewById(R.id.sv_button);}ViewsvButton=findViewById(R.id.sv_button);svButton.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){setStaticView();nextActivity();}});MemoryLeak2-StaticViewInner类继续,假设Activity中有一个内部类,可以提高可读性和封装性。就像我们创建一个内部类并持有一个静态变量的引用一样,恭喜,内存泄漏离你不远了(译者注:销毁时空白,嗯)。privatestaticObjectinner;voidcreateInnerClass(){classInnerClass{}inner=newInnerClass();}ViewicButton=findViewById(R.id.ic_button);icButton.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){createInnerClass();nextActivity();}});内存泄漏3-内部类内部类的优点之一是它可以访问外部类。不幸的是,内存泄漏的原因是内部类持有对外部类实例的强引用。匿名类类似地,匿名类也维护对外部类的引用。所以当你在Activity中定义匿名AsyncTsk时很容易发生内存泄漏。当异步任务在后台执行耗时任务时,Activity不幸被销毁(译者注:用户退出,系统回收),AsyncTask持有的Activity实例直到被垃圾回收器回收为止异步任务结束。voidstartAsyncTask(){newAsyncTask(){@OverrideprotectedVoiddoInBackground(Void...params){while(true);}}.execute();}super.onCreate(savedInstanceState);setContentView(R.layout.同样,定义一个匿名的Runnable,用一个匿名类Handler来执行。Runnable内部类将持有对外部类的隐式引用,并传递给Handler的MessageQueue。Message消息在处理之前,Activity实例不会被销毁,从而造成内存泄漏。voidcreateHandler(){newHandler(){@OverridepublicvoidhandleMessage(Messagemessage){super.handleMessage(message);}}.postDelayed(newRunnable(){@Overridepublicvoidrun(){while(true);}},Long.MAX_VALUE>>1);}ViewhButton=findViewById(R.id.h_button);hButton.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){createHandler();nextActivity();}});MemoryLeak5-HandlerThreads我们又通过Thread和TimerTask来展示内存泄漏。voidspawnThread(){newThread(){@Overridepublicvoidrun(){while(true);}}.start();}ViewtButton=findViewById(R.id.t_button);tButton.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(viewv){spawnThread();nextActivity();}});内存泄漏6-ThreadTimerTask只要是匿名类的实例,无论是否在工作线程中,都会持有Activity的引用,导致在内存泄漏中。oidscheduleTimer(){newTimer().schedule(newTimerTask(){@Overridepublicvoidrun(){while(true);}},Long.MAX_VALUE>>1);}ViewttButton=findViewById(R.id.tt_button);ttButton.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){scheduleTimer();nextActivity();}});内存泄漏7-TimerTaskSensorManager***,可以通过Context.getSystemService(intname)获取系统服务。这些服务在它们自己的进程中工作,帮助应用程序处理后台任务,并处理硬件交互。如果您需要使用这些服务,您可以注册一个侦听器,这将导致该服务持有对Context的引用。如果在Activity销毁时不取消这些监听器,就会造成内存泄漏。voidregisterListener(){SensorManagersensorManager=(SensorManager)getSystemService(SENSOR_SERVICE);Sensorsensor=sensorManager.getDefaultSensor(Sensor.TYPE_ALL);sensorManager.registerListener(this,sensor,SensorManager.SENSOR_DELAY_FASTtonEST.idView);smButton.setOnClickListener(newView.OnClickListener(){@OverridepublicvoidonClick(Viewv){registerListener();nextActivity();}});内存泄漏8-SensorManager总结内存泄漏的例子我见过太多了,很容易导致eatingup手机的内存使得垃圾回收过程更加频繁,最坏的情况下甚至会导致OOM。垃圾收集是一项代价高昂的操作,可能会导致可见的卡顿。因此,要注意实例化时持有的引用链,经常进行内存泄漏检查。原文:您的Android应用程序可能泄漏内存的八种方式