【内存泄漏】一个现网问题告诉你血淋淋的事实:java内存泄漏很严重
目录
- 什么是内存泄漏
- GC原理
- GC Roots对象
- java内存模型
- 现网问题
- 如何发现和解决
- 总结,什么情况下会出现内存泄漏
很多同学可能都有一个误解,C++才需要程序员自己管理对象的生命周期,在对象消亡的时候,需要手工释放对象,java有GC机制,不需要关注对象消亡,更不会有内存泄漏。
什么是内存泄漏
内存泄漏的本质就是对于程序来讲已经没有存在意义的对象继续驻留内存中,无法被GC回收,导致的内存被浪费现象。当无法回收的内存累积到一定程度时会发生性能急剧下降或者OOM。
GC原理
GC通过引用记数和对象可达判断一个对象是否应该被回收。
引用记数:每个对象有一个引用记数属性,新增加一个引用时加1,引用释放时记数减1,计数为0时,对象可以被回收。但是引用记数无法解决循环引用的问题。如下图中对象F和对象E,E和F对象没有被真正使用,但是无法被判定为应该被回收的对象。
可达判断:抽象对象间的引用关系是一个有向图,根节点是GC Roots。从GC Roots沿着引用链搜索,当一个对象到GC Roots没有引用链连接时,则该对象不可达,即该对象没有被引用,是一个可以被回收的对象。判断对象可达,能够解决循环引用的问题。
GC Roots对象
GC(Garbage Collector) Roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC Roots且没有被GC Roots引用的对象。
一个对象可以属于多个GC Roots,GC Roots有几下种:
- Class - 由系统类加载器(system class loader)加载的对象,这些类是不能够被回收的,他们可以以静态字段的方式保存持有其它对象。我们需要注意的一点就是,通过用户自定义的类加载器加载的类,除非相应的java.lang.Class实例以其它的某种(或多种)方式成为Roots,否则它们并不是roots。
- Thread - 活着的线程
- Stack Local - Java方法的local变量或参数
- JNI Local - JNI方法的local变量或参数
- JNI Global - 全局JNI引用
- Monitor Used - 用于同步的监控对象
- Held by JVM - 用于JVM特殊目的由GC保留的对象,但实际上这个与JVM的实现是有关的。可能已知的一些类型是:系统类加载器、一些JVM知道的重要的异常类、一些用于处理异常的预分配对象以及一些自定义的类加载器等。然而,JVM并没有为这些对象提供其它的信息,因此需要去确定哪些是属于"JVM持有"的了。
java内存模型
java运行时的内存分配策略为:静态分配、栈式分配、堆式分配,各分配策略对应内存区及存储的数据如下图。
现网问题
在整体架构上,我们现网环境是一个三方鉴权中心的接入方,在环境上我们有一个模块A,用于和鉴权中心通信,完成用户的单点登录。在系统上线运行两周后,模块A的CPU和内存使用率持续增加,CPU时间累积占用152.62%(8核)、内存占用超过35.80%。导致用户登录较慢或者无法登录,影响较大。以上问题在测试环境还无法模拟出来。
如何发现和解决
减少线程数
由于本模块里面有大量的鉴权操作及线程操作,每次用户操作都会查询数据库进行鉴权。怀疑是线程过多以及线程中有大量的临时变量导致。初步思路是使用线程池减少随意的线程增长、使用缓存存储权限数据(权限数据基本固定、定时查询即可)。但是整改后,经过一周,CPU和内存使用率依然增长。
Jstack工具
过了一周问题又重现了,由于不能使用过长的时间分析现网问题,只能先用jstack -l 1 > jstack.log保存下当前时刻的线程快照(进程运行在docker容器中,进程ID是1),然后重启A服务,先尽快的恢复服务(万能的重启)。
通过对线程堆栈的分析,发现存在7000多个IdleConnectionMonitorThread,并且线程还是等待状态。
通过对A模块代码分析,该线程是在与三方鉴权中心对接时通过三方的SDK创建。
public void init() {this.uri = String.format("%s://%s", this.protocal, this.domainName);ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();Registry<ConnectionSocketFactory> registry = RegistryBuilder.create().register("http", plainsf).register("https", sslsf).build();PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);cm.setMaxTotal(800);cm.setDefaultMaxPerRoute(400);HttpClientBuilder httpClientBuilder = HttpClients.custom();if (null != this.getRoutePlanner()) {httpClientBuilder = httpClientBuilder.setRoutePlanner(this.getRoutePlanner());}this.httpClient = httpClientBuilder.setConnectionManager(cm).setConnectionManagerShared(true).build();this.idleConnectionMonitorThread = new ExecutableClient.IdleConnectionMonitorThread(cm, this.idletime);this.idleConnectionMonitorThread.start();}
继续排查代码。发现A模块在每次使用完毕后,没有调用destroy方法导致。
public void destroy() {if (this.idleConnectionMonitorThread != null) {this.idleConnectionMonitorThread.shutdown();try {this.idleConnectionMonitorThread.join();} catch (InterruptedException var3) {}}if (this.httpClient != null) {try {this.httpClient.close();} catch (IOException var2) {}}}
解决方法
每次使用完毕三方SDK的资源对象后,调用destroy()方法。这是个典型的资源使用完毕没有释放,导致内存泄漏的问题。
修改后,内存使用率从35.80%降至4.6%。CPU也下降50%,但是依然有92.4%,肯定也有问题,请见我另一篇博客记一次由于临时变量导致的CPU使用率过高问题。
总结,什么情况下会出现内存泄漏
1、静态集合类引起内存泄漏
2、监听器
3、未关闭的资源类:如数据库连接、网络连接和IO连接等。
4、内部类和外部模块的引用。
4、单例模式
参考:https://blog.csdn.net/CurWer_SenFor/article/details/128644647
【内存泄漏】一个现网问题告诉你血淋淋的事实:java内存泄漏很严重相关推荐
- java split空值也保留_Java内存大家都知道,但你知道要怎么管理Java内存吗?
前言 深入研究Java内存管理,将增强你对堆如何工作.引用类型和垃圾回收的认识. 你可能会思考,如果你使用Java编程,关于内存如何工作你需要了解哪些哪些信息?Java可以进行自动内存管理,而且有一个 ...
- eclipse无法创建java虚拟机_手把手:Java内存泄漏分析Memory Analyzer Tool
点击上方"IT牧场",选择"设为星标"点击上方"IT牧场",选择"设为星标"技术干货每日送达 阅读文本大概需要3分钟. ...
- JAVA内存泄露分析和解决方案及WINDOWS自带查看工具
JAVA内存泄露分析和解决方案及WINDOWS自带查看工具 Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最 ...
- Java内存管理:深入Java内存区域
Java内存管理:深入Java内存区域 本文引用自:深入理解Java虚拟机的第2章内容 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述 ...
- Java核心技术- Java内存分配原理
Java内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入Java核心,详细介绍一下Java在内存分配方面的知识.一般 ...
- jvm 堆外内存_jvm┃java内存区域,跳槽大厂必会知识点
正文约: 2000字 预计阅读时间: 6分钟 文章首发于我的微信公众号:哪儿来的moon,欢迎大家关注 目录 目录 前言 正文 1.程序计数器 2.虚拟机栈 3.本地方法栈 4.堆 5.方法区 6.直 ...
- 利用java虚拟机的工具jmap分析java内存情况
2019独角兽企业重金招聘Python工程师标准>>> 有时候碰到性能问题,比如一个java application出现out of memory,出现内存泄漏的情况,再去修改bug ...
- java 内存详解_Java内存详解
一.了解java的内存大致划分 栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中. 堆:存放用new产生的数据 静态域:存放在对象中用static定义的静态成员 常量池:存放 ...
- 深度解析java内存原理
本文主要通过分析Java内存分配的栈.堆以以及常量池详细的讲解了其的工作原理. 一.Java虚拟机内存原型 寄存器:我们在程序中无法控制栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是 ...
最新文章
- AAAI 2020 论文接收结果出炉,得分 997 论文被拒,388 反而中了?
- cef在android中使用_嵌入Chrome cef之完整版(本教程基于cef1)
- Delphi与c++ 数据类型对照表(从万一的博客园摘录)
- php5.3.*编译出现make: *** [ext/gd/libgd/gd_compat.lo] Error 1 解决方法
- PIC32单片机harmony开发环境 - uart例程和代码分析
- 常用正则表达式,来自新浪微博的js
- 如何使用AOP改进.NET应用程序
- java GC垃圾回收
- Palo Alto Networks下一代安全平台五大创新功能:云安全为重中之重
- Java 开发常用资源
- 基于canny的边缘检测算法:
- 数学分析教程 第十八章学习感受
- 航摄比例尺、成图比例尺、地面分辨率与航摄设计用图比例尺
- MYSQL内存请求一直不释放_MySQL内存不释放
- Shinobi视频监控平台
- react-router嵌套路由页面无法显示
- ad18差分布线,设置差分对
- 我曾被stormzhang拉黑过
- 2021年JavaScript最新手机号码、电话号码正则表达式
- 统计员工业绩app_统计员工业绩app