内存泄露与内存溢出(内容全面、案例清晰,建议收藏)

内存泄露

概念

对象已经不会被程序使用了,但直到程序结束都无法被垃圾回收,通常情况下都是对象的生命周期设置的不合理,导致对象的强引用一直存在,内存泄露会导致程序中的内存(这里的内存指的是分配给jvm的内存)逐步被消耗殆尽,最终可能引发OOM

内存溢出OOM

官方概念

OOM,全称“Out Of Memory”,含义是当JVM因为没有足够的内存来为对象分配空间,并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(java.lang.OutOfMemoryError),需要注意这个报错是error类型的而非exception,意味着这个问题已经严重到不足以被应用处理了。

补充:JVM中除了程序计数器外其它存储位置都可能产生OOM问题

原因分析

  • 分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的VM参数指定)太少。
  • 内存泄露导致oom,由于java的JVM引入了垃圾回收机制,垃圾回收器会自动回收不再使用的对象,了解JVM回收机制的都知道JVM是使用引用计数法和可达性分析算法来判断对象是否是不再使用的对象,本质都是判断一个对象是否还被引用,开发者申请的内存使用完毕但没有及时释放,会让JVM误以为此对象还在引用中,jvm无法回收和重新分配,造成内存泄漏,内存泄露频繁出现就可能导致OOM,jvm本身是无法帮我们精准识别对象是否使用完毕,这种情况下还是要依赖开发者去手动释放的

内存泄露和内存溢出的常见情况

  • 流类对象使用完毕后没有主动释放流类的资源,比如数据库连接流、io流,这是开发中最常见的情况,这些流本身会占用一定内存,造成内存泄露,而且会导致连接耗尽,如果使用的是连接池的话会导致连接池中的连接对象很快被消耗完,,没有使用连接池则会造成内存泄露最终引发OOM,应对方案是使用完毕这些流类对象时,必须保证在fianlly代码块手动释放这些对象,并且fianlly代码块中不要有其它额外的操作,防止fianlly代码块中出现异常导致资源没有成功被释放掉,这类问题在生产环境中一定要避免的,引发问题非常严重,波及范围往往非常大

  • 静态集合类/数组容易导致内存泄露,集合类内部可能存储了大量对象的引用,而且由于集合是静态的,生命周期和类一致不会被轻易回收,存储在集合中的对象通常是局部变量,如果没有被存入静态集合的话这些局部变量本来应该在方法运行结束后出栈的,但存入静态集合后会被静态集合所引用,生命周期就会伴随整个程序运行期间,无法被垃圾回收,所以靠我们自己,集合业务场景,在使用完毕后及时清理,否则可会内存泄露甚至OOM

  • 使用Threadlocal,但没有remove

    • ThreadLocal作为静态变量使用,此时如果ThreadLocal使用完毕后没有主动remove,那么ThreadLocal作为key,生命周期是伴随整个程序运行期间的,但它所关联的value就无法被回收,造成内存泄露
    • ThreadLocal作为局部变量使用,如果没有使用线程池,那么即使你没有remove,也无需担心内存泄露,那么当方法执行完毕后,线程也会被回收,那么这个线程中的ThreadLocalMap也会被回收,同时ThreadLocal的引用在方法结束后也会出栈,ThreadLocal在后面的GC过程中也会被回收,但如果使用了线程池,时那么这意味着线程的生命周期也会伴随整个程序运行期间,那么线程中的ThreadLocalMap不会被回收,在使用完毕ThreadLocal在不主动remove,虽然ThreadLocal在ThreadLocalMap中是以弱引用的形式被存储的,在方法结束后ThreadLocal的引用被出栈,下次GC的时候ThreadLocal会被回收,但因为value用的是强引用,所以会出现这种情况,那就是ThreadLocalMap会出现大量的(null,value)的entry,造成内存泄露
  • 静态属性持有外部对象或者内部类对象导致内存泄露,归根到底还是静态属性生命周期和类一致,这使得但他们所持有jvm无法帮我们判断是否已经使用完毕,无法自动帮我们回收,需要我们自己去注意防范

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
内存泄漏指的是程序中存在一些不再使用的对象,但是这些对象却没有被垃圾回收器及时回收,导致内存占用不断增加,最终导致程序崩溃。 在Java程序中,常见的内存泄露问题包括: 1. 长时间持有对象引用:当一个对象被创建后,如果一直持有该对象的引用,即使该对象已经不再使用,也不会被垃圾回收器回收,从而导致内存泄漏。例如,当在一个集合类中添加一个对象后,如果一直持有该集合类的引用,那么该对象就不会被回收。 2. 静态集合类持有对象引用:如果一个对象被添加到静态的集合类中,那么该对象就不会被回收,因为静态集合类的生命周期与程序的生命周期相同。 3. 不正确的使用线程池:如果在使用线程池时,没有正确地释放线程资源,那么就会导致内存泄漏。例如,在J2EE应用程序中,如果没有正确地关闭线程池,就会导致内存泄漏。 4. 对象的finalize()方法未被正确地实现:当一个对象被垃圾回收器回收时,会调用该对象的finalize()方法。如果该方法未被正确地实现,就会导致内存泄漏。 5. 循环引用:如果多个对象之间存在循环引用,那么这些对象就无法被回收。 总之,内存泄漏是一个非常严重的问题,会导致程序的性能和稳定性出现问题。因此,在编写Java程序时,需要注意对象引用的生命周期,及时释放不再使用的对象,以避免出现内存泄漏等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值