一篇文章带你搞定JAVA内存泄漏

 更新时间:2021年07月14日 11:18:45   作者:香菜聊游戏  
今天小编就为大家分享一篇关于Java内存泄漏问题处理方法经验总结,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

1、什么是内存泄漏

内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。随着垃圾回收器活动的增加以及内存占用的不断增加,程序性能会逐渐表现出来下降,极端情况下,会引发OutOfMemoryError导致程序崩溃。

2、内存泄漏的原因

JVM 虚拟机是使用引用计数法和可达性分析来判断对象是否可回收,本质是判断一个对象是否还被引用,如果没有引用则回收。在开发的过程中,由于代码的实现不同就会出现很多种内存泄漏问题,让gc 系统误以为此对象还在引用中,无法回收,造成内存泄漏。

3、内存泄漏有哪些情况

3.1 代码中没有及时释放,导致内存无法回收。

下面的代码,因为是双向链表,但是断开的不够彻底,prev节点依然引用这当前正在使用的节点,导致无法回收

public class ListNode {
    int val;
    ListNode next;
    ListNode prev;
    ListNode() {
    }
    ListNode(int val) {
        this.val = val;
    }
    public ListNode(int val, ListNode next, ListNode prev) {
        this.val = val;
        this.next = next;
        this.prev = prev;
    }

    public static void main(String[] args) {
        ListNode curr = new ListNode(1);
        ListNode prev = new ListNode(2);
        ListNode next = new ListNode(3);
        curr.prev = prev;
        curr.next = next;
        curr.prev = null;
    }
}
    public static void main(String[] args) {
        ListNode curr = new ListNode(1);
        ListNode prev = new ListNode(2);
        ListNode next = new ListNode(3);
        curr.prev = prev;
        curr.next = next;
        curr.prev = null;
    }
}

3.2 资源未关闭造成的内存泄漏

各种连接,如数据库连接、网络连接和IO连接等,文件读写等,可以使用 try-with-resources 读取完文件,自动资源释放

try (RandomAccessFile raf = new RandomAccessFile(filePath, "r");) {
        Image image = null;
while((image = parseImage(raf)) != null){
            imageList.add(image);
        }
        return imageList;
} catch(Exception e){
    log.error("parse file error, path: {},", path, e);
    return null;
}

3.3 全局缓存持有的对象不使用的时候没有及时移除,导致一直在内存中无法移除

3.4 静态集合类

如HashMap、LinkedList等等。如果这些容器为静态的,那么它们的生命周期与程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。生命周期长的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。

3.5 堆外内存无法回收

堆外内存不受gc的管理,可能因为第三方的bug出现内存泄漏

4、内存泄漏的解决办法

1.尽量减少使用静态变量,或者使用完及时 赋值为 null。

2.明确内存对象的有效作用域,尽量缩小对象的作用域,能用局部变量处理的不用成员变量,因为局部变量弹栈会自动回收;

3.减少长生命周期的对象持有短生命周期的引用;

4.使用StringBuilder和StringBuffer进行字符串连接,Sting和StringBuilder以及StringBuffer等都可以代表字符串,其中String字符串代表的是不可变的字符串,后两者表示可变的字符串。如果使用多个String对象进行字符串连接运算,在运行时可能产生大量临时字符串,这些字符串会保存在内存中从而导致程序性能下降。

5.对于不需要使用的对象手动设置null值,不管GC何时会开始清理,我们都应及时的将无用的对象标记为可被清理的对象;

6.各种连接(数据库连接,网络连接,IO连接)操作,务必显示调用close关闭。

5、内存问题排查

没有任何一个程序员想要出现这种问题,但是出现了问题也要解决,内存泄漏的主要表象就是内存不足,内存告警之后如何判断是否有内存泄漏。

第一步 首先确认逻辑问题

查看内存中对象的数量和大小,判断是否在合理的范围,如果在合理的范围内,增大内存配置,调整内存比例就可以了。

命令:

jmap -heap pid

图片

第二步:分析gc是否正常执行

命令:

jstat -gcutil <pid> 1000

图片

