Java内存泄漏问题排查与解决

前言

Java 最牛逼的一个特性就是垃圾回收机制,不用像 C++ 需要手动管理内存,所以作为 Java 程序员很幸福,只管 New New New 即可,反正 Java 会自动回收过期的对象。。。

那么 Java 都自动管理内存了,那怎么会出现内存泄漏,难道 Jvm 有 bug? 不要急,且听我慢慢道来。。

1. 怎么判断可以被回收

先了解一下 Jvm 是怎么判断一个对象可以被回收。一般有两种方式,一种是引用计数法,一种是可达性分析。

引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。

这个办法看起来挺简单的,但是如果出现 A 引用了 B,B 又引用了 A,这时候就算他们都不再使用了,但因为相互引用 计算器=1 永远无法被回收。

此方法简单,无法解决对象相互循环引用的问题。

可达性分析(Reachability Analysis):从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。

可达性分析可以解决循环引用的问题。

那么 gc roots 对象是哪些呢

虚拟机栈中引用的对象

方法区中类静态属性引用的对象

方法区中常量引用的对象

本地方法栈中JNI[即一般说的Native]引用的对象

目前主流的虚拟机中大多使用可达性分析的方式来判定对象是否可被 GC 回收。

2. 什么情况下会出现内存泄漏

既然可达性分析好像已经很牛逼的样子了,怎么可能还会出现内存泄漏呢,那我们再来看一下内存泄漏的定义。

内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。

有可能此对象已经不使用了,但是还有其它对象保持着此对象的引用,就会导致 GC 不能回收此对象,这种情况下就会出现内存泄漏。

写一个程序让出现内存泄漏

①长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收。

public class Simple {
    Object object;
    public void method1(){
        object = new Object();
        //...其他代码
    }
}

这里的 object 实例,其实我们期望它只作用于 method1() 方法中,且其他地方不会再用到它,但是,当method1()方法执行完成后,object 对象所分配的内存不会马上被认为是可以被释放的对象,只有在 Simple 类创建的对象被释放后才会被释放,严格的说,这就是一种内存泄露。

解决方法就是将 object 作为 method1() 方法中的局部变量。

public class Simple {
    Object object;
    public void method1(){
        object = new Object();
        //...其他代码
        object = null;
    }
}

当然大家有可能会想就这一个方法也不会有多大影响,但如果在某些项目中,一个方法在一分钟之内调用上万次的时候,就会出现很明显的内存泄漏现象。

②集合中的内存泄漏,比如 HashMap、ArrayList 等,这些对象经常会发生内存泄露。比如当它们被声明为静态对象时,它们的生命周期会跟应用程序的生命周期一样长,很容易造成内存不足。

下面给出了一个关于集合内存泄露的例子。

Vector v=new Vector(10);
for (int i=1;i<100; i++)
{
    Object o=new Object();
    v.add(o);
    o=null;
}
//此时,所有的Object对象都没有被释放,因为变量v引用这些对象。

在这个例子中,我们循环申请 Object 对象,并将所申请的对象放入一个 Vector 中,如果我们仅仅释放引用本身,那么 Vector 仍然引用该对象,所以这个对象对 GC 来说是不可回收的。

因此,如果对象加入到 Vector 后,还必须从 Vector 中删除,最简单的方法就是将 Vector 对象设置为 null。

以上两种是最常见的内存泄漏案例。当然还有一些内存泄漏的例子,这里就不再一一例举了,感兴趣的同学可以在网上找找资料。

3. 如何检测内存泄漏

3.1 模拟内存泄漏代码

package com.wenxiaowu.solution;

import java.util.ArrayList;
import java.util.List;

/**
 * Java 内存泄露模拟
 */
