JVM 堆外内存泄漏分析(一)

标签: JVM K8S 堆外内存泄漏 | 发表时间:2019-09-13 08:19 | 作者:coderbee
出处:https://coderbee.net

1. JVM 感知容器资源

Java 应用部署在 Kubernetes 集群里,每个容器只运行一个进程, JVM 的启动命令是打包在镜像文件里的。

常规的方式是采用 -Xmx4g -Xms2g 这样的参数来指定 JVM 堆的最大、最小尺寸,如果需要调整堆大小就需要重新打包镜像。

为了避免因为修改堆大小而重新打包,从 JDK 8u191 版本开始支持 JVM 感知容器资源限制,这样在调整 JVM 内存分配时就不需要重新打包镜像文件,采用下面的参数来使 JVM 在启动时感知到容器的资源限制,并设定堆的大小:

  -XX:+UseCGroupMemoryLimitForHeap
-XX:InitialRAMPercentage=60.00
-XX:MaxRAMPercentage=80.00
-XX:MinRAMPercentage=60.00 

假如分配给容器的内存上限是 4G,那么上述配置,JVM 堆的初始大小和最小尺寸是 4G * 0.6 即 2.4G,最大尺寸是 4G * 0.8 即 3.2G。

2. JVM 被 oomkill

上面的配置运行一段时间后发现容器自动重启了,在 linux 下通过 dmesg 命令查看系统日志,可以看到类似下面的日志:

  Aug  8 15:32:40 H-LDOCKER-01 kernel: [ pid ]   uid   tgid   total_vm      rss        nr_ptes   nr_pmds   swapents   oom_score_adj  name
Aug  8 15:32:40 H-LDOCKER-01 kernel: [33775]   1001  33775  7624373       2036828    4476      32        0          -998           java
Aug  8 15:32:40 H-LDOCKER-01 kernel: Memory cgroup out of memory: Kill process 33775 (java) score 0 or sacrifice child
Aug  8 15:32:40 H-LDOCKER-01 kernel: Killed process 33775 (java) total-vm:30497492kB, anon-rss:8134056kB, file-rss:13256kB

注意:上面日志 rss 列表示进程占用的内存大小,对应的值是 2036828,单位是 4KB,也即这个 Java 进程占用了 7.77G,容器分配的内存上限是 8G。第3、4行表示 Java 进程被 oom_killer 了。

OOM_killer 是 Linux 的一种自我保护措施,当系统内存不足时为防止出现严重问题,系统唤醒 oom_killer,挑出 /proc/<pid>/oom_score 值最大的进程并 kill。

因为应用也输出了 GC 日志,从进程被 kill 前的那个时间节点的日志来看,JVM 的堆是远远没有 7G 那么大的,多出来的其实是堆外内存。

3. JVM 堆外内存

JVM 的堆外内存主要包括:

  • JVM 自身运行占用的空间;
  • 线程栈分配占用的系统内存;
  • DirectByteBuffer 占用的内存;
  • JNI 里分配的内存;
  • Java 8 开始的元数据空间;
  • NIO 缓存
  • Unsafe 调用分配的内存;
  • codecache

冰山对象:冰山对象是指在 JVM 堆里占用的内存很小,但其实引用了一块很大的本地内存。DirectByteBuffer 和 线程都属于这类对象。

堆外内存泄漏一般很难通过 MAT 之类的工具来分析,必须通过操作系统层面的工具来。

关于本地内存的可以参考 IBM 的一个分享 《Where Does All The Native Memory Go》,可以网上找下这个 PPT,下面是其中的一部分:
where-does-all-native-memory-go-2

where-does-all-native-memory-go-3

where-does-all-native-memory-go-5

where-does-all-native-memory-go-6


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

相关 [jvm 内存泄漏 分析] 推荐:

JVM 堆外内存泄漏分析(一)

- - coderbee笔记
Java 应用部署在 Kubernetes 集群里,每个容器只运行一个进程, JVM 的启动命令是打包在镜像文件里的. 常规的方式是采用 -Xmx4g -Xms2g 这样的参数来指定 JVM 堆的最大、最小尺寸,如果需要调整堆大小就需要重新打包镜像. 为了避免因为修改堆大小而重新打包,从 JDK 8u191 版本开始支持 JVM 感知容器资源限制,这样在调整 JVM 内存分配时就不需要重新打包镜像文件,采用下面的参数来使 JVM 在启动时感知到容器的资源限制,并设定堆的大小:.