S0 — Heap上的 Survivor space 0 区已使用空间的百分比    
S1 — Heap上的 Survivor space 1 区已使用空间的百分比    
E — Heap上的 Eden space 区已使用空间的百分比    
O   — Heap上的 Old space 区已使用空间的百分比    
P   — Perm space 区已使用空间的百分比
YGC — 从应用程序启动到采样时发生 Young GC 的次数
YGCT– 从应用程序启动到采样时 Young GC 所用的时间(单位秒)    
FGC — 从应用程序启动到采样时发生 Full GC 的次数
FGCT– 从应用程序启动到采样时 Full GC 所用的时间(单位秒)    
GCT — 从应用程序启动到采样时用于垃圾回收的总时间(单位秒)
LGCC - 进行GC的原因(低版本jdk可能没有这一列)

从这里观察gc是否异常,也可以根据这个进行jvm内存分配调优,来提高性能降低gc对性能的损耗

第三步 确认下版本新增代码的改动,尽快从代码上找出问题。

第四步:开启各种命令行和 导出 dump 各种工具分析

-XX:+HeapDumpOnOutOfMemoryError
-XX:OnError
-XX:+ShowMessageBoxOnError

推荐使用jprofile 进行本地分析,可以不用记住那么多命令。

图片

总结:

现在的服务器内存虽然很大,但是且用且珍惜,不要等到出现问题了才知道后果,在开发中规范自己代码,用完的对象及时释放,减少垃圾对象。出现问题了也不要慌,仔细分析代码,一切都是有原因的。

本篇文章就到这里了,希望能给你带来帮助,也希望您能多多关注脚本之家的更多内容!

相关文章

  • java 读取zip文件的两种方式示例详解

    java 读取zip文件的两种方式示例详解

    ZIP(压缩文件)是一种常见的文件格式,在Java中可以使用java.util.zip包提供的API来读取和处理ZIP文件,本文将介绍如何使用Java读取ZIP文件,并提供代码示例,感兴趣的朋友跟随小编一起看看吧
    2024-07-07
  • MyBatis之关于动态SQL解读

    MyBatis之关于动态SQL解读

    这篇文章主要介绍了MyBatis之关于动态SQL解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • java阿拉伯数字转中文数字

    java阿拉伯数字转中文数字

    这篇文章主要为大家详细介绍了java实现阿拉伯数字转换为中文数字,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-04-04
  • Java序列化原理详解

    Java序列化原理详解

    这篇文章主要介绍了Java序列化原理详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下
    2022-06-06
  • 浅谈Java中@Autowired和@Inject注解的区别和使用场景

    浅谈Java中@Autowired和@Inject注解的区别和使用场景

    本文主要介绍了浅谈Java中@Autowired和@Inject注解的区别和使用场景,@Autowired注解在依赖查找方式和注入方式上更加灵活,适用于Spring框架中的依赖注入,而@Inject注解在依赖查找方式上更加严格,适用于Java的依赖注入标准,感兴趣的可以了解一下
    2023-11-11
  • Java并发编程之volatile与JMM多线程内存模型

    Java并发编程之volatile与JMM多线程内存模型

    这篇文章主要介绍了Java并发volatile与JMM多线程内存模型,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • Java基础之extends用法详解及简单实例

    Java基础之extends用法详解及简单实例

    这篇文章主要介绍了 Java基础之extends用法详解及简单实例的相关资料,需要的朋友可以参考下
    2017-02-02
  • springboot log4j2日志框架整合与使用过程解析

    springboot log4j2日志框架整合与使用过程解析

    这篇文章主要介绍了springboot log4j2日志框架整合与使用,包括引入maven依赖和添加配置文件log4j2-spring.xml的相关知识,需要的朋友可以参考下
    2022-05-05
  • java判断字符串是否有逗号的方法

    java判断字符串是否有逗号的方法

    下面小编就为大家带来一篇java判断字符串是否有逗号的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • Java获得一个数组的指定长度排列组合算法示例

    Java获得一个数组的指定长度排列组合算法示例

    这篇文章主要介绍了Java获得一个数组的指定长度排列组合算法,结合实例形式分析了java排列组合相关数组遍历、运算操作技巧,需要的朋友可以参考下
    2019-06-06

最新评论