返回顶部
首页 > 资讯 > 精选 >轻松搞定Java内存泄漏(转)
  • 915
分享到

轻松搞定Java内存泄漏(转)

2023-06-03 06:06:17 915人浏览 安东尼
摘要

轻松搞定Java内存泄漏(转)[@more@] 抽象  尽管java虚拟机和垃圾回收机制管理着大部分的内存事务,但是在java软件中还是可能存在内存泄漏的情况。的确,在大型工程中,内存泄漏是一个普遍问题。避免内存泄漏的第一步,就是要了解他们

轻松搞定Java内存泄漏(转)[@more@] 抽象

  尽管java虚拟机和垃圾回收机制管理着大部分的内存事务,但是在java软件中还是可能存在内存泄漏的情况。的确,在大型工程中,内存泄漏是一个普遍问题。避免内存泄漏的第一步,就是要了解他们发生的原因。这篇文章就是要介绍一些常见的缺陷,然后提供一些非常好的实践例子来指导你写出没有内存泄漏的代码。一旦你的程序存在内存泄漏,要查明代码中引起泄漏的原因是很困难的。同时这篇文章也要介绍一个新的工具来查找内存泄漏,然后指明发生的根本原因。这个工具容易上手,可以让你找到产品级系统中的内存泄漏。

  垃圾回收(GC)的角色

  虽然垃圾回收关心着大部分的问题,包括内存管理,使得程序员的任务显得更加轻松,但是程序员还是可能犯些错误导致内存泄漏问题。GC(垃圾回收)通过递归对所有从"根"对象(堆栈中的对象,静态数据成员,JNI句柄等等)继承下来的引用进行工作,然后标记所有可以访问的活着的对象。而这些对象变成了程序唯一能够操纵的对象,其他的对象都被释放了。因为GC使得程序不能够访问那些被释放的对象,所以这样做是安全的。

内存管理可以说是自动的,但是这并没有让程序员脱离内存管理问题。比方说,对于内存的分配(还有释放)总是存在一定的开销,尽管这些开销对程序员来说是暗含的。一个程序如果创建了很多对象,那么它就要比完成相同任务而创建了较少对象的程序执行的速度慢(其他提供的内容都相同)。

  导致内存泄漏主要的原因是,先前申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器验证这些对象是否不再需要。正如我们前面看到的,如果存在对象的引用,这个对象就被定义为"活着的",同时不会被释放。要确定对象所占内存将被回收,程序员就要务必确认该对象不再会被使用。典型的做法就是把对象数据成员设为null或者从集合中移除该对象。注意,当局部变量不需要时,不需明显的设为 null,因为一个方法执行完毕时,这些引用会自动被清理。

  从更高一个层次看,这就是所有存在内存管的语言对内存泄漏所考虑的事情,剩余的对象引用将不再会被使用。

  典型泄漏

  既然我们知道了在java中确实会存在内存泄漏,那么就让我们看一些典型的泄漏,并找出他们发生的原因。

  全局集合

  在大型应用程序中存在各种各样的全局数据仓库是很普遍的,比如一个JNDI-tree或者一个session table。在这些情况下,注意力就被放在了管理数据仓库的大小上。当然是有一些适当的机制可以将仓库中的无用数据移除。

  可以有很多不同的解决形式,其中最常用的是一种周期运行的清除作业。这个作业会验证仓库中的数据然后清除一切不需要的数据。
