当前位置: 首页 > news >正文

JVM - 内存泄露与内存溢出


内存溢出


● 内存溢出相对于内存泄漏来说,尽管更容易被理解,但是同样的,内存溢出也是引发程序崩溃的罪魁祸首之一。

● 由于GC一直在发展,所有一般情况下,除非应用程序占用的内存增长速度非常快,造成垃圾回收已经跟不上内存消耗的速度,否则不太容易出现OOM的情况。

● 大多数情况下,GC会进行各种年龄段的垃圾回收,实在不行了就放大招,来一次独占式的Full GC操作,这时候会回收大量的内存,供应用程序继续使用。

● javadoc中对 OutOfMemoryError 的解释是,没有空闲内存,并且垃圾收集器也无法提供更多内存。


内存不足够的原因

首先说没有空闲内存的情况:说明Java虚拟机的堆内存不够。原因有二:

(1)Java虚拟机的堆内存设置不够。
比如:可能存在内存泄漏问题;也很有可能就是堆的大小不合理,比如我们要处理比较可观的数据量,但是没有显式指定JVM堆大小或者指定数值偏小。我们可以通过参数-Xms、-Xmx来调整。

(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)
对于老版本的Oracle JDK,因为永久代的大小是有限的,并且JVM对永久代垃圾回收(如,常量池回收、卸载不再需要的类型)非常不积极,所以当我们不断添加新类型的时候,永久代出现 OutOfMemoryError 也非常多见,尤其是在运行时存在大量动态类型生成的场合;类似intern字符串缓存占用太多空间,也会导致OOM问题。对应的异常信息, 会标记出来和永久代相关:“java.lang.OutOfMemoryError: PermGen space”。

随着元数据区的引入,方法区内存已经不再那么窘迫,所以相应的OOM有所改观,出现OOM,异常信息则变成了:“java.lang.OutOfMemoryError: Metaspace”。直接内存不足,也会导致OOM。

OOM前必有GC?

● 这里面隐含着一层意思是,在抛出OutOfMemoryError之前,通常垃圾收集器会被触发,尽其所能去清理出空间。
○ 例如:在引用机制分析中,涉及到JVM会去尝试回收软引用指向的对象等。
○ 在java.nio.BIts.reserveMemory()方法中,我们能清楚的看到,System.gc()会被调用,以清理空间。

● 当然,也不是在任何情况下垃圾收集器都会被触发的
○ 比如,我们去分配一个超大对象,类似一个超大数组超过堆的最大值,JVM 可以判断出垃圾收集并不能解决这个问题,所以直接抛出OutOfMemoryError


内存泄漏


也称作“存储渗漏”。严格来说,只有对象不会再被程序用到了,但是GC又不能回收他们的情况,才叫内存泄漏。

但实际情况很多时候一些不太好的实践(或疏忽)会导致对象的生命周期变得很长甚至导致OOM,也可以叫做宽泛意义上的“内存泄漏”。

尽管内存泄漏并不会立刻引起程序崩溃,但是一旦发生内存泄漏,程序中的可用内存就会被逐步蚕食,直至耗尽所有内存,最终出现OutOfMemory异常,导致程序崩溃。

注意,这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。


Java 中内存泄露的 8 种情况

  1. 静态集合类
    静态集合类,如HashMap、LinkedList等等。如果这些容器为静态的,那么它们的生命周期与JVM程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。简单而言,长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。
public class MemoryLeak {static List list = new ArrayList();public void oomTests() {Object obj = new Object();//局部变量list.add(obj);}
}
  1. 单例模式
    单例模式,和静态集合导致内存泄露的原因类似,因为单例的静态特性,它的生命周期和 JVM 的生命周期一样长,所以如果单例对象如果持有外部对象的引用,那么这个外部对象也不会被回收,那么就会造成内存泄漏。

  2. 内部类持有外部类

  3. 各种连接未关闭

