Android之内存泄漏与内存溢出

这篇具有很好参考价值的文章主要介绍了Android之内存泄漏与内存溢出。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Android之内存泄漏与内存溢出

Android之内存泄漏与内存溢出,安卓,android,jvm,java

概览

内存泄漏(memory leak):是指程序在申请内存后,无法释放已申请的内存空间,导致系统无法及时回收内存并且分配给其他进程使用。通常少次数的内存无法及时回收并不会到程序造成什么影响,但是如果在内存本身就比较少获取多次导致内存无法正常回收时,就会导致内存不够用,最终导致内存溢出。

内存溢出 (out of memory):是指程序申请内存时,没有足够的内存供申请者使用,导致数据无法正常存储到内存中。也就是说给你个int类型的存储数据大小的空间,但是却存储一个long类型的数据,这样就会导致内存溢出。

内存溢出和内存泄露的关系以及区别

关系:内存泄露最终会导致内存溢出,由于系统中的内存是有限的,如果过度占用资源而不及时释放,最后会导致内存不足,从而无法给所需要存储的数据提供足够的内存,从而导致内存溢出。导致内存溢出也可能是由于在给数据分配大小时没有根据实际要求分配,最后导致分配的内存无法满足数据的需求,从而导致内存溢出。

区别:内存泄露是由于GC无法及时或者无法识别可以回收的数据进行及时的回收,导致内存的浪费;内存溢出是由于数据所需要的内存无法得到满足,导致数据无法正常存储到内存中。内存泄露的多次表现就是会导致内存溢出。文章来源地址https://www.toymoban.com/news/detail-536653.html

内存泄漏

  • 从有一组定义为gc root的根节点到目标对象的路径,称为可达性。此类对象也就是存活对象,不可达的对象就是应该被gc垃圾回收机制进行回收的对象。在当前应用的生命周期内不再使用的对象,依然被gc root引用,导致无法回收,既造成了内存泄漏。
  • 内存泄漏即 ML (Memory Leak) 指 程序在申请内存后,当该内存不需再使用;但 却无法被释放&归还给程序的现象。

内存泄漏带来的危害

  • 用户对单次的内存泄漏并没有什么感知,但当可用的空闲空间越来越少,GC就会更容易被触发,GC进行时会停止其他线程的工作,因此有可能会造成界面卡顿等情况。
  • 后续需要分配内存的时候,很容易导致内存空间不足而出现 OOM(内存溢出)。

