iis服务器助手广告
返回顶部
首页 > 资讯 > 精选 >Java内存泄露的理解与解决是怎样的
  • 408
分享到

Java内存泄露的理解与解决是怎样的

2023-06-17 12:06:34 408人浏览 八月长安
摘要

这篇文章给大家介绍Java内存泄露的理解与解决是怎样的,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Java内存管理机制在c++ 语言中,如果需要动态分配一块内存,程序员需要负责这块内存的整个生命周期。从申请分配、到使

这篇文章给大家介绍Java内存泄露的理解与解决是怎样的,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

Java内存管理机制

c++ 语言中,如果需要动态分配一块内存,程序员需要负责这块内存的整个生命周期。从申请分配、到使用、再到***的释放。这样的过程非常灵活,但是却十分繁琐,程序员很容易由于疏忽而忘记释放内存,从而导致内存的泄露。 Java 语言对内存管理做了自己的优化,这就是垃圾回收机制。 Java 的几乎所有内存对象都是在堆内存上分配(基本数据类型除外),然后由 GC ( garbage collection)负责自动回收不再使用的内存。

上面是Java 内存管理机制的基本情况。但是如果仅仅理解到这里,我们在实际的项目开发中仍然会遇到内存泄漏的问题。也许有人表示怀疑,既然 Java 的垃圾回收机制能够自动的回收内存,怎么还会出现内存泄漏的情况呢?这个问题,我们需要知道 GC 在什么时候回收内存对象,什么样的内存对象会被 GC 认为是“不再使用”的。

Java中对内存对象的访问,使用的是引用的方式。在 Java 代码中我们维护一个内存对象的引用变量,通过这个引用变量的值,我们可以访问到对应的内存地址中的内存对象空间。在 Java 程序中,这个引用变量本身既可以存放堆内存中,又可以放在代码栈的内存中(与基本数据类型相同)。 GC 线程会从代码栈中的引用变量开始跟踪,从而判定哪些内存是正在使用的。如果 GC 线程通过这种方式,无法跟踪到某一块堆内存,那么 GC 就认为这块内存将不再使用了(因为代码中已经无法访问这块内存了)。

Java内存泄露的理解与解决是怎样的

通过这种有向图的内存管理方式,当一个内存对象失去了所有的引用之后,GC 就可以将其回收。反过来说,如果这个对象还存在引用,那么它将不会被 GC 回收,哪怕是 Java 虚拟机抛出 OutOfMemoryError 。

Java内存泄露

一般来说内存泄漏有两种情况。一种情况如在C/C++ 语言中的,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。***种情况,在 Java 中已经由于垃圾回收机制的引入,得到了很好的解决。所以, Java 中的内存泄漏,主要指的是第二种情况。

    可能光说概念太抽象了,大家可以看一下这样的例子:

Vector v = new  Vector( 10 );  for  ( int  i = 1 ;i < 100 ; i ++ ){  Object o = new  Object();  v.add(o);  o = null ;  }

在这个例子中,代码栈中存在Vector 对象的引用 v 和 Object 对象的引用 o 。在 For 循环中,我们不断的生成新的对象,然后将其添加到 Vector 对象中,之后将 o 引用置空。问题是当 o 引用被置空后,如果发生 GC ,我们创建的 Object 对象是否能够被 GC 回收呢?答案是否定的。因为, GC 在跟踪代码栈中的引用时,会发现 v 引用,而继续往下跟踪,就会发现 v 引用指向的内存空间中又存在指向 Object 对象的引用。也就是说尽管 o 引用已经被置空,但是 Object 对象仍然存在其他的引用,是可以被访问到的,所以 GC 无法将其释放掉。如果在此循环之后, Object 对象对程序已经没有任何作用,那么我们就认为此 Java 程序发生了内存泄漏。

尽管对于C/C++ 中的内存泄露情况来说, Java 内存泄露导致的破坏性小,除了少数情况会出现程序崩溃的情况外,大多数情况下程序仍然能正常运行。但是,在移动设备对于内存和 CPU 都有较严格的限制的情况下, Java 的内存溢出会导致程序效率低下、占用大量不需要的内存等问题。这将导致整个机器性能变差,严重的也会引起抛出 OutOfMemoryError ,导致程序崩溃。