另一个办法是使用引用计算。集合用来对了解每个集合入口关联器(referrer)的数目负责。这要求关联器通知集合什么时候完成进入。当关联器的数目为零时,就可以移除集合中的相关元素。

  高速缓存

  高速缓存是一种用来快速查找已经执行过的操作结果的数据结构。因此,如果一个操作执行很慢的话,你可以先把普通输入的数据放入高速缓存,然后过些时间再调用高速缓存中的数据。

  高速缓存多少还有一点动态实现的意思,当数据操作完毕,又被送入高速缓存。一个典型的算法如下所示:

  1.检查结果是否在高速缓存中,存在则返回结果;

  2.如果结果不在,那么计算结果;

  3.将结果放入高速缓存,以备将来的操作调用。

  这个算法的问题(或者说潜在的内存泄漏)在最后一步。如果操作伴随着一个不同的,输入非常大的数字,那么存入高速缓存的也是一个非常大的结果。那么这个方法就不是能够胜任的了。

  为了避免这种潜在的致命错误设计,程序就必须确定高速缓存在他所使用的内存中有一个上界。因此,更好的算法是:

  1.检查结果是否在高速缓存中,存在则返回结果;

  2.如果结果不在,那么计算结果;

  3.如果高速缓存所占空间过大,移除缓存中旧的结果;

  4.将结果放入高速缓存,以备将来的操作调用。

  通过不断的从缓存中移除旧的结果,我们可以假设,将来,最新输入的数据可能被重用的几率要远远大于旧的结果。这通常是一个不错的设想。

  这个新的算法会确保高速缓存的容量在预先确定的范围内。精确的范围是很难计算的,因为缓存中的对象存在引用时将继续有效。正确的划分高速缓存的大小是一个复杂的任务,你必须权衡可使用内存大小和数据快速存取之间的矛盾。

  另一个解决这个问题的途径是使用java.lang.ref.SoftReference类坚持将对象放入高速缓存。这个方法可以保证当虚拟机用完内存或者需要更多堆的时候,可以释放这些对象的引用。

  类装载器

  Java类装载器创建就存在很多导致内存泄漏的漏洞。由于类装载器的复杂结构,使得很难得到内存泄漏的透视图。这些困难不仅仅是由于类装载器只与"普通的"对象引用有关,同时也和对象内部的引用有关,比如数据变量,方法和各种类。这意味着只要存在对数据变量,方法,各种类和对象的类装载器,那么类装载器将驻留在JVM中。既然类装载器可以同很多的类关联,同时也可以和静态数据变量关联,那么相当多的内存就可能发生泄漏。

  定位内存泄漏

  常常地,程序内存泄漏的最初迹象发生在出错之后,得到一个OutOfMemoryError在你的程序中。这种典型地情况发生在产品环境中,而在那里,你希望内存泄漏尽可能的少,调试的可能性也达到最小。也许你的测试环境和产品的系统环境不尽相同,导致泄露的只会在产品中揭示。这种情况下,你需要一个低内务操作工具来监听和寻找内存泄漏。同时,你还需要把这个工具同你的系统联系起来,而不需要重新启动他或者机械化你的代码。也许更重要的是,当你做分析的时候,你需要能够同工具分离而使得系统不会受到干扰。

  一个OutOfMemoryError常常是内存泄漏的一个标志,有可能应用程序的确用了太多的内存;这个时候,你既不能增加JVM的堆的数量,也不能改变你的程序而使得他减少内存使用。但是,在大多数情况下,一个 OutOfMemoryError是内存泄漏的标志。一个解决办法就是继续监听GC的活动,看看随时间的流逝,内存使用量是否会增加,如果有,程序中一定存在内存泄漏。

  详细输出

  有很多办法来监听垃圾回收器的活动。也许运用最广泛的就是以:-Xverbose:gc选项运行JVM,然后观察输出结果一段时间。

[memory] 10.109-10.235: GC 65536K->16788K (65536K), 126.000 ms

  箭头后的值(在这个例子中 16788K)是垃圾回收后堆的使用量。

  控制台

  观察这些无尽的GC详细统计输出是一件非常单调乏味的事情。好在有一些工具来代替我们做这些事情。The JRockit Management Console可以用图形的方式输出堆的使用量。通过观察图像,我们可以很方便的观察堆的使用量是否伴随时间增长。




400) {this.resized=true; this.width=400; this.alt='Click here to open new window';}" border=0>