public class TestMemoryLeak {
    public static void main(String[] args) throws InterruptedException {
        List<SimpleObject> list = new ArrayList<>();
        Runtime run = Runtime.getRuntime();
        int i = 1;

        while (true) {
            SimpleObject simpleObject = new SimpleObject();
            list.add(simpleObject);
            simpleObject = null;

            if (i++ % 1000 == 0) {
                System.out.print(i + ": 最大内存=" + run.maxMemory() / 1024 / 1024 + "M,");
                System.out.print("已分配内存=" + run.totalMemory() / 1024 / 1024 + "M,");
                System.out.print("剩余空间内存=" + run.freeMemory() / 1024 / 1024 + "M");
                System.out.println("最大可用内存=" + (run.maxMemory() - run.totalMemory() + run.freeMemory()) / 1024 / 1024 + "M");
            }
            Thread.sleep(1);
        }
    }
}

class SimpleObject {
    // 初始化占用1M内存的数组
    private int[] arr = new int[1024 * 8];
    public int[] getArr() {
        return arr;
    }
}

3.2 生成dump文件

java -jar -Xmx1G -Xms1G -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp wenxiaowu-java.jar

3.3 用JProfile加载dump文件

可以看到,红框处的int数组占用空间最大

3.4 定位最大占用内存的原始位置

最终找到这个对象是在main方法中创建的。

解决办法

使用完之后,先删除对应的引用,再将对象置为null,即可正常进行内存回收。

总结