常见的内存泄漏问题

    1. 资源性对象未关闭
      • 对于资源性对象不再使用时,应该立即调用它的close()函数,将其关闭,然后再置为null。
      • 例如:
        • 文件I/O流、数据库连接(SQLiteDatabase)、媒体资源(MediaPlayer)、网络连接(HttpURLConnection)、Cursor对象、ContentResolver、蓝牙连接(BluetoothSocket)、网络套接字(Socket)、Bitmap对象未关闭。
        • TimerTask定时任务、Timer计时器未取消。
        • WebView未销毁。
        • 线程池未关闭。
      • 以上所提及的资源应该在使用完或者在Activity页面销毁时及时关闭。
    1. 注册对象未注销各种Listener
      • 订阅者模式中,如果注册对象不再使用时,未及时注销,会导致订阅者列表中维持这对象的引用,阻止垃圾回收,导致内存泄露。
      • 例如:
        • BraodcastReceiver、EventBus未注销造成的内存泄漏,我们应该在Activity销毁时及时注销。
    1. 类的静态变量持有大数据对象
      • 尽量避免使用静态变量存储数据,特别是大数据对象,建议使用数据库存储。
    1. 单例造成的内存泄漏
      • 优先使用Application的Context,如需使用Activity的Context,可以在传入Context时使用弱引用进行封装,然后,在使用到的地方从弱引用中获取Context,如果获取不到,则直接return即可。
      •  public class Singleton {
             private static Singleton instance;
             private Context mContext;
             private Singleton(Context context){
                 this.mContext = context;
             }
        
         	/**
         		* 如果传入的context是activity,service的上下文,会导致内存泄漏
         		* 原因是我们的instance是一个static的静态对象,这个对象的生命周期和整个app的生命周期一样长
         		* 当activity销毁的时候,我们的这个instance仍然持有者这个activity的context,就会导致activity对象无法被释放回收,就导致了内存泄漏
             */
             public static Singleton getInstance(Context context){
                 if (instance == null) {
                     synchronized (Singleton.class){
                         if (instance == null){
                             instance = new Singleton(context);
                         }
                     }
                 }
                 return instance;
             }
         }
        
    1. 非静态内部类的静态实例
      • 非静态内部类持有外部类实例的引用,若非静态内部类的实例是静态的,便拥有app存活期整个生命周期,长期持有外部类的引用,阻止外部类实例被回收。
      • 使用内部类的情况十分常见,尤其是匿名内部类:一些接口的匿名实现类,都是内部类。
      • 解决方案:
        • (1)改为静态内部类,不再持有外部类实例的引用。
        • (2)避免申明非静态内部类的静态实例。
        • (3)将内部类抽取出来封装成一个单例,如果需要Context,没有特殊要求就使用Application Context;如果需要Activity Context,则使用完毕置空,或者使用弱引用。
    1. Handler造成的泄漏
      • Handler造成内存泄露的原因:非静态内部类或者匿名内部类使得Handler默认持有外部类的引用。在Activity销毁时,由于Handler可能有未执行完/正在执行的Message,导致Handler持有Activity的引用,进而导致GC无法回收Activity。
      • 解决办法:
        • 静态内部类+弱引用:
          •  private static class MyHandler extends Handler{
             	private final WeakReference<MineActivity> mMineActivityWeak;
             	public MyHandler(MineActivity mineActivity){
             		mMineActivityWeak = new WeakReference<>(mineActivity);
             	}
             	@Override
             	public void handleMessage(@NonNull Message msg) {
             		super.handleMessage(msg);
             		MineActivity mineActivity = mMineActivityWeak.get();
             		if(mineActivity != null){
             			mineActivity.number = 5;
                     }
                 }
             }
            
        • Activity销毁时,清空Handler中,未执行或正在执行的Callback以及Message。
          •   // 清空消息队列,移除对外部类的引用
             @Override
             protected void onDestroy() {
               	super.onDestroy();
               	mHandler.removeCallbacksAndMessages(null);
             }
            
      • 注:AsyncTask内部也是Handler机制,同样存在内存泄漏风险,但其一般是临时性的。
        • 解决办法:
          • ①静态内部类+弱引用。
          • ②在 Activity 或 Fragment 的 onDestroy() 方法中,手动取消 AsyncTask。
            •  @Override
               protected void onDestroy() {
                   super.onDestroy();
                   staticAsyncTask.cancel(true);
               }
              
          • ③使用 AsyncTaskLoader,它是一个专门设计用来避免内存泄露的异步任务处理器。它会在 Activity 或 Fragment 销毁时自动取消所有的任务,从而避免内存泄露。
    1. 集合类持有过多对象导致的泄漏
      • 集合类持有过多对象可能导致内存泄漏的原因是因为集合中的对象如果变成无用状态,但是由于被集合所持有,其内存不会及时被回收,导致内存泄漏。
      • 常见的容器类还有线程池、对象池、图片缓存池等。
      • 例如:
        • 如果一个 Activity 中持有了一个集合对象,如果该 Activity 被销毁了但是集合中的对象没有移除或者清空,那么这些对象将无法被回收,导致内存泄漏。
      • 解决办法:
        • 在集合中存储对象时,要注意及时移除或者清空集合中不再需要的对象,尽量减少无用对象在集合中存储的时间。最简单的方法是:清空集合对象并设置为null。
          •  @Override
             protected void onDestroy() {
                 mList.clear();
                 mList = null;
                 super.onDestroy();
             }
            
        • 使用弱引用的集合,将集合中的对象与应用程序的生命周期解耦,避免持有过多无用对象导致的内存泄漏问题。
    1. WebView导致的泄漏
      • 目前Android中WebView的实现存在很大的兼容性问题,Google支持各个ROM厂商自行定制自己的WebView实现,各个ROM间差异较大,且大多都存在内存泄露问题。除了调用其内部的clearCache()、clearHistory()、removeAllViews()、freeMemory()、destroy()和置null以外,一般比较粗暴有效的解决方法是:将包含WebView的Activity放在一个单独的进程中,不需要时将进程销毁,从而释放所有所占内存。
      • 解决办法:
        • 不在 xml 中定义 Webview ,这样会引用 Activity,而是在需要的时候在 Activity 中创建,并且使用 getApplicationgContext()。
          •  LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);
             mWebView = new WebView(getApplicationContext());
             mWebView.setLayoutParams(params);
             mLayout.addView(mWebView);
            
        • Activity 关闭时需要手动释放 Webview 内存。
          •  override fun onDestroy() {
                 // webview?.loadDataWithBaseURL(null, "", "text/html", "utf-8", null)
                 // webview?.clearView()
                 webview?.loadUrl("about:blank")
                 webview?.parent?.let {
                     (it as ViewGroup).removeView(webview)
                 }
                 webview?.stopLoading()
                 webview?.settings?.javaScriptEnabled = false
                 webview?.clearHistory()
                 webview?.clearCache(true)
                 webview?.removeAllViewsInLayout()
                 webview?.removeAllViews()
                 webview?.webViewClient = null
                 webview?.webChromeClient = null
                 webview?.destroy()
                 webview = null
                 super.onDestroy()
             }
            
    1. 使用ListView时造成的内存泄漏
      • 使用ListView时,如果不正确地使用Adapter,可能会导致内存泄漏。这是因为ListView的机制,它会在滚动过程中重用convertView,如果在convertView中持有了一些对象的引用并没有及时释放,就会导致内存泄漏。
        •  @Override
           public View getView(int position, View convertView, ViewGroup parent) {
               ViewHolder holder;
               if (convertView == null) {
                   convertView = mInflater.inflate(R.layout.list_item_layout, null);
                   holder = new ViewHolder();
                   holder.titleTextView = convertView.findViewById(R.id.title_text_view);
                   holder.contentTextView = convertView.findViewById(R.id.content_text_view);
                   convertView.setTag(holder);
               } else {
                   holder = (ViewHolder) convertView.getTag();
               }
               return convertView;
           }
          
           private static class ViewHolder {
               TextView titleTextView;
               TextView contentTextView;
           }
          
    1. 使用第三库传递context
      • 在项目中经常会使用各种三方库,有些三方库的初始化需要我们传入一个 Context 对象,尽量使用 Context.getApplicationContext,不要直接将 Activity 传递给其他组件。
      • 比如:在一些广告的SDK中,它可能要求要用Activity的Context,但是还是要尽量查阅资料或者咨询一下看能不能使用Application的Context。
    1. 静态View导致内存泄露
      • 有时,当一个Activity经常启动,但是对应的View读取非常耗时,我们可以通过静态View变量来保持对该Activity的rootView引用。这样就可以不用每次启动Activity都去读取并渲染View了。这确实是一个提高Activity启动速度的好方法!但是要注意,一旦View attach到我们的Window上,就会持有一个Context(即Activity)的引用。而我们的View有事一个静态变量,所以导致Activity不被回收。
      • 解决办法:
        • 在使用静态View时,需要确保在资源回收时,将静态View detach掉。
    1. 属性动画未及时关闭导致内存泄露
      • 在使用ValueAnimator或者ObjectAnimator时,如果没有及时做cancel取消动画,就可能造成内存泄露。 因为在cancel方法里,最后调用了endAnimation(); ,在endAnimation里,有个AnimationHandler的单例,会持有属性动画对象的引用
      • 解决办法:
        • 在在onDestory时,调用动画的cancel方法