Figure 1. The JRockit Management Console


  管理控制台甚至可以配置成在堆使用量出现问题(或者其他的事件发生)时向你发送邮件。这个显然使得监控内存泄漏更加容易。
  内存泄漏探测工具

  有很多专门的内存泄漏探测工具。其中The JRockit Memory Leak Detector可以供来观察内存泄漏也可以针对性地找到泄漏的原因。这个强大的工具被紧密地集成在JRockit JVM中,可以提供最低可能的内存事务也可以轻松的访问虚拟机的堆。

  专门工具的优势

  一旦你知道程序中存在内存泄漏,你需要更专业的工具来查明为什么这里会有泄漏。而JVM是不可能告诉你的。现在有很多工具可以利用了。这些工具本质上主要通过两种方法来得到JVM的存储系统信息的:JVMTI和字节码使用仪器。Java虚拟机工具接口(JVMTI)和他的原有形式JVMPI(压型接口)都是标准接口,作为外部工具同JVM进行通信,搜集JVM的信息。字节码使用仪器则是引用通过探针获得工具所需的字节信息的预处理技术。

  通过这些技术来侦测内存泄漏存在两个缺点,而这使得他们在产品级环境中的运用不够理想。首先,根据两者对内存的使用量和内存事务性能的降级是不可以忽略的。从JVM 获得的堆的使用量信息需要在工具中导出,收集和处理。这意味着要分配内存。按照JVM的性能导出信息是需要开销的,垃圾回收器在搜集信息的时候是运行的非常缓慢的。另一个缺点就是,这些工具所需要的信息是关系到JVM的。让工具在JVM开始运行的时候和它关联,而在分析的时候,分离工具而保持JVM运行,这显然是不可能的。

  既然JRockit Memory Leak Detector是被集成到JVM中的,那么以上两种缺点就不再适用。首先,大部分的处理和分析都是在JVM中完成的,所以就不再需要传送或重建任何数据。处理也可以在垃圾回收器的背上,他的意思是提高速度。再有,内存泄漏侦测器可以同一个运行的JVM关联和分离,只要JVM在开始的时候伴随着 -Xmanagement选项(这个允许监听和管理JVM通过远程JMX接口)。当工具分离以后,工具不会遗留任何东西在JVM中;JVM就可以全速运行代码就好像工具关联之前一样。

 趋势分析

  让我们更深一步来观察这个工具,了解他如何捕捉到内存泄漏。在你了解到代码中存在内存泄漏,第一步就是尝试计算出什么数据在泄漏--哪个对象类导致泄露。The JRockit Memory Leak Detector通过在垃圾回收的时候,计算每个类所包含的现有的对象来达到目的。如果某一个类的对象成员数目随着时间增长(增长率),那么这里很可能存在泄漏。




400) {this.resized=true; this.width=400; this.alt='Click here to open new window';}" border=0>

igure 2. The trend analysis view of the Memory Leak Detector

  因为一个泄漏很可能只是像水滴一样小,所以趋势分析必须运行足够长的一段时间。在每个短暂的时间段里,局部类的增加会使得泄漏发生推迟。但是,内存事务是非常小的(最大的内存事务是由在每个垃圾回收时从JRockit向内存泄漏探测器发送的一个数据包组成的)。内存事务不应该成为任何系统的问题--甚至一个在产品阶段全速运行的程序。

  一开始,数字会有很大的跳转,随时间的推进,这些数字会变得稳定,而后显示哪些类会不断的增大。

  寻找根本原因

  知道那些对象的类会导致泄露,有时候足够制止泄露问题。这个类也许只是被用在非常有限的部分,通过快速的视察就可以找到问题所在。不幸的是,这些信息是不够的。比方说,经常导致内存泄漏的对象类java.lang.String,然而String类被应用于整个程序,这就变得有些无助。

  我们想知道的是其他的对象是否会导致内存泄漏,好比上面提到的String类,为什么这些导致泄漏的对象还是存在周围?那些引用是指向这些对象的?这里一列的对象存有对String类的引用,就会变得太大而没有实际意义。为了限制数据的数量,我们可以通过类把他们编成一个组,这样我们就可以看到,那些其他类的对象会依然泄漏对象(String类)。比如,将一个String类放入Hashtable,那里我们可以看到关联到String类的 Hashtable入口。从Hashtable入口向后运行,我们终于找到那些关联到String类的Hashtable对象(参看图三如下)。




Click here to open new window400) {this.resized=true; this.width=400; this.alt='Click here to open new window';}" border=0 resized="true">

Figure 3. Sample view of the type graph as seen in the tool
  向后工作

  自从开始我们就一直着眼于对象类,而不是单独的对象,我们不知道那个Hashtable存在泄漏。如果我们可以找出所有的Hashtable在系统中有多大,我们可以假设最大的那个Hashtable存在泄漏(因为它可以聚集足够的泄漏而变得很大)。因此,所有Hashtable,同时有和所有他们所涉及的数据,可以帮助我们查明导致泄露的精确的Hashtable。




400) {this.resized=true; this.width=400; this.alt='Click here to open new window';}" border=0>

