龙空技术网

JAVA应用内存占用率居高不下,谁来背锅

想要飞的小灰 109

前言:

如今咱们对“jvm内存使用率高是因为什么”大概比较关心,我们都需要学习一些“jvm内存使用率高是因为什么”的相关文章。那么小编也在网摘上搜集了一些有关“jvm内存使用率高是因为什么””的相关资讯,希望看官们能喜欢,各位老铁们快快来学习一下吧!

当时我正在低头认真coding。突然,某个群里弹出一条@我的消息,“某 JAVA 服务内存占用率超过90%,告警了!”

当时,大手一抖,心中一慌。赶紧去查看监控:(当前配置是2核6G)FullGC 未曾触发过,YoungGC 触发也不频繁,堆上空闲内存也是足够的。如果没有监控的话,也可以通过 top 命令去查看。

疑问

1.堆内存 used 看上去也就 1G左右,但是应用进程占用内存将近6G。难道真的是内存泄露了吗?

2.根据监控来看,内存最高时是将近6G。但是后面 GC 过,内存占用现在1G左右。那么为什么进程占用内存将近6G呢?是内存没有释放吗?

但是通过监控的相关指标来看,或者使用 top 命令来看。系统的整体负载和cpu 使用率都不高。如此开看,系统是没有问题的。那就是要看一下为什么进程内存占用率高了。如此我们先了解一下 jvm 垃圾回收和操作系统内存的关系。

JVM 内存管理

1. JVM 的垃圾回收,只是一个逻辑上的回收,回收的只是 JVM 申请的那一块逻辑堆区域,将数据标记为空闲之类的操作,而不是将内存归还给操作系统(比如调用 free)。

JVM 的自动内存管理,其实只是先向操作系统申请了一大块内存,然后自己在这块已申请的内存区域中进行“自动内存管理”。JAVA 中的对象在创建前,会先从这块申请的一大块内存中划分出一部分来给这个对象使用,在 GC 时也只是这个对象所处的内存区域数据清空,标记为空闲而已。

2. JVM 释放的内存什么时候归还给操作系统呢?这个不同的垃圾回收算法归还的时机是不同的。

JVM 归还内存给操作系统的代价比较高,所以不会轻易归还。比如在清除算法(sweep)中,是通过空闲链表(free-list)算法来分配内存的。就是将已申请的大块内存区域分为 N 个data小区域,将这些区域同链表的结构组织起来。每个 data 区域可以容纳 N 个对象,那么当一次 GC 后,某些对象会被回收,可是此时这个 data 区域中还有其他存活的对象,如果想将整个 data 区域释放那是肯定不行的。

所以这个归还内存给操作系统的操作并没有那么简单,执行起来代价过高,JVM 自然不会在每次 GC 后都进行内存的归还。

JVM 归还内存功能

JVM 提供了-XX:MinHeapFreeRatio和-XX:MaxHeapFreeRatio 两个参数,用于配置这个归还策略。

MinHeapFreeRatio 代表当空闲区域大小下降到该值时,会进行扩容,扩容的上限为 XmxMaxHeapFreeRatio 代表当空闲区域超过该值时,会进行“缩容”,缩容的下限为Xms

不过虽然有这个归还的功能,不过因为这个代价比较昂贵,所以 JVM 在归还的时候,是线性递增归还的,并不是一次全部归还。

不同的垃圾回收器,不同的 JDK 版本中都是有区别的。可以看一下:

如何在保证应用正常使用的同时,减小应用占用内存呢?

一般可以通过修改启动参数来调整。就是 Xms、Xmx 这两个配置。这里就不展开讲了。一般来说都是将 Xms和Xmx配置为同一个值,避免运行中扩容。不过 并不是配置了 5G,就直接分配5G。

进程申请内存时,不是直接分配物理内存,而是分配一块虚拟空间,到真正堆这块虚拟空间写入数据时才会通过缺页异常(Page Fault)处理机制分配物理内存,也就是我们看到的进程 Res 指标。

可以简单地认为操作系统的内存分配是“惰性”的,分配并不会发生实际的占用,有数据写入时才会发生内存占用,影响 Res。

所以,哪怕配置了Xms6G,启动后也不会直接占用 6G 内存,实际占用的内存取决于你有没有往这 6G 内存区域中写数据的。

标签: #jvm内存使用率高是因为什么