一般情况下内存泄漏的避免

在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度。我们有时也将其称为“对象游离”。

例如:

public class FileSearch{        private byte [] content;        private File mFile;       public FileSearch(File file){        mFile = file;        }       public boolean hasString(String str){           int size = getFileSize(mFile);          content =  new  byte [size];           loadFile(mFile, content);           String s =  new String(content);           return s.contains(str);       }  }

在这段代码中,FileSearch 类中有一个函数 hasString ,用来判断文档中是否含有指定的字符串。流程是先将mFile 加载到内存中,然后进行判断。但是,这里的问题是,将 content 声明为了实例变量,而不是本地变量。于是,在此函数返回之后,内存中仍然存在整个文件的数据。而很明显,这些数据我们后续是不再需要的,这就造成了内存的无故浪费。

要避免这种情况下的内存泄露,要求我们以C/C++ 的内存管理思维来管理自己分配的内存。***,是在声明对象引用之前,明确内存对象的有效作用域。在一个函数内有效的内存对象,应该声明为 local 变量,与类实例生命周期相同的要声明为实例变量&hellip;&hellip;以此类推。第二,在内存对象不再需要时,记得手动将其引用置空。

复杂数据结构中的内存泄露问题

在实际的项目中,我们经常用到一些较为复杂的数据结构用于缓存程序运行过程中需要的数据信息。有时,由于数据结构过于复杂,或者我们存在一些特殊的需求(例如,在内存允许的情况下,尽可能多的缓存信息来提高程序的运行速度等情况),我们很难对数据结构中数据的生命周期作出明确的界定。这个时候,我们可以使用Java 中一种特殊的机制来达到防止内存泄露的目的。

之前我们介绍过,Java 的 GC 机制是建立在跟踪内存的引用机制上的。而在此之前,我们所使用的引用都只是定义一个“ Object o; ”这样形式的。事实上,这只是 Java 引用机制中的一种默认情况,除此之外,还有其他的一些引用方式。通过使用这些特殊的引用机制,配合 GC 机制,就可以达到一些我们需要的效果。

Java中的几种引用方式

Java中有几种不同的引用方式,它们分别是:强引用、软引用、弱引用和虚引用。下面,我们首先详细地了解下这几种引用方式的意义。

强引用

在此之前我们介绍的内容中所使用的引用 都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

软引用(SoftReference )

SoftReference 类的一个典型用途就是用于内存敏感的高速缓存。 SoftReference  的原理是:在保持对对象的引用时保证在  JVM  报告内存不足情况之前将清除所有的软引用。关键之处在于,垃圾收集器在运行时可能会(也可能不会)释放软可及对象。对象是否被释放取决于垃圾收集器的算法 以及垃圾收集器运行时可用的内存数量。

弱引用(WeakReference )

WeakReference 类的一个典型用途就是规范化映射( canonicalized mapping )。另外,对于那些生存期相对较长而且重新创建的开销也不高的对象来说,弱引用也比较有用。关键之处在于,垃圾收集器运行时如果碰到了弱可及对象,将释放  WeakReference  引用的对象。然而,请注意,垃圾收集器可能要运行多次才能找到并释放弱可及对象。

虚引用(PhantomReference )

PhantomReference 类只能用于跟踪对被引用对象即将进行的收集。同样,它还能用于执行  pre-mortem  清除操作。 PhantomReference  必须与  ReferenceQueue  类一起使用。需要  ReferenceQueue  是因为它能够充当通知机制。当垃圾收集器确定了某个对象是虚可及对象时, PhantomReference  对象就被放在它的  ReferenceQueue  上。将  PhantomReference  对象放在  ReferenceQueue  上也就是一个通知,表明  PhantomReference  对象引用的对象已经结束,可供收集了。这使您能够刚好在对象占用的内存被回收之前采取行动。 Reference与 ReferenceQueue 的配合使用。

GC、 Reference 与 ReferenceQueue 的交互

