Java 内存管理原理、内存泄漏实例及解决方案研究

Java领域内存管理:避免内存泄漏的方法 Java作为一种广泛应用的编程语言,其内存管理机制对于程序的性能和稳定性至关重要。内存泄漏是Java开发中常见且棘手的问题,它会导致程序占用的内存不断增加,最终可能引发系统崩溃。本文的目的是深入探讨Java领域的内存管理,详细介绍避免内存泄漏的方法,帮助Java开发者更好地理解和掌握内存管理的技巧,提高程序的性能和稳定性。本文的范围涵盖了Java内存管理的基本概念、核心算法原理、实际操作步骤、项目实战案例、实际应用场景等方面,同时还提供了相关的学习资源、开发工具和论文著作推荐。 阅读详情

 

      为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配 和释放问题。在Java中,程序员需要通过关键字new为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。另外,对象的释放是由GC决定和执行的。在Java中,内存的分配是由程序完成的,而内存的释放是有GC完成的,这种收支两条线 的方法确实简化了程序员的工作。但同时,它也加重了JVM的工作。这也是Java程序运行速度较慢的原因之一。因为,GC为了能够正确释放对象,GC必须 监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。

监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。

      为了更好理解GC的工作原理,我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向 被引对象。另外,每个线程对象可以作为一个图的起始顶点,例如大多程序从main进程开始执行,那么该图就是以main进程顶点开始的一棵根树。在这个有 向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。

以下,我们举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况。以下右图,就是左边程序运行到第6行的示意图。


  
      Java 使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的。这种方式的 优点是管理内存的精度很高,但是效率较低。另外一种常用的内存管理技术是使用计数器,例如COM模型采用计数器方式管理构件,它与有向图相比,精度行低 (很难处理循环引用的问题),但执行效率很高。

 

下面,我们就可以描述什么是内存泄漏。在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点, 首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对 象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

在C++中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java中,这些不可达的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。

通过分析,我们得知,对于C++,程序员需要自己管理边和顶点,而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式,Java提高了编程的效率。


 

      因此,通过以上分析,我们知道在Java中也有内存泄漏,但范围比C++要小一些。因为Java从语言上保证,任何对象都是可达的,所有的不可达对象都由GC管理。

对于程序员来说,GC基本是透明的,不可见的。虽然,我们只有几个函数可以访问GC,例如运行GC的函数 System.gc(),但是根据Java语言规范定义, 该函数不保证JVM的垃圾收集器一定会执行。因为,不同的JVM实现者可能使用不同的算法管理GC。通常,GC的线程的优先级别较低。JVM调用GC的策 略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心 这些。除非在一些特定的场合,GC的执行影响应用程序的性能,例如对于基于Web的实时系统,如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃 圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpot JVM就支持这一特性。

      下面给出了一个简单的内存泄露的例子。在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个 Vector中,如果我们仅仅释放引用本身,那么Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector 后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。 


Java内存泄漏的类型、实例及解决

1.对象游离
  一种形式的内存泄漏有时候叫做对象游离 (object loitering),是通过清单 1 中的 LeakyChecksum 类来说明的,清单 1 中有一个 getFileChecksum() 方法用于计算文件内容的校验和。getFileChecksum() 方法将文件内容读取到缓冲区中以计算校验和。一种更加直观的实现简单地将缓冲区作为 getFileChecksum() 中的本地变量分配,但是该版本比那样的版本更加 “聪明”,不是将缓冲区缓存在实例字段中以减少内存 churn。该 “优化”通常不带来预期的好处;对象分配比很多人期望的更便宜。(还要注意,将缓冲区从本地变量提升到实例变量,使得类若不带有附加的同步,就不再是线程 安全的了。直观的实现不需要将 getFileChecksum() 声明为 synchronized,并且会在同时调用时提供更好的可伸缩性。) 

