java内存泄露的理解与解决

简介:

java内存管理机制


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

java内存泄露的理解与解决

上面是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

相关文章
|
11天前
|
缓存 安全 Java
Java并发编程进阶:深入理解Java内存模型
Java并发编程进阶:深入理解Java内存模型
25 0
|
10天前
|
存储 算法 Java
深入浅出Java内存管理
【8月更文挑战第28天】Java的内存管理是每个Java开发者都绕不过去的技术话题。本文将通过生动的比喻和直观的例子,带你走进Java内存管理的奇妙世界。我们将一起探索对象在Java虚拟机中的生命周期,了解栈与堆的区别,以及垃圾回收机制如何默默守护着我们的应用程序。准备好,我们即将启程!
37 14
|
2天前
|
算法 安全 Java
Java内存管理:深入理解垃圾收集器
在Java的世界里,内存管理是一块基石,它支撑着应用程序的稳定运行。本文将带你走进Java的垃圾收集器(GC),探索它是如何默默守护着我们的内存安全。我们将从垃圾收集的基本概念出发,逐步深入到不同垃圾收集器的工作机制,并通过实例分析它们在实际应用中的表现。文章不仅旨在提升你对Java内存管理的认识,更希望你能通过这些知识优化你的代码,让程序运行更加高效。
20 3
|
10天前
|
监控 算法 Java
Java内存管理:垃圾收集器的工作原理与调优实践
在Java的世界里,内存管理是一块神秘的领域。它像是一位默默无闻的守护者,确保程序顺畅运行而不被无用对象所困扰。本文将带你一探究竟,了解垃圾收集器如何在后台无声地工作,以及如何通过调优来提升系统性能。让我们一起走进Java内存管理的迷宫,寻找提高应用性能的秘诀。
|
8天前
|
Kubernetes Cloud Native Java
云原生之旅:从容器到微服务的演进之路Java 内存管理:垃圾收集器与性能调优
【8月更文挑战第30天】在数字化时代的浪潮中,企业如何乘风破浪?云原生技术提供了一个强有力的桨。本文将带你从容器技术的基石出发,探索微服务架构的奥秘,最终实现在云端自由翱翔的梦想。我们将一起见证代码如何转化为业务的翅膀,让你的应用在云海中高飞。
|
1天前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
21 11
|
9天前
|
缓存 Java
Java内存管理秘籍:掌握强软弱幻四大引用,让代码效率翻倍!
【8月更文挑战第29天】在Java中,引用是连接对象与内存的桥梁,主要分为强引用、软引用、弱引用和幻象引用。强引用确保对象生命周期由引用控制,适用于普通对象;软引用在内存不足时可被回收,适合用于内存敏感的缓存;弱引用在无强引用时即可被回收,适用于弱关联如监听器列表;幻象引用需与引用队列配合使用,用于跟踪对象回收状态,适用于执行清理工作。合理使用不同类型的引用车可以提升程序性能和资源管理效率。
31 4
|
10天前
|
Java 编译器 开发者
深入浅出Java内存模型
【8月更文挑战第28天】Java内存模型(JMM)是理解Java并发编程不可或缺的一环。本文通过浅显易懂的方式,带你一探JMM的奥秘,从基本概念到工作原理,再到实际代码示例,逐步揭开Java内存模型的神秘面纱。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和深入的理解。
|
11天前
|
存储 缓存 Java
Java内存模型(JMM)
Java内存模型(JMM)是一个抽象概念,用于规范程序中各种变量(实例字段、静态字段及数组元素)的访问方式,确保不同Java虚拟机(JVM)上的并发程序结果一致可靠。JMM定义了主存储器(所有线程共享)与工作存储器(线程私有)的概念,线程间通过主存储器进行通信。JMM具备三大特性:原子性(确保基本读写操作的不可分割)、可见性(确保一个线程对共享变量的修改对其他线程可见)、有序性(防止指令被处理器或编译器重排序影响程序逻辑)。通过这些特性,JMM解决了多线程环境下的数据一致性问题。
|
13天前
|
Java 容器
【Azure Function App】Java Function在运行中遇见内存不足的错误
【Azure Function App】Java Function在运行中遇见内存不足的错误
下一篇
DDNS