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

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

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 进行本地分析,可以不用记住那么多命令。

总结:

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

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

(0)

相关推荐

  • Java中的内存泄漏

    Java.Lang.OutOfMemoryError: Java Heap Space Java应用程序只允许使用有限的内存.此限制在应用程序启动期间指定.为了使事情更复杂,Java内存被分成两个不同的区域.这些区域称为永久生成区域(permgene和Permgen): 这些区域的大小是在Java虚拟机(JVM)启动期间设置的,可以通过指定JVM参数-Xmx和-XX:MaxPermSize进行定制.如果未显式设置大小,则将使用特定于平台的默认值. 这个java.lang.OutOfMemoryE

  • Java基础详解之内存泄漏

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

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

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

  • Java内部类的实现原理与可能的内存泄漏说明

    在使用java内部类的时候要注意可能引起的内存泄漏 代码如下 package com.example; public class MyClass { public static void main(String[] args) throws Throwable { } public class A{ public void methed1(){ } } public static class B{ public void methed1(){ } } 编译生成了如下文件 反编译MyClass 反

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

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

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

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

  • 一篇文章带你搞定JAVA反射

    目录 1.反射的概念 1.概念 2.获取字节码文件对象的方式 2.1 元数据的概念 2.2 获取class对象的方式 1.访问权限 2.获取方法 2.1 访问静态方法 2.2 访问类方法 3.获取字段,读取字段的值 4.获取实现的接口 5.获取构造函数,创建实例 6.获取继承的父类 7.获取注解 4.反射实例 5.总结 1.反射的概念 1.概念 反射,指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对任意一个对象,都能调用它的任意一个方法.这种动态获取信息,以及动态调用对象方法

  • 一篇文章带你搞定JAVA泛型

    目录 1.泛型的概念 2.泛型的使用 3.泛型原理,泛型擦除 3.1 IDEA 查看字节码 3.2 泛型擦除原理 4.?和 T 的区别 5.super extends 6.注意点 1.静态方法无法访问类的泛型 2.创建之后无法修改类型 3.类型判断问题 4.创建类型实例 7.总结 1.泛型的概念 泛型的作用就是把类型参数化,也就是我们常说的类型参数 平时我们接触的普通方法的参数,比如public void fun(String s):参数的类型是String,是固定的 现在泛型的作用就是再将St

  • 一篇文章带你搞定JAVA注解

    目录 1.注解是什么 2.jdk支持的注解有哪些 2.1 三种常用的注解: 2.2 元注解 3.注解实例 1.自定义注解 2.在对应的方法上增加注解 3.在项目启动的时候检查注解的枚举 4.总结 1.注解是什么 Java 注解用于为 Java 代码提供元数据,看完这句话也许你还是一脸懵逼,用人话说就是注解不直接影响你的代码执行,仅提供信息.接下我将从注解的定义.元注解.注解属性.自定义注解.注解解析JDK 提供的注解这几个方面再次了解注解(Annotation) 2.jdk支持的注解有哪些 2.

  • 一篇文章带你搞定JAVA Maven

    目录 1.maven是什么,为什么存在?项目结构是什么样子,怎么定位jar 2.Idea 的操作 1.新建maven项目 2.配置仓库 3.添加依赖,添加fastjson的依赖 4.打包项目 3.Maven坐标主要组成 4.maven生命周期 4.1 名词解释 4.2 生命周期 4.3 goal 的概念 4.4 生命周期和phase的关系 5.idea maven的配置 6.POM有2个很重要的关系:聚合.继承 一.聚合 二.继承 7.Maven 中的 profile 8.maven 插件 9.

  • 一篇文章带你搞定 springsecurity基于数据库的认证(springsecurity整合mybatis)

    一.前期配置 1. 加入依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> &

  • 一篇文章带你搞定Python多进程

    目录 1.Python多进程模块 2.Python多进程实现方法一 3.Python多进程实现方法二 4.Python多线程的通信 5.进程池 1.Python多进程模块 Python中的多进程是通过multiprocessing包来实现的,和多线程的threading.Thread差不多,它可以利用multiprocessing.Process对象来创建一个进程对象.这个进程对象的方法和线程对象的方法差不多也有start(), run(), join()等方法,其中有一个方法不同Thread线

  • 一篇文章带你搞定SpringBoot中的热部署devtools方法

    一.前期配置 创建项目时,需要加入 DevTools 依赖 二.测试使用 (1)建立 HelloController @RestController public class HelloController { @GetMapping("/hello") public String hello(){ return "hello devtools"; } } 对其进行修改:然后不用重新运行,重新构建即可:只加载变化的类 三.热部署的原理 Spring Boot 中热部

  • 一篇文章带你搞定SpringBoot不重启项目实现修改静态资源

    一.通过配置文件控制静态资源的热部署 在配置文件 application.properties 中添加: #表示从这个默认不触发重启的目录中除去static目录 spring.devtools.restart.exclude=classpath:/static/** 或者使用: #表示将static目录加入到修改资源会重启的目录中来 spring.devtools.restart.additional-paths=src/main/resource/static 此时对static 目录下的静态

  • 一篇文章带你搞定Ubuntu中打开Pycharm总是卡顿崩溃

    由于 Ubuntu 中的汉字输入实在是太不友好了,所以装了个 搜狗输入法,好不容易把 搜狗输入法装好,本以为可以开开心心的搞代码了,然而... pycharm 一打开,就崩溃,关不掉,进程杀死还是不行,只能关机重启. 本以为 pycharm 出现了问题,又重装了两遍,还是不行. 最终发现竟然是搜狗输入法以及 fcitx 输入法的锅 唉,只能老老实实的把 fctix 和搜狗输入法卸载了: (1)Ubuntu 软件里卸载 fctix,然后将键盘输入法系统改成 IBus (2)卸载搜狗输入法 先查找软

随机推荐