清单 1. 展示 “对象游离” 的类


   这个类存在很多的问题,但是我们着重来看内存泄漏。缓存缓冲区的决定很可能是根据这样的假设得出的,即该类将在一个程序中被调用许多次,因此它应该更加 有效,以重用缓冲区而不是重新分配它。但是结果是,缓冲区永远不会被释放,因为它对程序来说总是可及的(除非 LeakyChecksum 对象被垃圾收集了)。更坏的是,它可以增长,却不可以缩小,所以 LeakyChecksum 将永久保持一个与所处理的最大文件一样大小的缓冲区。退一万步说,这也会给垃圾收集器带来压力,并且要求更频繁的收集;为计算未来的校验和而保持一个大型 缓冲区并不是可用内存的最有效利用。

      LeakyChecksum 中问题的原因是,缓冲区对于 getFileChecksum() 操作来说逻辑上是本地的,但是它的生命周期已经被人为延长了,因为将它提升到了实例字段。因此,该类必须自己管理缓冲区的生命周期,而不是让 JVM 来管理。 

2.软引用 

      弱引用如何可以给应用程序提供当对象被程序使用时另一种到达该对象的方法,但是不会延长对象的生命周期。Reference 的另一个子类 —— 软引用 —— 可满足一个不同却相关的目的。其中弱引用允许应用程序创建不妨碍垃圾收集的引用,软引用允许应用程序通过将一些对象指定为 “expendable” 而利用垃圾收集器的帮助。尽管垃圾收集器在找出哪些内存在有应用程序使用哪些没在使用方面做得很好,但是确定可用内存的最适当使用还是取决于应用程序。如 果应用程序做出了不好的决定,使得对象被保持,那么性能会受到影响,因为垃圾收集器必须更加辛勤地工作,以防止应用程序消耗掉所有内存。

      高速缓存是一种常见的性能优化,允许应用程序重用以前的计算结果,而不是重新进行计算。高速缓存是 CPU 利用和内存使用之间的一种折衷,这种折衷理想的平衡状态取决于有多少内存可用。若高速缓存太少,则所要求的性能优势无法达到;若太多,则性能会受到影响, 因为太多的内存被用于高速缓存上,导致其他用途没有足够的可用内存。因为垃圾收集器比应用程序更适合决定内存需求,所以应该利用垃圾收集器在做这些决定方 面的帮助,这就是件引用所要做的。 

      
如果一个对象惟一剩下的引用是弱引用或软引用,那么该对象是软可及的(softly reachable)。垃圾收集器并不像其收集弱可及的对象一样尽量地收集软可及的对象,相反,它只在真正 “需要” 内存时才收集软可及的对象。软引用对于垃圾收集器来说是这样一种方式,即 “只要内存不太紧张,我就会保留该对象。但是如果内存变得真正紧张了,我就会去收集并处理这个对象。” 垃圾收集器在可以抛出 OutOfMemoryError 之前需要清除所有的软引用。 

      
通过使用一个软引用来管理高速缓存的缓冲区,可以解决 LeakyChecksum 中的问题,如清单 2 所示。现在,只要不是特别需要内存,缓冲区就会被保留,但是在需要时,也可被垃圾收集器回收:

清单 2. 用软引用修复 LeakyChecksum

 

3、基于数组的集合

  当数组用于实现诸如堆栈或环形缓冲区之 类的数据结构时,会出现另一种形式的对象游离。清单 3 中的 LeakyStack 类展示了用数组实现的堆栈的实现。在 pop() 方法中,在顶部指针递减之后,elements 仍然会保留对将弹出堆栈的对象的引用。这意味着,该对象的引用对程序来说仍然可及(即使程序实际上不会再使用该引用),这会阻止该对象被垃圾收集,直到该 位置被未来的 push() 重用。 

清单 3. 基于数组的集合中的对象游离
 

修复这种情况下的对象游离的方法是,当对象从堆栈弹出之后,就消除它的引用,如清单 3 中注释掉的行所示。但是这种情况 —— 由类管理其自己的内存 —— 是一种非常少见的情况,即显式地消除不再需要的对象是一个好主意。大部分时候,认为不应该使用的强行消除引用根本不会带来性能或内存使用方面的收益,通常 是导致更差的性能或者 NullPointerException。该算法的一个链接实现不会存在这个问题。在链接实现中,链接节点(以及所存储的对象的引用)的生命期将被自动 与对象存储在集合中的期间绑定在一起。弱引用可用于解决这个问题 —— 维护弱引用而不是强引用的一个数组 —— 但是在实际中,LeakyStack 管理它自己的内存,因此负责确保对不再需要的对象的引用被清除。使用数组来实现堆栈或缓冲区是一种优化,可以减少分配,但是会给实现者带来更大的负担,需 要仔细地管理存储在数组中的引用的生命期。

 

