关于java:一次恐怖的-Java-内存泄漏排查实战

44次阅读

共计 3150 个字符,预计需要花费 8 分钟才能阅读完成。

作者:小怪聊职场
https://www.jianshu.com/p/454…

最近在看《深刻了解 Java 虚拟机:JVM 高级个性与最佳实际》(第二版)这本书,实践 + 实际联合,深入浅出,强烈推荐给大家。

这两天对 JVM 内容进行了一个探讨,探讨的内容次要包含如下几个方面。

1)内存溢出和内存泄露的介绍?
2)如何排查和解决内存泄露?

一、内存溢出和内存泄露

一种艰深的说法。

1、内存溢出:你申请了 10 个字节的空间,然而你在这个空间写入 11 或以上字节的数据,呈现溢出。

2、内存透露:你用 new 申请了一块内存,起初很长时间都不再应用了(按理应该开释),然而因为始终被某个或某些实例所持有导致 GC 不能回收,也就是该被开释的对象没有开释。点击此处查看内存透露更多阐明。

上面具体介绍。

1.1 内存溢出

java.lang.OutOfMemoryError,是指程序在申请内存时,没有足够的内存空间供其应用,呈现 OutOfMemoryError。点击此处查看内存透露更多阐明。

产生起因

产生该谬误的起因次要包含:

  • JVM 内存过小。
  • 程序不紧密,产生了过多的垃圾。

程序体现

个别状况下,在程序上的体现为

  • 内存中加载的数据量过于宏大,如一次从数据库取出过多数据。
  • 汇合类中有对对象的援用,应用完后未清空,使得 JVM 不能回收。
  • 代码中存在死循环或循环产生过多反复的对象实体。
  • 应用的第三方软件中的 BUG。
  • 启动参数内存值设定的过小。

谬误提醒

此谬误常见的谬误提醒:

tomcat:java.lang.OutOfMemoryError: PermGen space
tomcat:java.lang.OutOfMemoryError: Java heap space
weblogic:Root cause of ServletException java.lang.OutOfMemoryError
resin:java.lang.OutOfMemoryError
java:java.lang.OutOfMemoryError

解决办法

**1)减少 JVM 的内存大小
**

对于 tomcat 容器,找到 tomcat 在电脑中的装置目录,进入这个目录,而后进入 bin 目录中,在 window 环境下找到 bin 目录中的 catalina.bat,在 linux 环境下找到 catalina.sh。
编辑 catalina.bat 文件,找到 JAVA_OPTS(具体来说是 set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%")这个选项的地位,这个参数是 Java 启动的时候,须要的启动参数。
也能够在操作系统的环境变量中对 JAVA_OPTS 进行设置,因为 tomcat 在启动的时候,也会读取操作系统中的环境变量的值,进行加载。
如果是批改了操作系统的环境变量,须要重启机器,再重启 tomcat,如果批改的是 tomcat 配置文件,须要将配置文件保留,而后重启 tomcat,设置就能失效了。

2)优化程序,开释垃圾

次要思路就是防止程序体现上呈现的状况。防止死循环,避免一次载入太多的数据,进步程序强壮型及时开释。因而,从根本上解决 Java 内存溢出的惟一办法就是批改程序,及时地开释没用的对象,开释内存空间。

1.2  内存泄露

Memory Leak,是指程序在申请内存后,无奈开释已申请的内存空间,一次内存泄露危害能够疏忽,但内存泄露沉积结果很重大,无论多少内存,迟早会被占光。

在 Java 中,内存透露就是存在一些被调配的对象,这些对象有上面两个特点。

1)首先,这些对象是可达的,即在有向图中,存在通路能够与其相连;
2)其次,这些对象是无用的,即程序当前不会再应用这些对象。

如果对象满足这两个条件,这些对象就能够断定为 Java 中的内存透露,这些对象不会被 GC 所回收,然而它却占用内存。

对于内存泄露的解决页就是进步程序的强壮型,因为内存泄露是纯代码层面的问题。点击此处查看内存透露更多阐明。

1.3 内存溢出和内存泄露的分割

内存泄露会最终会导致内存溢出。

相同点:都会导致利用程序运行呈现问题,性能降落或挂起。
不同点:1) 内存泄露是导致内存溢出的起因之一,内存泄露积攒起来将导致内存溢出。2) 内存泄露能够通过欠缺代码来防止,内存溢出能够通过调整配置来缩小产生频率,但无奈彻底防止。

二、一个 Java 内存透露的排查案例

某个业务零碎在一段时间忽然变慢,咱们狐疑是因为呈现内存泄露问题导致的,于是踏上排查之路。

2.1 确定频繁 Full GC 景象

首先通过“虚拟机过程情况工具:jps”找出正在运行的虚拟机过程,最次要是找出这个过程在本地虚拟机的惟一 ID(LVMID,Local Virtual Machine Identifier),因为在前面的排查过程中都是须要这个 LVMID 来确定要监控的是哪一个虚拟机过程。

同时,对于本地虚拟机过程来说,LVMID 与操作系统的过程 ID(PID,Process Identifier)是统一的,应用 Windows 的工作管理器或 Unix 的 ps 命令也能够查问到虚拟机过程的 LVMID。

jps 命令格局为:
jps [options] [hostid]
应用命令如下:
应用 jps:jps -l

应用 ps:ps aux | grep tomat  找到你须要监控的 ID(假如为 20954),再利用“虚拟机统计信息监督工具:jstat”监督虚拟机各种运行状态信息。

jstat 命令格局为:
jstat [option vmid [interval[s|ms] [count]] ]
应用命令如下:
jstat -gcutil 20954 1000
意思是每 1000 毫秒查问一次,始终查。gcutil 的意思是已应用空间站总空间的百分比。

后果如下图:

jstat 执行后果

查问结果表明:这台服务器的新生代 Eden 区(E,示意 Eden)应用了 28.30%(最初)的空间,两个 Survivor 区(S0、S1,示意 Survivor0、Survivor1)别离是 0 和 8.93%,老年代(O,示意 Old)应用了 87.33%。程序运行以来共产生 Minor GC(YGC,示意 Young GC)101 次,总耗时 1.961 秒,产生 Full GC(FGC,示意 Full GC)7 次,Full GC 总耗时 3.022 秒,总的耗时(GCT,示意 GC Time)为 4.983 秒。

2.2  找出导致频繁 Full GC 的起因

分析方法通常有两种:

1)把堆 dump 下来再用 MAT 等工具进行剖析,但 dump 堆要花较长的工夫,并且文件微小,再从服务器上拖回本地导入工具,这个过程有些折腾,不到万不得已最好别这么干。

2)更轻量级的在线剖析,应用“Java 内存影像工具:jmap”生成堆转储快照(个别称为 headdump 或 dump 文件)。

jmap 命令格局:
jmap [option] vmid
应用命令如下:
jmap -histo:live 20954
查看存活的对象状况,如下图所示:

存活对象

依照一位 IT 友的说法,数据不失常,十有八九就是泄露的。在我这个图上对象还是挺失常的。

我在网上找了一位博友的不失常数据,如下:

能够看出 HashTable 中的元素有 5000 多万,占用内存大概 1.5G 的样子。这必定不失常。

2.3 定位到代码

定位带代码,有很多种办法,比方后面提到的通过 MAT 查看 Histogram 即可找出是哪块代码。——我以前是应用这个办法。也能够应用 BTrace,我没有应用过。

近期热文举荐:

1.600+ 道 Java 面试题及答案整顿 (2021 最新版)

2. 终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3. 阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0