java内存泄露的理解与解决

java内存管理机制


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

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

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

通过这种有向图的内存管理方式,当一个内存对象失去了所有的引用之后,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
变量,与类实例生命周期相同的要声明为实例变量……以此类推。第二,在内存对象不再需要时,记得手动将其引用置空。

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


实际的项目中,我们经常用到一些较为复杂的数据结构用于缓存程序运行过程中需要的数据信息。有时,由于数据结构过于复杂,或者我们存在一些特殊的需求(例

如,在内存允许的情况下,尽可能多的缓存信息来提高程序的运行速度等情况),我们很难对数据结构中数据的生命周期作出明确的界定。这个时候,我们可以使用
java 中一种特殊的机制来达到防止内存泄露的目的。

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

来源:51CTO

时间: 2024-08-30 02:24:46

java内存泄露的理解与解决的相关文章

android开发中的java内存泄露分析

做了较长时间的android开发了,发现其实android应用开发入门容易,但是进阶或者成为高级工程师,需要具备的基础能力还是非常高的:性能优化.内存泄露.apk瘦身.热修复等等,这些都非常的考验一个人的能力.android成长之路还很长,自己会持续的走下去.本文主要介绍android内存泄露方面的知识.其实要真的理解内存泄露,需要对JVM.java语言有一定的了解,在这个基础上就比较容易理解本文了. 一.内存泄露概念 在java中,如果一个对象没有可用价值了,但又被其他引用所指向,那么这个对象

使用OptimizeIT进行Java内存泄露的检测

java程序也会引起内存泄露已经是公开的秘密了,工作中需要对该问题引起充分的重视. 市场上能够进行java内存检测的工具也有不少,我在工作中使用JBuilder自带的Optimize工具,现就最近一次使用Optimize的工程及经验进行以下梳理. 前一段时间根据需要,对工作中维护的某个模块进行了java内存泄露的测试,结果发现了很严重的问题:该模块一共有6大基本功能,每个功能每操作一次,大约要新消耗9-10k的内存空间,生成110个左右的新对象.作为商用软件产品是绝不允许这样的问题的出现的. O

Java内存泄露问题分析

很多人在谈论内存泄露问题,当然对于c/c++来说,这个应该是老掉牙的问题,但是很多Java人员也越来越多得讨论这个问题,我这里写个小结,希望对大家有一定的参考价值. 内存泄漏的慨念 1.c/c++是程序员自己管理内存,Java内存是由GC自动回收的. 我虽然不是很熟悉C++,不过这个应该没有犯常识性错误吧. 2.什么是内存泄露? 内存泄露是指系统中存在无法回收的内存,有时候会造成内存不足或系统崩溃. 在C/C++中分配了内存不释放的情况就是内存泄露. 3.Java存在内存泄露 我们必须先承认这个

聊聊我对Java内存模型的理解

所有的编程语言中都有内存模型这个概念,区别于微架构的内存模型,高级语言的内存模型包括了编译器和微架构两部分.我试图了解了Java.C#和Go语言的内存模型,发现内容基本大同小异,只是这些语言在具体实现的时候略有不同. 我们来看看Java内存模型吧,提到Java内存模型大家对这个图一定非常熟悉: 这张图告诉我们在线程运行的时候有一个内存专用的一小块内存,当Java程序会将变量同步到线程所在的内存,这时候会操作工作内存中的变量,而线程中变量的值何时同步回主内存是不可预期的.但同时Java内存模型又告

如何排查Java内存泄露(内附各种排查工具介绍)

今天刚刚才加一个故障review会议, 故障非常典型, google下也可以找到相似案例介绍. 在排查问题的过程中,使用了大量的工具, 发现有问题的地方还不只一个,总结一下. (本篇文章不会重点描述案例本身,重点会介绍个人对java内存泄露问题的排查思路和各种工具的使用). java内存泄露典型特征 现象一: 堆/Perm 区不断增长, 没有下降趋势(回收速度赶不上增长速度), 最后不断触发FullGC, 甚至crash(如下**两张图是同一个应用的GC和Perm数据, GC触发原因确认是Per

Android 和 Java 内存泄露检测工具——LeakCanary

LeakCanary Android 和 Java 内存泄露检测. "A small leak will sink a great ship." - Benjamin Franklin 千里之堤, 毁于蚁穴. -- <韩非子·喻老> demo 一个非常简单的 LeakCanary demo: https://github.com/liaohuqiu/leakcanary-demo 开始使用 在 build.gradle 中加入引用,不同的编译使用不同的引用: depende

关于java内存泄露求大神帮忙分析

问题描述 关于java内存泄露求大神帮忙分析 用MAT进行分析但是有点看不懂求大神帮忙分析一下 解决方案 Java内存泄露和分析Java内存泄露分析Java内存泄露问题分析 解决方案二: Details 看看 解决方案三: 你看看你有没有写入到内存里很大的文件 解决方案四: 这是今天重新定位问题的明细图片 解决方案五: 这是相关数据的图片

java内存泄露是错误还是异常

问题描述 今天去掏宝面试,被问到"java的内存泄露是错误还是异常?"我答:是错误我不知道答得对不对?请大家说说!! 解决方案 解决方案二:个人愚见:如果内存泄露的消息被java反馈回来,并有警示消息那么应该是异常,应该说所谓"异常"就是被程序捕获的错误,超出程序预期的目的或者计划.如果内存泄露后,未能捕获进而引发了灾难性的后果,例如主程序崩溃或者蓝屏死机,那么就是应该是错误.能否捕获并处理是区分是否是错误或者异常的关键,这个题目感觉出的很模糊,java的内存泄露到

详细介绍Java内存泄露原因_java

一.Java内存回收机制 不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址.Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的.GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请.引用.被引用.赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题.在J