Figure 4. Screenshot of the list of Hashtable objects and the size of the data they are holding live

  计算一个对象所涉及的数据的开销是非常大的(这要求引用图表伴随着那个对象作为根运行)而且如果对每一个对象都这样处理,就需要很多时间。知道一些关于 Hashtable内部的实现机制可以带来捷径。在内部,一个Hashtable有一个Hashtable的数组入口。数组的增长伴随着 Hashtable中对象的增长。因此,要找到最大的Hashtable,我们可以把搜索限制在寻找包含Hashtable引用入口的最大的数组。这样就更快捷了。




400) {this.resized=true; this.width=400; this.alt='Click here to open new window';}" border=0>

Figure 5. Screenshot of the listing of the largest Hashtable entry arrays, as well as their sizes.

  向下深入

  当我们发现了存在泄漏的Hashtable的实例,就可以顺藤摸瓜找到其他的引用这些Hashtable的实例,然后用上面的方法来找到是那个Hashtable存在问题。




Click here to open new window400) {this.resized=true; this.width=400; this.alt='Click here to open new window';}" border=0 resized="true">

Figure 6. This is what an instance graph can look like in the tool.

  举个例子,一个Hashtable可以有一个来自MyServer的对象的引用,而MyServer包含一个activeSessions数据成员。这些信息就足够深入代码找出问题所在。




400) {this.resized=true; this.width=400; this.alt='Click here to open new window';}" border=0>

Figure 7. Inspecting an object and its references to other objects

  找出分配点

  当发现了内存泄漏问题,找到那些泄漏的对象在何处是非常有用的。也许没有足够的信息知道他们同其他相关对象之间的联系,但是关于他们在那里被创建的信息还是很有帮助的。当然,你不会愿意创建一个工具来打印出所有分配的堆栈路径。你也不会愿意在模拟环境中运行程序只是为了捕捉到一个内存泄漏。

  有了JRockit Memory Leak Detector,程序代码可以动态的在内存分配出创建堆栈路径。这些堆栈路径可以在工具中累积,分析。如果你不启用这个工具,这个特征就不会有任何消耗,这就意味着时刻准备着开始。当需要分配路径时,JRockit的编译器可以让代码不工作,而监视内存分配,但只对需要的特定类有效。更好的是,当做完数据分析后,生成的机械代码会完全被移除,不会引起任何执行上的效率衰退。




400) {this.resized=true; this.width=400; this.alt='Click here to open new window';}" border=0>

Figure 8. The allocation stack traces for String during execution of a sample program

  总结

  内存泄漏查找起来非常困难,文章中的一些避免泄漏的好的实践,包括了要时刻记住把什么放进了数据结构中,更接近的监视内存中意外的增长。

  我们同时也看到了JRockit Memory Leak Detector是如何捕捉产品级系统中的内存泄漏的。该工具通过三步的方法发现泄漏。一,通过趋势分析发现那些对象类存在泄漏;二,找出同泄漏对象相关的其他类;三,向下发掘,观察独立的对象之间是如何相互联系的。同时,该工具也可以动态的,找出所有内存分配的堆栈路径。利用这三个特性,将该工具紧紧地集成在JVM中,那么就可以安全的,有效的捕捉和修复内存泄漏了。

--结束END--

本文标题: 轻松搞定Java内存泄漏(转)

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

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

