Java中的内存泄漏

1.什么是内存泄漏

   简单地讲,内存泄漏就是一个不在使用的对象或者变量一直占用内存,导致 GC 不能回收此对象。

2.为什么会产生内存泄漏呢

     java里面不是有垃圾回收机制嘛,怎么又会造成内存泄漏(GC不回收的情况呢)?

     我们先来了解一下两种比较常用的对象存活判断的方法:引用计数法 与 可达性分析法

     引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。

         缺陷   :循环引用问题,也就是我引用你,你也引用我,我们的引用数量都不为0,加入你我在程序中都没有用到,那么你我都没用,但是我们都不会被回收  这不就产生内存泄漏了嘛

     可达性分析法:从GCroots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。

Java中的内存泄漏

目前主流的虚拟机中大多使用可达性分析的方式来判定对象是否可被 GC 回收。

可达性分析法也是会产生内存泄漏的,当符合两个条件的时候

  1.对象是可达的(也就是有强引用指向该对象)

 2.该对象是无用的

3.内存泄漏的一些例子

静态集合类:在使用Set、Vector、HashMap等集合类的时候需要特别注意,有可能会发生内存泄漏。当这些集合被定义成静态的时候,由于它们的生命周期跟应用程序一样长,这时候,就有可能会发生内存泄漏。

监听器:在Java中,我们经常会使用到监听器,如对某个控件添加单击监听器addOnClickListener(),但往往释放对象的时候会忘记删除监听器,这就有可能造成内存泄漏。好的方法就是,在释放对象的时候,应该记住释放所有监听器,这就能避免了因为监听器而导致的内存泄漏。

各种连接:Java中的连接包括数据库连接、网络连接和io连接,如果没有显式调用其close()方法,是不会自动关闭的,这些连接就不能被GC回收而导致内存泄漏。一般情况下,在try代码块里创建连接,在finally里释放连接,就能够避免此类内存泄漏。

外部模块的引用:调用外部模块的时候,也应该注意防止内存泄漏。如模块A调用了外部模块B的一个方法,如:public void register(Object o)。这个方法有可能就使得A模块持有传入对象的引用,这时候需要查看B模块是否提供了去除引用的方法,如unregister()。这种情况容易忽略,而且发生了内存泄漏的话,比较难察觉,应该在编写代码过程中就应该注意此类问题。

单例模式:使用单例模式的时候也有可能导致内存泄漏。因为单例对象初始化后将在JVM的整个生命周期内存在,如果它持有一个外部对象(生命周期比较短)的引用,那么这个外部对象就不能被回收,而导致内存泄漏。如果这个外部对象还持有其它对象的引用,那么内存泄漏会更严重,因此需要特别注意此类情况。这种情况就需要考虑下单例模式的设计会不会有问题,应该怎样保证不会产生内存泄漏问题

 

4.内存泄漏与内存溢出

   内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出

 

5.如何解决或者排查内存泄漏