MicroKibaco / CrazyDailyQuestion

每日一问: 水滴石穿,聚沙成塔,坚持数月, 必有收获~
35 stars 1 forks source link

2019-08-15:内存溢出和内存泄漏分别是什么,他们之间有什么关系? #13

Open liu1813565583 opened 5 years ago

pioneerz commented 5 years ago

区别: 1.内存泄漏是指对象在申请内存后,却无法释放。我们比较常见的内存泄漏有:a.Handler引用外部类的引用造成的泄漏;b.资源对象没有关闭,比如cursor,inputStream等对象 c.广播动态注册,生命周期结束没有解绑等。 2.内存溢出是指对象的内存占用已经超出分配的大小。java.lang.OutOfMemoryError就是我们常看到的内存溢出错误,造成内存溢出的原因有:a.资源过大 b.因内存泄漏导致内存到不到回收,进而降低可使用内存的大小,久而久之,可使用内存会越来越小。

联系: 内存泄漏会造成内存溢出

chengying1216 commented 5 years ago

https://www.jianshu.com/p/5353a24f09e4

gys0000 commented 5 years ago

内存溢出

一个5升的水桶,你要装6升的水,不溢出才怪。程序也一样啊,你申请了45M的内存空间,却装载了46M的数据,那肯定会崩溃报错(Out Of Memory)。一般这个在listview或者recycleview加载图片列表时,图片没做优化,就会出现这种问题。

内存泄漏

家里柜子的钥匙有一天被我搞丢了,那这个柜子就不能再被使用了,别人也没办法使用。这种情况就算是内存泄漏。内存被申请使用之后,使用完之后却没有释放,一直被占用着。

内存溢出的的问题我感觉还好解决,因为出现这种问题的时候会崩溃,容易找到问题的发生点。 内存泄漏的问题比较不容易察觉出来,因为他一般不会造成影响;可是当内存泄漏累积到一定程度的时候,就会在程内存溢出等一些问题。 着重分析一下内存的泄漏问题: 下边讲几种容易遇到的情况 第一种场景:静态变量持有了Activity 当静态变量持有了activity时,因为静态变量的生命周期和应用一样长,那么activity就无法被垃圾回收机制回收,这样就造成了内存泄漏。 第二种:匿名内部类会持有外部类的引用,使用不慎会造成内存泄漏。

void spawnThread() {
        new Thread() {
            @Override public void run() {
                while(true);
            }
        }.start();
    }

第三种:Handler Handler会持有activity的引用,当发送延迟消息的时候,activity已经推出来,可是消息还在发送中,那么activity还在被引用着,就会造成内存泄漏。 第四种:单例造成的内存泄漏 单例的静态特性使得他的生命周期特别长,使用的时候需要注意 第五种:资源未关闭造成的内存泄漏

对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,从而造成内存泄漏。

第六种:webview造成的泄漏

当我们不要使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存,否则其长期占用的内存也不能被回收,从而造成内存泄露。

解决方法: 1.静态变量不要持有activity,如果可以的话,尽量把静态变量改为一般变量。 2.将非静态内部类匿名内部类替换为静态内部类,这样就不会有外部类的引用。静态内部类中使用弱引用来引用外部类的成员变量。 3.在activity的ondestory方法中调用handler.removeCallbacksAndMessages(null);,这样可以一处所有的消息和回调。 4.慎用static,因为静态数据不会被回收掉 5.及时回收需要回收的资源,对于不再需要使用的对象,显示的将其赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null。 6.保持对生命周期的敏感

liu1813565583 commented 5 years ago

内存溢出:是指当对象的内存占用已经超出分配内存的空间大小,这时未经处理的异常就会抛出。比如常见的内存溢出情况有:bitmap过大;引用没释放;资源对象没关闭。

内存泄漏:有些对象只有有限的生命周期。当它们的任务完成之后,它们将被垃圾回收。如果在对象的生命周期本该结束的时候,这个对象还被一系列的引用,这就会导致内存泄漏。随着泄漏的累积,app将消耗完内存。比如,在Activity.onDestroy()被调用之后,view树以及相关的bitmap都应该被垃圾回收。如果一个正在运行的后台线程继续持有这个Activity的引用,那么相关的内存将不会被回收,这最终将导致OutOfMemoryError崩溃。 memory leak会最终会导致out of memory! 两者的关系

内存泄漏是造成内存溢出(OOM)的主要原因,因为系统分配给每个程序的内存也就是Heap size的值都是有限的,当内存泄漏到一定值的时候,最终会发生程序所需要的内存值加上泄漏值大于了系统所分配的内存额度,就是触发内存溢出

zhengjunke commented 5 years ago

内存溢出:OOM(OutOfMemory),指的是程序所需内存超出了虚拟机所分配的最大内存范围,除了程序计数器以外,虚拟机内存的其他几个运行时区域都有可能发生OOM。 1、Java堆溢出:程序创建了过多对象,超过了最大堆的容量,且没被GC回收,则会发生对溢出; 2、虚拟机栈和本地方法栈溢出:如果线程请求的最大栈深度超过了虚拟机所允许的最大栈深度,则会抛出StackOverflow异常;如果虚拟机在扩展栈时无法申请到足够的内存空间,则会抛出OutOfMemory异常; 3、方法区和运行时常量池溢出 :运行时常量池是方法区的一部分。方法区用于存放Class的相关信息,如类名,访问修饰符,常量池,字段描述,方法描述等。这一点溢出原因还未弄懂; 内存泄漏:指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。Java内存泄漏的根本原因是长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收。主要有以下几种情况: 1、集合里面的对象属性被修改,再调用remove()方法不生效; 2、各种连接:比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。 3、内部类和外部模块的引用 ;