到此这篇关于Java内存泄漏问题排查与解决的文章就介绍到这了,更多相关Java内存泄漏内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

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

    目录 1.什么是内存泄漏 2.内存泄漏的原因 3.内存泄漏有哪些情况 3.1 代码中没有及时释放,导致内存无法回收. 3.2 资源未关闭造成的内存泄漏 3.3 全局缓存持有的对象不使用的时候没有及时移除,导致一直在内存中无法移除 3.4 静态集合类 3.5 堆外内存无法回收 4.内存泄漏的解决办法 5.内存问题排查 第一步 首先确认逻辑问题 第二步:分析gc是否正常执行 第三步 确认下版本新增代码的改动,尽快从代码上找出问题. 第四步:开启各种命令行和 导出 dump 各种工具分析 总结: 1.

  • java内存管理关系及内存泄露的原理分析

    目录 java内存管理关系及内存泄露原理 java对象和内存的关系 创建对象 null的作用 内存泄露 检测内存泄露的原理 java内存管理关系及内存泄露原理 这可能是最近写的博客中最接近底层的了.闲言少叙,进入正题. java对象和内存的关系 首先,我们要知道下面几条真理(自己总结的) 一个完整的建立对象流程是 1声明对象,2开辟内存空间,3将对象和内存空间建立联系. 一个对象只能对应一个内存空间,一个内存空间可以对应很多对象 回收一个内存空间 .如果,这个内存空间没有任何一个对象和他有联系.

  • 教你用MAT工具分析Java堆内存泄漏问题的解决方法

    一.MAT概述与安装 MAT,全称Memory Analysis Tools,是一款分析Java堆内存的工具,可以快速定位到堆内泄漏问题.该工具提供了两种使用方式,一种是插件版,可以安装到Eclipse使用,另一种是独立版,可以直接解压使用. 我把独立版MAT安装包放到了网盘上,方便直接下载 链接: https://pan.baidu.com/s/1DVHlHuSfi_4TVl2ei5YuLA 提取码: 42qt 独立版解压后,其内部文件是这样的-- 这里有一个MemoryAnalyzer.in

  • Java基础详解之内存泄漏

    一.什么是内存泄漏 内存泄漏是指你向系统申请分配内存进行使用(new/malloc),然后系统在堆内存中给这个对象申请一块内存空间,但当我们使用完了却没有归系统(delete),导致这个不使用的对象一直占据内存单元,造成系统将不能再把它分配给需要的程序. 一次内存泄漏的危害可以忽略不计,但是内存泄漏堆积则后果很严重,无论多少内存,迟早会被占完,造成内存泄漏. 二.Java内存泄漏引起的原因 1.静态集合类引起内存泄漏: 像HashMap.Vector等的使用最容易出现内存泄露,这些静态变量的生命

  • Java基础之内存泄漏与溢出详解

    一.浅析 内存泄露( memory leak):是指程序在申请内存后,无法释放已申请的内存空间,多次内存泄露堆积后果很严重,内存迟早会被占光.内存泄漏最终会造成内存溢出. 内存溢出(out of memory) :是指程序在申请内存时,没有足够的内存空间供其使用 JVM中有一下几种内存空间: 栈内存(Stack):每个线程私有的. 堆内存(Heap):所有线程公用的. 方法区(Method Area):有点像以前常说的"进程代码段",这里面存放了每个加载类的反射信息.类函数的代码.编译

  • Java中的内存泄露问题和解决办法

    目录 为什么会产生内存泄漏? 内存泄漏对程序的影响? 如何检查和分析内存泄漏? 常见的内存泄漏及解决方法 1.单例造成的内存泄漏 2.非静态内部类创建静态实例造成的内存泄漏[已无] 3.Handler造成的内存泄漏 4.线程造成的内存泄漏 5.资源未关闭造成的内存泄漏 6.使用ListView时造成的内存泄漏 7.集合容器中的内存泄露 8.WebView造成的泄露 如何避免内存泄漏? 总结 (Memory Leak,内存泄漏) 为什么会产生内存泄漏? 当一个对象已经不需要再使用本该被回收时,另外

  • Java怎样创建集合才能避免造成内存泄漏你了解吗

    目录 双括号语法初始化集合 不建议使用这种形式 替代方案 使用Arrays工具类 使用Stream 使用第三方工具类 Java 9内置方法 由于Java语言的集合框架中(collections, 如list, map, set等)没有提供任何简便的语法结构,这使得在建立常量集合时的工作非常繁索.每次建立时我们都要做: 1.定义一个空的集合类变量 2.向这个结合类中逐一添加元素 3.将集合做为参数传递给方法 例如,要将一个Set变量传给一个方法: Set users = new HashSet()

  • 浅谈Java内存泄露

    纳尼,Java 不是自动管理内存吗?怎么可能会出现内存泄泄泄泄泄泄漏! Java 最牛逼的一个特性就是垃圾回收机制,不用像 C++ 需要手动管理内存,所以作为 Java 程序员很幸福,只管 New New New 即可,反正 Java 会自动回收过期的对象... 那么 Java 都自动管理内存了,那怎么会出现内存泄漏,难道 Jvm 有 bug? 不要急,且听我慢慢道来.. 怎么判断可以被回收 先了解一下 Jvm 是怎么判断一个对象可以被回收.一般有两种方式,一种是引用计数法,一种是可达性分析.

  • Java内存泄漏问题排查与解决

    前言 Java 最牛逼的一个特性就是垃圾回收机制,不用像 C++ 需要手动管理内存,所以作为 Java 程序员很幸福,只管 New New New 即可,反正 Java 会自动回收过期的对象... 那么 Java 都自动管理内存了,那怎么会出现内存泄漏,难道 Jvm 有 bug? 不要急,且听我慢慢道来.. 1. 怎么判断可以被回收 先了解一下 Jvm 是怎么判断一个对象可以被回收.一般有两种方式,一种是引用计数法,一种是可达性分析. 引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加

  • 一次 Java 内存泄漏的排查解决过程详解

    由来 前些日子小组内安排值班,轮流看顾我们的服务,主要做一些报警邮件处理.Bug 排查.运营 issue 处理的事.工作日还好,无论干什么都要上班的,若是轮到周末,那这一天算是毁了. 不知道是公司网络广了就这样还是网络运维组不给力,网络总有问题,不是这边交换机脱网了就是那边路由器坏了,还偶发地各种超时,而我们灵敏地服务探测服务总能准确地抓住偶现的小问题,给美好的工作加点料.好几次值班组的小伙伴们一起吐槽,商量着怎么避过服务保活机制,偷偷停了探测服务而不让人发现(虽然也并不敢). 前些天我就在周末

  • Android 5.1 WebView内存泄漏问题及快速解决方法

    问题背景 在排查项目内存泄漏过程中发现了一些由WebView引起的内存泄漏,经过测试发现该部分泄漏只会出现在android 5.1及以上的机型.虽然项目使用WebView的场景并不多,但秉承着一个泄漏都不放过的精神,我们肯定要把它给解决了. 遇到的问题 项目中使用WebView的页面主要在FAQ页面,问题也出现在多次进入退出时,发现内存占用大,GC频繁.使用LeakCanary观察发现有两个内存泄漏很频繁: 我们分析一下这两个泄漏: 从图一我们可以发现是WebView的ContentViewCo

  • macOS上使用gperftools定位Java内存泄漏问题及解决方案

    这几天在排查一个堆外内存泄漏的问题时看到很多人都提到了gperftools这个神器,想要尝试一下结果发现它对macOS的支持不太友好.而且大多数教程是针对C++的,里面的一通编译链接的操作看得我个Java仔眼花缭乱的.所以我在这里整理一份mac和Java版的使用教程,免得大家再来踩坑了. 一.简介 gperftools是google提供的一套分析工具,包括堆内存检测heap-profiler,内存泄漏分析工具heap-checker和CPU性能监测工具cpu-profiler.众所周知堆外内存的

  • Java 内存溢出的原因和解决方法

    你是否遇到过Java应用程序卡顿或突然崩溃的情况?您可能遇到过Java内存泄漏.在本文中,我们将深入研究Java内存泄漏的确切原因,并推荐一些最好的工具来防止内存泄漏发生. 什么是JAVA内存泄漏? 简单地说,Java内存泄漏是指对象不再被应用程序使用,而是在工作内存中处于活动状态. 在Java和大多数其他编程语言中,垃圾收集器的任务是删除不再被应用程序引用的对象.如果不选中,这些对象将继续消耗系统内存,并最终导致崩溃.有时java内存泄漏崩溃不会输出错误,但通常错误会以java.lang.Ou

  • Android内存泄漏的原因及解决技巧

    正确的生命周期管理如何防止Android内存泄漏 OutOfMemoryException是一个常见的令人沮丧的错误,也是导致应用程序意外关闭的主要原因之一. "如果应用程序昨天运行良好,为什么现在会发生这种情况?这个问题让Android的开发者和新手都感到困惑. 导致OutOfMemory异常的潜在原因有很多种,但其中最常见的是内存泄漏-应用程序中的内存分配从未释放.本文将解释如何通过有效的生命周期管理(开发过程中一个重要但经常被忽视的部分)来最小化这种风险. 为什么安卓系统会发生内存泄漏?

  • java内存泄漏与内存溢出关系解析

    这篇文章主要介绍了java内存泄漏与内存溢出关系解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory: 内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光. memory leak会最终会导致out of memory!

  • GoLang内存泄漏原因排查详解

    目录 背景 临时性内存泄漏 通道理解 背景 Go 语言中有对应的Go 内存回收机制,在Go采用 并发三色标记清除  算法, 但是由于实际的过程中 发现会有一些内存泄漏的常见,内存泄漏 分为: 临时性 和 永久性内存泄漏. 初步排查过程中: 发现Linux使用top 发现内存随着时间会持续的增加没有稳定在一个合理值中. 在使用 pprof ,BBC 等 Go的内存泄漏工具进行排查 临时性内存泄漏 指的释放内存 不及时,对应的内存在更晚时候释放,这类问题主要是 string,slice 和底层的Bu

  • 简单了解JAVA内存泄漏和溢出区别及联系

    1.内存泄漏memory leak : 是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出. 2.内存溢出 out of memory : 指程序申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储int类型数据的存储空间,但是你却存储long类型的数据,那么结果就是内存不够用,此时就会报错OOM,即所谓的内存溢出. 3.二者的关系: 内存泄漏的堆积最终会导致内存溢出 内存溢出就是你要的内存空间超过了系统实际分配给你的空间

随机推荐