A、  GC无法删除存在强引用的对象的内存。

B、  GC发现一个只有软引用的对象内存,那么:

①  SoftReference对象的 referent  域被设置为 null ,从而使该对象不再引用 heap 对象。

②  SoftReference引用过的 heap 对象被声明为 finalizable 。

③  当 heap  对象的  finalize()  方法被运行而且该对象占用的内存被释放, SoftReference  对象就被添加到它的  ReferenceQueue (如果后者存在的话)。

C、  GC发现一个只有弱引用的对象内存,那么:

①  WeakReference对象的 referent 域被设置为 null , 从而使该对象不再引用heap 对象。

②  WeakReference引用过的 heap 对象被声明为 finalizable 。

③  当heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放时, WeakReference 对象就被添加到它的 ReferenceQueue (如果后者存在的话)。

D、  GC发现一个只有虚引用的对象内存,那么:

①  PhantomReference引用过的 heap 对象被声明为 finalizable 。

②  PhantomReference在堆对象被释放之前就被添加到它的 ReferenceQueue 。

值得注意的地方有以下几点:

GC 在一般情况下不会发现软引用的内存对象,只有在内存明显不足的时候才会发现并释放软引用对象的内存。

GC 对弱引用的发现和释放也不是立即的,有时需要重复几次 GC ,才会发现并释放弱引用的内存对象。
3、软引用和弱引用在添加到 ReferenceQueue 的时候,其指向真实内存的引用已经被置为空了,相关的内存也已经被释放掉了。而虚引用在添加到 ReferenceQueue 的时候,内存还没有释放,仍然可以对其进行访问。

代码示例

通过以上的介绍,相信您对Java 的引用机制以及几种引用方式的异同已经有了一定了解。光是概念,可能过于抽象,下面我们通过一个例子来演示如何在代码中使用 Reference 机制。

String str  =   new  String( " hello " );  // ①   ReferenceQueue < String >  rq  =   new  ReferenceQueue < String > ();  // ②   WeakReference < String >  wf  =   new  WeakReference < String > (str, rq);  // ③   str = null ;  // ④取消"hello"对象的强引用   String str1 = wf.get();  // ⑤假如"hello"对象没有被回收,str1引用"hello"对象  // 假如"hello"对象没有被回收,rq.poll()返回null   Reference <?   extends  String >  ref = rq.poll();  // ⑥

在以上代码中,注意⑤⑥两处地方。假如“hello ”对象没有被回收 wf.get() 将返回“ hello ”字符串对象, rq.poll() 返回 null ;而加入“ hello ”对象已经被回收了,那么 wf.get() 返回 null , rq.poll() 返回 Reference 对象,但是此 Reference 对象中已经没有 str 对象的引用了 ( PhantomReference 则与WeakReference 、 SoftReference 不同 )。

引用机制与复杂数据结构的联合应用

了解了GC 机制、引用机制,并配合上 ReferenceQueue ,我们就可以实现一些防止内存溢出的复杂数据类型。

例如,SoftReference 具有构建 Cache 系统的特质,因此我们可以结合哈希表实现一个简单的缓存系统。这样既能保证能够尽可能多的缓存信息,又可以保证 Java 虚拟机不会因为内存泄露而抛出 OutOfMemoryError 。这种缓存机制特别适合于内存对象生命周期长,且生成内存对象的耗时比较长的情况,例如缓存列表封面图片等。对于一些生命周期较长,但是生成内存对象开销不大的情况,使用WeakReference 能够达到更好的内存管理的效果。

附SoftHashMap源码一份,相信看过之后,大家会对 Reference 机制的应用有更深入的理解。

