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

JVM之内存泄漏的详细解析

内存泄漏

泄露溢出

内存泄漏(Memory Leak):是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果

可达性分析算法来判断对象是否是不再使用的对象,本质都是判断一个对象是否还被引用。由于代码的实现不同就会出现很多种内存泄漏问题,让 JVM 误以为此对象还在引用中,无法回收,造成内存泄漏

内存溢出(out of memory)指的是申请内存时,没有足够的内存可以使用

内存泄漏和内存溢出的关系:内存泄漏的越来越多,最终会导致内存溢出


几种情况
静态集合

静态集合类的生命周期与 JVM 程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。原因是长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收

public class MemoryLeak {
    static List list = new ArrayList();
    public void oomTest(){
        Object obj = new Object();//局部变量
        list.add(obj);
    }
}

单例模式

单例模式和静态集合导致内存泄露的原因类似,因为单例的静态特性,它的生命周期和 JVM 的生命周期一样长,所以如果单例对象持有外部对象的引用,那么这个外部对象也不会被回收,那么就会造成内存泄漏


内部类

内部类持有外部类的情况,如果一个外部类的实例对象调用方法返回了一个内部类的实例对象,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象也不会被回收,造成内存泄漏


连接相关

数据库连接、网络连接和 IO 连接等,当不再使用时,需要显式调用 close 方法来释放与连接,垃圾回收器才会回收对应的对象,否则将会造成大量的对象无法被回收,从而引起内存泄漏


不合理域

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

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

通过 readFromNet 方法把接收消息保存在 msg 中,然后调用 saveDB 方法把内容保存到数据库中,此时 msg 已经可以被回收,但是 msg 的生命周期与对象的生命周期相同,造成 msg 不能回收,产生内存泄漏

解决:

  • msg 变量可以放在 receiveMsg 方法内部,当方法使用完,msg 的生命周期也就结束,就可以被回收了

  • 在使用完 msg 后,把 msg 设置为 null,这样垃圾回收器也会回收 msg 的内存空间。


改变哈希

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


缓存泄露

内存泄漏的一个常见来源是缓存,一旦把对象引用放入到缓存中,就会很容易被遗忘

使用 WeakHashMap 代表缓存,当除了自身有对 key 的引用外没有其他引用,map 会自动丢弃此值


案例分析
public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
​
    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }
​
    public void push(Object e) { //入栈
        ensureCapacity();
        elements[size++] = e;
    }
​
    public Object pop() { //出栈
        if (size == 0)
            throw new EmptyStackException();
        return elements[--size];
    }
​
    private void ensureCapacity() {
        if (elements.length == size)
            elements = Arrays.copyOf(elements, 2 * size + 1);
    }
}

程序并没有明显错误,但 pop 函数存在内存泄漏问题,因为 pop 函数只是把栈顶索引下移一位,并没有把上一个出栈索引处的引用置空,导致栈数组一直强引用着已经出栈的对象

解决方法:

public Object pop() {
    if (size == 0)
        throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null;
    return result;
}


http://www.kler.cn/a/507643.html

相关文章:

  • 摄像头模块在狩猎相机中的应用
  • 微信小程序校园自助点餐系统实战:从设计到实现
  • 图论的起点——七桥问题
  • 基于 K-Means 聚类分析实现人脸照片的快速分类
  • 如何优化Elasticsearch大文档查询?
  • pytorch小记(一):pytorch矩阵乘法:torch.matmul(x, y)
  • 处理 SQL Server 中的表锁问题
  • JAVA之原型模式
  • pandoc + wkhtmltox 批量转换Markdown文件为PDF文件
  • docker报错 无法连接registry-1.docker.io,pull镜像失败
  • Android渲染Latex公式的开源框架比较
  • SQL和MySQL以及DAX的日期表生成?数字型日期?将生成的日期表插入到临时表或者实体表中
  • .NET Core封装Activex Dll,向COM公开.NET Core组件
  • (学习总结20)C++11 可变参数模版、lambda表达式、包装器与部分新内容添加
  • 5-1 创建和打包AXI Interface IP
  • 备份和容灾之区别(The Difference between Backup and Disaster Recovery)
  • PDF文件提取开源工具调研总结
  • 国产编辑器EverEdit - 复制为RTF
  • 【vue】rules校验规则简单描述
  • 人工智能之深度学习-[1]-了解深度学习
  • 动态路由vue-router
  • SpringBoot中整合RabbitMQ(测试+部署上线 最完整)
  • 【例43.3】 转二进制
  • Django学堂在线笔记-1
  • FreeRTOS 简介
  • Module 模块