内存泄漏检测

  • 内存泄漏检测神器LeakCanary。
    • LeakCanary 是 Square 公司的一个开源库。通过它可以在 App 运行过程中检测内存泄漏,当内存泄漏发生时会生成发生泄漏对象的引用链,并通知程序开发人员。
    • 原理:LeakCanary 是通过在 Application 的 registerActivityLifecycleCallbacks 方法实现对 Activity 销毁监听的,该方法主要用来统一管理所有 Activity 的生命周期。所有 Activity 在销毁时在其 OnDestory 方法中都会回调 ActivityLifecycleCallbacks 的 onActivityDestroyed 方法,而 LeakCanary 要做的就是在该方法中调用 RefWatcher.watch 方法实现对 Activity 进行内存泄漏监控。
    • 接入:
      • 添加依赖:
        •  dependencies {
           	// debugImplementation because LeakCanary should only run in debug builds.
              debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
           }
          
    • 2.0以上版本会自己启动。

到了这里,关于Android之内存泄漏与内存溢出的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java jvm 内存溢出分析

    我们经常用visualVm监控Jvm的内存,cpu,线程的使用情况,通常可以根据内存不断增长来判断内存是否存在不释放。但是我们不可能时时盯着去看,这里涉及jvm堆内存配置,堆内存参数配置和调优会在其他章节编写。 如果真是内存溢出了,线上出现的我们需要配置JVm内存溢出,

    2024年02月09日
    浏览(54)
  • Android 内存泄漏

    内存泄漏:即memory leak。是指内存空间使用完毕后无法被释放的现象,虽然Java有垃圾回收机制(GC),但是对于还保持着引用, 该内存不能再被分配使用,逻辑上却已经不会再用到的对象,垃圾回收器不会回收它们。 内存溢出:即out of memory, 当你要求分配的内存超过了系统给你

    2024年02月08日
    浏览(35)
  • 深入理解 JVM 之——Java 内存区域与溢出异常

    更好的阅读体验 huge{color{red}{更好的阅读体验}} 更好的阅读体验 本篇为深入理解 Java 虚拟机第二章内容,推荐在学习前先掌握基础的 Linux 操作、编译原理、计算机组成原理等计算机基础以及扎实的 C/C++ 功底。 该系列的 GitHub 仓库:https://github.com/Doge2077/learn-jvm Java 虚拟机在

    2024年02月09日
    浏览(63)
  • Android内存泄漏分析及检测工具LeakCanary简介,Android进阶

    @Synchronized override fun expectWeaklyReachable( watchedObject: Any, description: String ) { if (!isEnabled()) { return } removeWeaklyReachableObjects() val key = UUID.randomUUID() .toString() val watchUptimeMillis = clock.uptimeMillis() val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue) SharkLog.d { \\\"Watching \\\" +

    2024年04月25日
    浏览(41)
  • 什么是内存溢出,什么是内存泄漏?

    提示:以下是本篇文章正文内容,下面案例可供参考 假设我们 JVM 中可用的内存空间只剩下 3M,但是我们要创建一个 5M 的对象,那么,新创建的对象就放不进去了。这个时候,我们就叫做内存溢出。就好比是一个容量只有 300ml 的水杯,我们硬要往里面倒500ml 的水,这时候,

    2024年02月12日
    浏览(43)
  • 性能优化-内存泄漏、内存溢出、cpu占用高、死锁、栈溢出详解

    含义:内层泄露是程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费。(换言之,GC回收不了这些不再被使用的对象,这些对象的生命周期太长) 危害:当应用程序长时间连续运行时,会导致严重的性能下降;OOM;偶尔会耗尽连接对象;可

    2024年01月19日
    浏览(68)
  • Android 内存泄漏、性能分析常用工具

    一、内存泄漏 1、 MAT-eclipse :“Memory Analyzer Tool”,一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。 2、Leakcanary :一款开源的自动检测内存泄漏的工具。 3、AndroidStudio Profiler :Android Studio 3.0 采用全新

    2024年02月12日
    浏览(50)
  • android 如何进行内存泄漏检测及解决方法

    内存泄漏是在Android开发中常见的问题之一,它可能导致应用的内存占用逐渐增加,最终影响应用的性能和稳定性。以下是一些常见的方法来进行内存泄漏检测和解决: 1. 使用工具进行内存泄漏检测: Android Profiler: Android Studio提供的Android Profiler工具可以帮助您监视应用的内

    2024年02月07日
    浏览(51)
  • 【Android】一个contentResolver引起的内存泄漏问题分析

    长时间的压力测试后,系统发生了重启,报错log如下 JNI ERROR (app bug): global reference table overflow (max=51200) global reference table overflow的log 08-08 04:11:53.052912   973  3243 F zygote64: indirect_reference_table.cc:256] JNI ERROR (app bug): global reference table overflow (max=51200) 08-08 04:11:53.053014   973  3243 F z

    2024年02月08日
    浏览(37)
  • jvm 程序计算器 程序计数器是否溢出 程序计数器是做什么的 java程序计数器会内存溢出吗 程序计数器作用与用处 jvm内存模型 jvm合集(一)

    1. jvm内存模型:     内存模型:                     程序计数器                     堆                     栈                     本地方法栈                     方法区 2. java代码编译为class文件,由类加载器加载到jvm,然后

    2024年02月09日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包