  4. 变量不合理的作用域
    变量不合理的作用域。一般而言,一个变量的定义的作用范围大于其使用范围,很有可能会造成内存泄漏。另一方面,如果没有及时地把对象设置为null,很有可能导致内存泄漏的发生。

public class UsingRandom {private String msg;public void receiveMsg(){//private String msg;readFromNet();// 从网络中接受数据保存到msg中saveDB();// 把msg保存到数据库中//msg = null;}
}

如上面这个伪代码,通过readFromNet方法把接受的消息保存在变量msg中,然后调用saveDB方法把msg的内容保存到数据库中,此时msg已经就没用了,由于msg的生命周期与对象的生命周期相同,此时msg还不能回收,因此造成了内存泄漏。

实际上这个msg变量可以放在receiveMsg方法内部,当方法使用完,那么msg的生命周期也就结束,此时就可以回收了。还有一种方法,在使用完msg后,把msg设置为null,这样垃圾回收器也会回收msg的内存空间。

  1. 改变哈希值
    改变哈希值,当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了。
    否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,造成内存泄漏。

这也是 String 为什么被设置成了不可变类型,我们可以放心地把 String 存入 HashSet,或者把 String 当做 HashMap 的 key 值;

  1. 缓存泄露
    内存泄漏的另一个常见来源是缓存,一旦你把对象引用放入到缓存中,他就很容易遗忘。比如:之前项目在一次上线的时候,应用启动奇慢直到夯死,就是因为代码中会加载一个表中的数据到缓存(内存)中,测试环境只有几百条数据,但是生产环境有几百万的数据。

对于这个问题,可以使用WeakHashMap代表缓存,此种Map的特点是,当除了自身有对key的引用外,此key没有其他引用那么此map会自动丢弃此值。

  1. 监听器和回调
    内存泄漏另一个常见来源是监听器和其他回调,如果客户端在你实现的API中注册回调,却没有显式的取消,那么就会积聚。

需要确保回调立即被当作垃圾回收的最佳方法是只保存它的弱引用,例如将他们保存成为WeakHashMap中的键。

http://www.dtcms.com/a/482080.html

相关文章:

  • iOS 26 性能测试实战,性能评估、帧率、资源瓶颈 + 多工具辅助测试
  • elasticsearch数据迁移
  • 可以横跨时间轴,分类显示的事件
  • 2.0 轴承的分类与套筒、甩油环作用
  • mvc 网站 只列出目录wordpress速度慢2018
  • 电子商务网站建设一体化教案代运营公司网站
  • 深度学习与大模型技术实战:从算法原理到应用部署
  • YOLO v3:目标检测领域的经典革新与实战指南
  • MATLAB基于GWO(灰狼优化算法)优化LSTM神经网络的分类模型实现。主要功能是通过智能算法自动寻找LSTM的最佳超参数,构建分类模型并对数据进行分类预测
  • 网站的制医院网站建设台账
  • 用python操作mysql之pymysql库基本操作
  • 数据结构 05 栈和队列
  • 01、大模型部署方案与Dify的使用
  • 使用Spring Boot构建消息通信层
  • 山东济南seo整站优化公司对其网站建设进行了考察调研
  • MIPI_CSI22_Xilinx IP
  • 【C++STL :stack queue (一) 】STL:stack与queue全解析|深入使用(附高频算法题详解)
  • DevOps工具链对比,云效 vs TikLab哪一款更好用?
  • Kanass,一款超级轻量且简洁的项目管理工具
  • 如何做企业的网站微信如何开通小程序
  • 【从0开始学习Java | 第20篇】网络编程
  • PetaLinux 工程迁移指南
  • Java面试实战:互联网医疗场景中的JVM调优与Spring Boot应用
  • http环境实现通知
  • 分布式雷达 vs 多基地雷达:同频共振的“合唱团”和“乐队”
  • 手机端-adb脚本自动化-真机版
  • Python爬虫常见陷阱:Ajax动态生成内容的URL去重与数据拼接
  • 简繁英3合1企业网站生成管理系统V1.6wordpress如何降级
  • 【学以致用|python自动化办公】OCR批量识别自动存为Excel(批量识别发票)
  • AJAX 实时搜索