VASP+机器学习快速收敛AIMD 本文介绍了如何利用VASP结合机器学习技术快速收敛AIMD(从头算分子动力学)模拟。首先,通过训练机器学习力场,生成关键文件如ML_ABN(包含结构数据集)、ML_FFN(存储回归结果)和ML_LOGFILE(记录训练日志)。接着,配置INCAR文件以设置计算参数,包括并行化、分子动力学和机器学习相关参数。KPOINTS文件用于定义K点网格。计算完成后,备份CONTCAR文件作为后续模拟的初始结构。最后,利用训练好的力场文件进行纯机器学习力场分子动力学模拟,并提交任务执行。 阅读详情

相关推荐

测试丢包_电信IPRAN承载LTE业务丢包故障处理案例

银川 景旭阳电信IPRAN承载LTE业务丢包故障处理案例一.故障现象某地市IPRAN网络承载LTE基站业务,客户反馈近期丢包严重,基站侧ping侧核心网EPC丢包达到10%左右;且基站数量在10个左右,并分布在同一地区,需要IPRAN网络协助排查;随后查看了IPRAN网络;发现这些业务所在基站的PW-BFD历史告警存在频繁的UP/DOWN告警;综上信息展开故障处理,初步判断由于BFD频繁...

weixin_29740921的博客 1912

ava中,是否有内存泄漏问题?

理论上Java由垃圾回收机制GC,不会存在内存泄漏问题; 但在实际项目开发的时候,就会经常遇到内存泄漏的问题。可能会存在没有用处但是还存在在内存空间的对象,这些对象不能被GC回收,所以这些对象的堆积也会使得内存造成泄漏。比如hibernate的session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些兑现的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄漏。 在Java语言中,判断一个内存空间是否符合垃圾回收的标准有两个:

小汤圆 242

手把手教你分析解决Innovus DRC Violation 和Antenna Effect Violation

数字IC后端设计培训教程之手把手教你分析解决Innovus DRC Violation 和Antenna Effect Violation

weixin_37584728的博客 2940

Java内存泄漏

Java内存泄漏引起的原因: 内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。 长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏,尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收,这就是Java中内存泄漏的发生场景。 造成内存泄漏的几种情况: 静态集合类引起内存泄漏   像HashM...

Lzc的博客 199

RT-Thread Studio与CubeMX联合编程(超级详细)

RT-Thread Studio与CubeMX联合编程 本文章参考以下文章: https://blog.youkuaiyun.com/qq_40824852/article/details/122596043 https://zhuanlan.zhihu.com/p/395106066 https://blog.youkuaiyun.com/m0_37697335/article/details/108890584 一、简介 经过上次对RT-Thread Studio的初步尝试,发现RT-Thread Studio中存在官方支

zhuzhu的博客 3万+

电力电子技术复习笔记

通常在开关过程前后加入谐振,使开关开通前电压先降到0,关断前电流先降到0,消除开关过程电压、电流的重叠,降低它们的变化率,从而大大减小甚至消除开关损耗。小信号线性化模型的原理:忽略开关频率分量和开关频率谐波分量及其边频分量,建立占空比、输入电压等的低频扰动对变换器中的电压、电流影响的模型。解:可以利用变压器的变比改变输出电压大小,避免控制过程的占空比过小或过大,提高控制精度,同时有利于减轻输出电路中电力电子器件的耐压要求。驱动电路:用于控制功率半导体器件的开关,从而控制电路的电流和电压。

Miracle Fan的博客 1万+

Java 内存管理原理、内存泄漏实例及解决方案研究收藏

新一篇: 调整 Java I/O 性能 | 旧一篇: 也谈基础! 在项目的最后阶段,就是要防止系统的内存泄漏了,顺便找了些资料,看了些java内存泄漏的实例及解决,总结一下: Java是如何管理内存 为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的。J...

cichaojiao4477的博客 148

若依Vue整合积木报表实战:Token安全校验全流程解析

本文详细解析了在若依Vue后台系统中安全集成积木报表的全流程,重点阐述了Token安全校验的核心机制。通过前端菜单配置、Token传递、后端权限拦截及自定义Token服务实现,构建了一套完整的企业级报表访问安全防线,确保核心业务数据的安全可控。