package  com. *** .widget;      // : SoftHashMap.java        import  java.util. * ;       import  java.lang.ref. * ;       import  Android.util.Log;           public   class  SoftHashMap  extends  AbstractMap  {                   private   final  Map hash  =   new  HashMap();                   private   final   int  HARD_SIZE;                   private   final  LinkedList hardCache  =   new  LinkedList();                   private  ReferenceQueue queue  =   new  ReferenceQueue();         // Strong Reference number         public  SoftHashMap()  {  this ( 100 ); }          public  SoftHashMap( int  hardSize)  { HARD_SIZE  =  hardSize; }                 public  Object get(Object key)  {          Object result  =   null ;           //  We get the SoftReference represented by that key           SoftReference soft_ref  =  (SoftReference)hash.get(key);           if  (soft_ref  !=   null )  {             //  From the SoftReference we get the value, which can be             //  null if it was not in the map, or it was removed in             //  the processQueue() method defined below            result  =  soft_ref.get();             if  (result  ==   null )  {               //  If the value has been garbage collected, remove the               //  entry from the HashMap.               hash.remove(key);            }   else   {               //  We now add this object to the beginning of the hard               //  reference queue.  One reference can occur more than               //  once, because lookups of the FIFO queue are slow, so               //  we don't want to search through it each time to remove               //  duplicates.                 // keep recent use object in memory              hardCache.addFirst(result);               if  (hardCache.size()  >  HARD_SIZE)  {                 //  Remove the last entry if list longer than HARD_SIZE                 hardCache.removeLast();              }             }           }            return  result;        }                        private   static   class  SoftValue  extends  SoftReference  {           private   final  Object key;  //  always make data member final                        private  SoftValue(Object k, Object key, ReferenceQueue q)  {             super (k, q);             this .key  =  key;          }         }                        public   void  processQueue()  {          SoftValue sv;           while  ((sv  =  (SoftValue)queue.poll())  !=   null )  {               if (sv.get() ==   null ) {                 Log.e( " processQueue " ,  " null " );             } else {                 Log.e( " processQueue " ,  " Not null " );             }            hash.remove(sv.key);  //  we can access private data!            Log.e( " SoftHashMap " ,  " release  "   +  sv.key);         }         }                    public  Object put(Object key, Object value)  {          processQueue();  //  throw out garbage collected values first           Log.e( " SoftHashMap " ,  " put into  "   +  key);          return  hash.put(key,  new  SoftValue(value, key, queue));        }          public  Object remove(Object key)  {          processQueue();  //  throw out garbage collected values first            return  hash.remove(key);        }          public   void  clear()  {          hardCache.clear();          processQueue();  //  throw out garbage collected values          hash.clear();       }         public   int  size()  {         processQueue();  //  throw out garbage collected values first           return  hash.size();       }         public  Set entrySet()  {          //  no, no, you may NOT do that!!! GRRR           throw   new  UnsupportedOperationException();       }     }

关于Java内存泄露的理解与解决是怎样的就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

--结束END--

本文标题: Java内存泄露的理解与解决是怎样的