猜你喜欢
  • 轻松搞定Java内存泄漏(转)
    轻松搞定Java内存泄漏(转)[@more@] 抽象  尽管java虚拟机和垃圾回收机制管理着大部分的内存事务,但是在java软件中还是可能存在内存泄漏的情况。的确,在大型工程中,内存泄漏是一个普遍问题。避免内存泄漏的第一步,就是要了解他们...
    99+
    2023-06-03
  • 一篇文章带你搞定JAVA内存泄漏
    目录1、什么是内存泄漏2、内存泄漏的原因3、内存泄漏有哪些情况3.1 代码中没有及时释放,导致内存无法回收。3.2 资源未关闭造成的内存泄漏3.3 全局缓存持有的对象不使用的时候没有...
    99+
    2024-04-02
  • 【转】Java的内存泄漏
    一 问题的提出Java的一个重要优点就是通过垃圾收集器 (Garbage Collection,GC)自动管理内存的回收,程序员不需要通过调用函数来释放内存。因此,很多程序员认为Java不存在内存泄漏问题,或者认为即使有内存泄漏也不是程序的...
    99+
    2023-06-03
  • Perfdog玩转内存泄漏
    背景交代最近QC同学在跑游戏的过程中发现玩的时间久了游戏会发生闪退,经过搜集信息后排除了功能性bug的一.判断是否是内存泄露拿到真机,USB连接,杀掉多余后台进程,打开Perfdog,接下来一顿操作猛如虎,Perfdog具体操作不在赘述,有...
    99+
    2023-06-05
  • Java 中的内存泄漏
    什么是 Java 中的内存泄漏? 当应用程序持有不再需要的对象引用时,就会发生 Java 内存泄漏。这些意外的对象引用阻止内置的 Java 垃圾收集机制释放这些对象消耗的内存,最终导致致命的OutOfMemoryError。 简而言之,...
    99+
    2023-10-11
    java jvm 开发语言
  • 一文搞懂如何避免JavaScript内存泄漏
    目录一、什么是内存泄漏二、常见的内存泄漏1、意外的全局变量2、 计时器3、 闭包4、 事件监听器5、缓存6、分离的DOM元素三、识别内存泄漏1、使用性能分析器可视化内存消耗2、 识别...
    99+
    2024-04-02
  • Java内存泄漏如何修复
    修复Java内存泄漏的方法如下:1. 避免创建过多的对象:在编写代码时,尽量避免频繁创建大量的对象。可以重用对象,或者使用对象池来管...
    99+
    2023-09-23
    Java
  • java内存泄漏怎么排查
    Java内存泄漏是指在程序运行过程中,不再使用的对象仍然占用着内存空间,导致内存无法被回收。以下是一些常见的排查内存泄漏的方法:1....
    99+
    2023-08-31
    java
  • java怎么检查内存泄漏
    本篇内容介绍了“java怎么检查内存泄漏”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!内存泄漏场景长生命周期的对象持有短生命周期对象的引用就...
    99+
    2023-06-30
  • java内存泄漏如何解决
    今天小编给大家分享一下java内存泄漏如何解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1、单例引起的内存泄漏。,由于单...
    99+
    2023-06-30
  • 教你5分钟轻松搞定内存字节对齐
    写出一个struct,然后sizeof,你会不会经常对结果感到奇怪sizeof的结果往往都比你声明的变量总长度要大,这是怎么回事呢讲讲字节对齐吧. 上面是你随便google一下,人...
    99+
    2022-11-15
    字节对齐
  • 如何轻松搞定Pdf转Word用Python
    这期内容当中小编将会给大家带来有关如何轻松搞定Pdf转Word用Python,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。大家在日常的工作学习过程中,都会遇到一个问题就是将pdf中的文本内容转化为word...
    99+
    2023-06-16
  • Java基础详解之内存泄漏
    目录一、什么是内存泄漏二、Java内存泄漏引起的原因三、内存泄漏的危害一、什么是内存泄漏 内存泄漏是指你向系统申请分配内存进行使用(new/malloc),然后系统在堆内存中给这个对...
    99+
    2024-04-02
  • Java中怎么引入内存泄漏
    Java中怎么引入内存泄漏,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1. 什么是内存泄漏内存泄漏的定义:应用程序不再使用对象,但是垃圾收集器不能删除它们,因为它们正在被引用...
    99+
    2023-06-16
  • Java内存泄漏的排查分析
    本篇内容介绍了“Java内存泄漏的排查分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、由来前些日子小组内安排值班,轮流看顾我们的服务,...
    99+
    2023-06-02
  • JAVA内存泄漏的示例分析
    本篇文章给大家分享的是有关JAVA内存泄漏的示例分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可...
    99+
    2023-06-03
  • java中内存泄漏和内存溢出是什么
    内存泄漏(Memory Leak)是指在程序运行过程中,因为不正确的内存管理而导致一部分内存无法被回收的现象。当程序中的对象不再被使...
    99+
    2023-08-25
    java
  • 如何解决JAVA内存泄漏问题
    本篇内容介绍了“如何解决JAVA内存泄漏问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目录什么是内存泄漏内存泄漏的原因内存泄漏有哪些情况...
    99+
    2023-06-20
  • Java内存泄漏实例排查分析
    这篇文章主要介绍“Java内存泄漏实例排查分析”,在日常操作中,相信很多人在Java内存泄漏实例排查分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java内存泄漏实例排查分析”的疑惑有所帮助!接下来,请跟...
    99+
    2023-06-16
  • java如何看有没有内存泄漏
    Java中可以通过工具来检测内存泄漏,以下是几种常用的方法:1. 手动分析:通过分析代码中的资源使用情况,判断是否存在内存泄漏的可能...
    99+
    2023-08-31
    java
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作