t8u9v0的博客 578

Java 内存管理原理、内存泄漏实例及解决方案研究【转】

为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配 和释放问题。在Java中,程序员需要通过关键字new为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。另外,对象的释放是由GC决定和执行的。在Java中,内存的分配是由程序完成的,而内存的释放是有GC完成的,这种收支两条线 的方法确实简化了程序员的工作。但...

pacozhong的专栏 211

ENVI学习总结(十二)——基于改进的 CASA 模型反演 NPP

基于改进的 CASA 模型反演 NPP 数据为地理空间数据云提供的 TM 影像,其具体信息如图 1 所示。该实验中所使用的 TM 数据已进行过 QUAC 快速大气校正,来消除大气和光照等因素对地物反射的影响。Landsat 主题成像仪(TM)是 Landsat4 和 Landsat5 携带的传感器,每 16 天扫描同一地区,即 16 天覆盖全球一次。 TM 影像包含 7 个波段,波段 1-5 ...

Arcgiser的博客 3万+

Google Earth Engine(GEE)——使用归一化燃烧指数 (NBR) 绘制地表烧伤严重程度图

归一化燃烧指数(Normalized Burn Ratio, NBR)是一种遥感指标,用于评估野火或烧伤地面后的植被恢复情况。NBR能增强较大范围的火灾区域,如大于200公顷。它是通过比较远红外波段 (SWIR) 和近红外波段 (NIR) 的反射率来计算的。SWIR波段测量烧伤地面的反射率,NIR波段测量没有受到烧伤影响的植被的反射率。NBR的计算公式为:NBR =(NIR-SWIR)/(NIR + SWIR)。其中,NIR为近红外波段,SWIR为短波红外波段。

此星光明博客 3465

Java中关于内存泄漏分析和解决方案,都在这里了

就写到这了,也算是给这段时间的面试做一个总结,查漏补缺,祝自己好运吧,也希望正在求职或者打算跳槽的 程序员看到这个文章能有一点点帮助或收获,我就心满意足了。多思考,多问为什么。希望小伙伴们早点收到满意的offer!越努力越幸运!金九银十已经过了,就目前国内的面试模式来讲,在面试前积极的准备面试,复习整个 Java 知识体系将变得非常重要,可以很负责任的说一句,复习准备的是否充分,将直接影响你入职的成功率。

2401_85358162的博客 1969

Qt —— 调用海康C++版SDK获取摄像头每帧图像,通过OpenCv处理每帧图像后显示在Qt窗口(附:源码)

  关注 笔者 - jxd 微信公众号搜索 “码农总动员” 或 微信扫描下方二维码,了解更多你不知道的XX,O(∩_∩)O

5447

Java内存泄漏深度分析与解决方案全景指南

内存泄漏是Java开发中的"慢性杀手",它不会立即导致系统崩溃,却会逐渐侵蚀应用性能,最终引发灾难性的OutOfMemoryError。本文将系统性地剖析Java内存泄漏的根源、检测方法和解决方案,并通过图表直观展示关键概念和流程。

一起探索IT的世界 1647

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Java的内存泄漏

Java的一个重要优点就是通过垃圾收集器(Garbage Collection,GC)自动管理内存的回收,程序员不需要通过调用函数来释放内存。因此,很多程序员认为Java不存在内存泄漏问题,或者认为即使有内存泄漏也不是程序的责任,而是GC或JVM的问题。其实,这种想法是不正确的,因为Java也存在内存泄露,但它的表现与C++不同。问题的提出 Java的一个重要优点就是通过垃圾收集器(G

elimago的专栏 8054

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Java 内存管理原理、内存泄漏实例及解决方案

 在项目的最后阶段,就是要防止系统的内存泄漏了,顺便找了些资料,看了些java内存泄漏的实例及解决,总结一下: Java是如何管理内存 为了判断Java中是否有内存泄露,我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中,程序员需要通过关键字new为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。另外,对象的释放是...

haishizi1的专栏 149

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

上一篇: 心态放低,从小事做起
下一篇: JSF 基础知识
javaActor
博客等级 码龄17年 22粉丝 42原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值