本文链接: https://www.lsjlt.com/news/288898.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java内存泄露的理解与解决是怎样的
    这篇文章给大家介绍Java内存泄露的理解与解决是怎样的,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Java内存管理机制在C++ 语言中,如果需要动态分配一块内存,程序员需要负责这块内存的整个生命周期。从申请分配、到使...
    99+
    2023-06-17
  • Android内存泄露怎么解决
    解决Android内存泄露问题的方法有以下几种:1. 避免长生命周期的引用:确保在不使用时及时释放对象的引用,如Activity中的...
    99+
    2023-09-29
    android
  • Java 内存泄露问题详解
    目录 1、什么是内存泄露? 2、Java 中可能导致内存泄露的场景 3、长生命周期对象持有短生命周期对象引用造成的内存泄露问题示例 4、静态集合类持有对象引用造成内存泄露问题的示例 1、什么是内存泄露?         内存泄露指的是程...
    99+
    2023-09-08
    Java 内存泄露
  • 怎么用JS理解IE的内存泄露
    这篇文章主要讲解了“怎么用JS理解IE的内存泄露”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用JS理解IE的内存泄露”吧!一、前言IE6~8除了不遵守...
    99+
    2024-04-02
  • golang函数内存泄露检测与解决
    go 语言中存在函数内存泄露,它会导致应用程序内存不断消耗和崩溃。我们可以使用 runtime/pprof 包进行检测,并检查函数是否意外保留了对不需要资源的引用。要解决内存泄露,我们需...
    99+
    2024-04-23
    golang 内存泄露
  • Java中的内存泄露问题和解决办法
    目录为什么会产生内存泄漏?内存泄漏对程序的影响?如何检查和分析内存泄漏?常见的内存泄漏及解决方法1、单例造成的内存泄漏2、非静态内部类创建静态实例造成的内存泄漏【已无】3、Handl...
    99+
    2024-04-02
  • win11内存泄露如何解决
    这篇文章主要介绍“win11内存泄露如何解决”,在日常操作中,相信很多人在win11内存泄露如何解决问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”win11内存泄露如何解决”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-06-30
  • Java 中出现内存泄露如何解决
    这期内容当中小编将会给大家带来有关Java 中出现内存泄露如何解决,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。首先,我用下面的命令监视进程:while ( sleep 1&...
    99+
    2023-06-17
  • ehcache内存泄露如何解决
    解决Ehcache内存泄漏的问题可以尝试以下几个方法:1. 升级Ehcache版本:确保使用的是最新的Ehcache版本,因为较新的...
    99+
    2023-09-13
    ehcache
  • qt程序内存泄露怎么解决
    解决Qt程序内存泄漏的方法如下:1. 使用对象的父子关系:在创建对象时,将对象的父对象设置为合适的父对象。这样,当父对象被销毁时,它...
    99+
    2023-08-18
    qt
  • android内存泄露怎么查看和解决
    Android内存泄露是指内存中的对象无法被及时释放,导致内存占用过高,影响应用的性能和稳定性。以下是查看和解决Android内存泄...
    99+
    2024-03-13
    android
  • Python内存泄露怎么查看和解决
    在Python中,内存泄露指的是由于对象在不再需要时没有被正确释放,导致内存占用不断增加的情况。下面是一些查找和解决Python内存...
    99+
    2023-10-22
    Python
  • Java语言中的内存泄露代码详解
    Java的一个重要特性就是通过垃圾收集器(GC)自动管理内存的回收,而不需要程序员自己来释放内存。理论上Java中所有不会再被利用的对象所占用的内存,都可以被GC回收,但是Java也存在内存泄露,但它的表现与C++不同。JAVA中的内存管理...
    99+
    2023-05-30
    java 内存泄露 实例
  • ThreadLocal作用原理与内存泄露示例解析
    目录ThreadLocal作用简单例子局部变量、成员变量 、 ThreadLocal、静态变量共享 or 隔离原理源码分析TheadLocalTheadLocalMapThreadL...
    99+
    2024-04-02
  • Java中内存泄露与溢出的区别是什么
    这期内容当中小编将会给大家带来有关Java中内存泄露与溢出的区别是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Java内存泄露与溢出的区别内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足...
    99+
    2023-06-17
  • qt内存泄露的原因及解决办法是什么
    Qt内存泄漏的原因通常是因为对象在不再使用时没有被正确释放,导致内存无法被回收。以下是一些常见的Qt内存泄漏的原因和解决办法:1. ...
    99+
    2023-09-16
    qt
  • java内存管理关系及内存泄露的原理分析
    目录java内存管理关系及内存泄露原理java对象和内存的关系创建对象null的作用内存泄露检测内存泄露的原理java内存管理关系及内存泄露原理 这可能是最近写的博客中最接近底层的了...
    99+
    2024-04-02
  • ThreadLocal在Tomcat中引起内存泄露怎么解决
    这篇文章主要介绍“ThreadLocal在Tomcat中引起内存泄露怎么解决”,在日常操作中,相信很多人在ThreadLocal在Tomcat中引起内存泄露怎么解决问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答...
    99+
    2023-06-05
  • 内存泄露的原因是什么
    这篇文章主要介绍“内存泄露的原因是什么”,在日常操作中,相信很多人在内存泄露的原因是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”内存泄露的原因是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!Th...
    99+
    2023-06-16
  • Flex中出现内存泄露如何解决
    Flex中出现内存泄露如何解决,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Flex内存泄露举例:引用泄露:对子对象的引用,外部对本对象或子对象的引用都需要置null;系统类泄...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作