JVM 堆外内存泄漏分析(二)

- - coderbee笔记
关于 堆外内存的组成可以看上一篇文章 JVM 堆外内存泄漏分析(一). NMT(Native Memory Tracking)是 HotSpot JVM 引入的跟踪 JVM 内部使用的本地内存的一个特性,可以通过 jcmd 工具访问 NMT 数据. NMT 目前不支持跟踪第三方本地代码的内存分配和 JDK 类库.

String substring的内存泄漏分析和优化方法

- - ITeye博客
本文将对String.substring方法可能产生内存泄漏的问题进行分析,并给出相应的优化方法. String.substring内存泄漏分析. 首先看一下JDK6 String.substring的源代码:. 从上述的源代码可以看出,使用substring获取子字符串方法中,原有字符串的内容value(char[])将继续重用.

使用Memory Analyzer tool(MAT)分析内存泄漏

- - 移动开发 - ITeye博客
前言的前言:本文是自2005年8月以来,首次在一个月之内发布三篇文章. 谨以此文献给这么多年始终不济的我. 北漂快两年了,何时能回到故乡,回去后又会怎样,也许永远是个未知……. 在平时工作过程中,有时会遇到OutOfMemoryError,我们知道遇到Error一般表明程序存在着严重问题,可能是灾难性的.

内存泄漏

- - CSDN博客系统运维推荐文章
程序申请了堆空间,但是“忘记”释放,导致该块区域在程序结束前无法被再次使用导致的. 泄漏时间长了,就会导致用户空间内存不足,严重的导致死机. 如果泄漏比较严重,很容易察觉;但是有些泄漏很缓慢,不容易察觉,但是软件会运行很长时间后,会慢慢导致严重问题,而且当发现症状的时候,基本上已经是比较晚的时候了,想要识别泄漏,还是可以实现的,本篇文章来聊聊内存操作的原理.

jvm内存映像分析

- - ITeye博客
     jdk自带的jmap就是java内存映像工具,可以用于上生成堆转储快照:. 在eclipse中启动一个java类,打开jdk安装目录下的C:\Program Files\Java\jdk1.6.0_11\bin目录,双击jconsole.exe,显示连接窗口:.  ,单击pid为6920的选项,点连接进入,可以看到jvm运行时的多种参数,.

JVM原理分析笔记

- - Java - 编程语言 - ITeye博客
1.Javac编译器的作用. 将符合Java语言规范的源代码转化成符合Java虚拟机规范的Java字节码. 2.编译器主要的几个处理阶段. 词法分析、语法分析、语义分析和代码生成,基于访问者模式来遍历语法树的过程. 二.ClassLoader. 将Class加载到JVM中,审查每个类应该由谁加载,将Class字节码重新解析成JVM统一要求的对象格式.

MAT JVM内存分析

- - 开源软件 - ITeye博客
我们使用的是 Eclipse Memory Analyzer V0.8,Sun JDK 6. 和其他插件的安装非常类似,MAT 支持两种安装方式,一种是“单机版“的,也就是说用户不必安装 Eclipse IDE 环境,MAT 作为一个独立的 Eclipse RCP 应用运行;另一种是”集成版“的,也就是说 MAT 也可以作为 Eclipse IDE 的一部分,和现有的开发平台集成.

java内存泄漏

- - 编程语言 - ITeye博客
不论哪种语言的内存分配方式,都需要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址. Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的. GC为了能够正确释放对象,会监控每个对象的运行状况,对他们的申请、引用、被引用、赋值等状况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否可以达到,如果不可到达,则就将其回收,这样也可以消除引用循环的问题.

JVM系列三:JVM参数设置、分析

- - zzm
 不管是YGC还是Full GC,GC过程中都会对导致程序运行中中断,正确的选择 不同的GC策略,调整JVM、GC的参数,可以极大的减少由于GC工作,而导致的程序运行中断方面的问题,进而适当的提高Java程序的工作效率. 但是调整GC是以个极为复杂的过程,由于各个程序具备不同的特点,如:web和GUI程序就有很大区别(Web可以适当的停顿,但GUI停顿是客户无法接受的),而且由于跑在各个机器上的配置不同(主要cup个数,内存不同),所以使用的GC种类也会不同(如何选择见 GC种类及如何选择).