skylarliuu commented 5 years ago
  1. 内存溢出:当JVM无法给程序分配够程序需要的内存时,就会抛出OutOfMemoryError异常。

  2. 内存泄漏:当生命周期长的对象持有生命周期短的对象的引用时,生命周期短的对象即使无用了,也不会被GC回收,此时发生了内存泄漏。

  3. 常见发生内存泄漏的场景:

    • 匿名内部类和非静态内部类持有外部类的引用(非静态内部类的Handler)
    • 单例持有Activity的引用
    • 当集合里面的对象属性被修改后,hashcode值已发生改变,再调用remove()方法时删除不掉这个对象
    • 各种连接,比如数据库连接(dataSourse.getConnection()),网络连接(socket)和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。
  4. 当内存泄漏现象越来越严重时,大量无用的对象还是存储在Java堆中得不到释放,就可能导致JVM的可用内存越来越少,当JVM没有足够的内存分配给程序时,就会产生内存溢出。

liyilin-jack commented 5 years ago

先来说说OOM(内存溢出)

说到OOM(内存溢出)就不得不提jvm的内存区域划分了。

JVM中把内存划分为以下几个区域:

还有一个特殊的区域,直接内存,这块区域是不属于jvm管理的,是由java中的directByteBuffer持 有该区域的指针对其进行操作。

其中gc堆是内存的主要管理区域,一般发生OOM也是在这块区域中。 这块区域中的OOM主要是因为,申请总内存超过分配给进程内存大小导致的

然后再来说下jvm栈,该区域也会引发OOM异常,该区域的异常分为两种情况:

  1. 无法申请到足够的内存空间时(如单次申请的连续内存空间不足),会导致OOM异常
  2. 线程请求的栈深超过设置的栈深度时,会触发stackOverflow异常

另外方法区(又被成为永久带,PerGen)也可能会出现OOM异常(OutOMemoryError:PerGen space) 方法区具体包括,常量池和运行时常量池,用来存储类信息,常量,静态变量等,和GC堆都属于线程共享的内存区域。当动态创建大量的类,或存储过多的常量时,就有可能导致OOM异常

另外直接内存虽然不受JVM内存管理,但是当其申请的总大小超过真实的物理内存时,仍然会引发OOM异常,这也是OOM的原因之一。

再来说说内存泄漏

内存泄漏涉及到了jvm中的对象自动回收技术(GC),这一块研究的还不深,过一段时间再来总结 但是内存泄漏的严重程度比OOM要低很多,只是GC回收失败了而已。但是过多的内存泄漏也可能会导致OOM,具体属于哪块内存区域的OOM就要具体分析了

SeniorDoctorHui commented 5 years ago

1,内存泄漏指的是创建对象已不需要使用,但是由于被长周期对象所引用,造成该对象到GC Root可达,故不被gc回收,从而使该对象所占有的内存无法释放。常见的内存泄漏场景如下:

1,非静态内部类持有外部类的引用,并且它的周期大于外部类的生命周期。(使用了延时的handler或者开启了线程) 2,对象引用被静态对象所持有的,因为静态对象一旦创建则其生命周期等同于整个app生命周期 3,mvp模型中,p持有v的引用,而在v销毁时,没有把p中v的引用置为null

对于内存泄漏,我们可以采取以下方法

1,尽量不要让静态对象引用持有Activity或者fragment,如果一定要持有引用,可以持有对象的弱引用,因为只持有弱引用的情况下,并不影响gc对对象内存的回收。 2,可以在短周期对象生命周期结束时,将长周期对象对它的持有引用置为null(一般适用于有明显生命周期的对象,如Activity,Fragment等) 3,针对于handler延时发送,而可能发生的内存泄漏,可以在Activity的onDestroy方法调用handler.removeCallbacksAndMessages(null).此时handler所持有的MessageQueue将移除所有handler发送的延迟消息,因此就不会造成内存泄漏了。

2,内存溢出是指要创建的对象所需分配的内存大于所剩内存的情况,这时候就会抛出OutOfMemory的错误。对于内存溢出,我们所以采取以下做法来减少内存溢出的发生几率

1,在bitmap不需要使用时,将bimap置为null,尽早让gc能够回收,释放内存 2,使用统一的图片加载框架,因为图片框架使用了LRUCache来管理bitmap的内存,使用相同的图片加载框架能统一管理加载bitmap的内存,减少Bitmap发生几率。 两者关系:内存泄漏发生多了有可能导致内存溢出的发生

devzhan commented 5 years ago

内存溢出:JVM无法给程序分配够程序需要的内存时,就会抛出OutOfMemoryError异常。 内存泄漏:长生命周期的对象对短生命周期对象引用,短生命周期对象无法及时释放,导致无法被回收,造成的内存紧张现象。 两者总体来说前者是站在整个应用的角度,因为虚拟机对每个应用分配的空间是指定的。后者是站在单个对象实体来说。后者的不正常,将会影响整体。