1. 现象

最近发现线上机器 java 8 进程的 VIRT 虚拟内存使用达到了 50G+,如下图所示:

2. 不管用的 -Xmx

首先第一想到的当然使用 java 的 -Xmx 去限制堆的使用。但是无论怎样设置,都没有什么效果。没办法,只好开始苦逼的研究。

3. 什么是 VIRT

现代操作系统里面分配虚拟地址空间操作不同于分配物理内存。在64位操作系统上,可用的最大虚拟地址空间有16EB,即大概180亿GB。那么在一台只有16G的物理内存的机器上,我也能要求获得4TB的地址空间以备将来使用。例如:

    void *mem = mmap(0, 4ul * 1024ul * 1024ul * 1024ul * 1024ul,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
-1, 0);
当使用 mmap 并设置 MAP_NORESERVE 标志时,并不会要求实际的物理内存和swap空间存在。所以上述代码可以在top中看到使用了 4096g 的 VIRT 虚拟内存,这当然是不可能的,它只是表示使用了 4096GB 的地址空间而已。

4. 为什么会用这么多地址空间

那 Java 程序为什么会使用这么多的地址空间呢?使用“pmap -x”来查看一下:

00007ff638021000   65404       0       0 -----    [ anon ]
00007ff63c000000 132 36 36 rw--- [ anon ]
00007ff63c021000 65404 0 0 ----- [ anon ]
00007ff640000000 132 28 28 rw--- [ anon ]
00007ff640021000 65404 0 0 ----- [ anon ]
00007ff644000000 132 8 8 rw--- [ anon ]
00007ff644021000 65404 0 0 ----- [ anon ]
00007ff648000000 184 184 184 rw--- [ anon ]
00007ff64802e000 65352 0 0 ----- [ anon ]
00007ff64c000000 132 100 100 rw--- [ anon ]
00007ff64c021000 65404 0 0 ----- [ anon ]
00007ff650000000 132 56 56 rw--- [ anon ]
00007ff650021000 65404 0 0 ----- [ anon ]
00007ff654000000 132 16 16 rw--- [ anon ]
00007ff654021000 65404 0 0 ----- [ anon ]
发现有很多奇怪的64MB的内存映射,查资料发现这是 glibc 在版本 2.10 引入的 arena 新功能导致。CentOS 6/7 的 glibc 大都是 2.12/ 2.17 了,所以都会有这个问题。这个功能对每个线程都分配一个分配一个本地arena来加速多线程的执行。
在 glibc 的 arena.c 中使用的 mmap() 调用就和之前的示例代码类似:
    p2 = (char *)mmap(aligned_heap_area, HEAP_MAX_SIZE, PROT_NONE,
MAP_NORESERVE | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)
之后,只有很小的一部分地址被映射到了物理内存中:
    mprotect(p2, size, PROT_READ | PROT_WRITE)
因此在一个多线程程序中,会有相当多的 64MB 的 arena 被分配。这个可以用环境变量 MALLOC_ARENA_MAX 来控制。在64位系统中的默认值为 128。

5. Java 的特殊性

Java 程序由于自己维护堆的使用,导致调用 glibc 去管理内存的次数较少。更糟的是 Java 8 开始使用 metaspace 原空间取代永久代,而元空间是存放在操作系统本地内存中,那线程一多,每个线程都要使用一点元空间,每个线程都分配一个 arena,每个都64MB,就会导致巨大的虚拟地址被分配。

6. 结束语

总结一下:

  • VIRT高是因为分配了太多地址空间导致。
  • 一般来说不用太在意VIRT太高,因为你有16EB的空间可以使用。
  • 如果你实在需要控制VIRT的使用,设置环境变量MALLOC_ARENA_MAX,例如hadoop推荐值为4,因为YARN使用VIRT值监控资源使用。

Java 进程占用 VIRT 虚拟内存超高的问题研究的更多相关文章

  1. Java进程占用CPU资源过多分析

    问题描述: 生产环境下的某台tomcat7服务器,在刚发布时的时候一切都很正常,在运行一段时间后就出现CPU占用很高的问题,基本上是负载一天比一天高. 问题分析: 1,程序属于CPU密集型,和开发沟通 ...

  2. Java进程占用内存过高,排查解决方法

    最近收到邮件报警,说内存使作率达到84%.如下图: 解决方法: A:可能是代码原因导致的问题: 1.使用命令:top 查看当前进程的状态 2.从上图可以看到PID:916的java进程占用内存较大.定 ...

  3. Java进程占用系统内存较高的排查方法

    1.通过top 查看具体是哪个进程占用内存较多 Tasks: 65 total, 1 running, 64 sleeping, 0 stopped, 0 zombie %Cpu(s): 2.0 us ...

  4. linux下查找java进程占用CPU过高原因

    1. 查找进程 top查看进程占用资源情况 明显看出java的两个进程22714,12406占用过高cpu.   2.查找线程 使用top -H -p <pid>查看线程占用情况   3. ...

  5. Java 进程占用内存过多,幕后元凶原来是线程太多

    那天中午吃饭,一个同事说,那个项目组的人快气死我了,程序有问题,早晨在群里@了他们,到中午才回消息,然后竟然还说他们的程序没有问题,是我们这边调用的太频繁了. 简直想笑. 背景说明 我们当前这个系统和 ...

  6. java进程占用系统内存高,排查解决

    转自:http://blog.51cto.com/chengxiaobai/2052530?cid=695076 故障:最近收到生产服务器的报警短信以及邮件,报警内容为:内存使用率高于70%. 使用t ...

  7. java进程占用CPU资源过高分析脚本

    #!/bin/bash #输入占用CPU较高的进程号 pid=$ if [ -z $pid ] then echo "PID is NULL" exit fi #找到该进程中占用较 ...

  8. JAVA进程占用CPU分析

    在一次生产环境中,服务器负载报警,SSH登录上看到CPU占用很高. 1.执行top命令,看到进程号为9737的进程持续占用CPU 2.怀疑是否是进程配置的内存不够了,引发了fullGC导致CPU占用高 ...

  9. jstack 排查 java 进程占用大量 CPU 问题

    1. top 看看哪个进程是罪魁祸首 2.将这个进程的jstack dump 到一个文件里面,以备使用. jstack -l 25886 > /tmp/jstack.log # 如果报错,则加 ...

随机推荐

  1. css3 定义选择器

    引言:CSS样式规则有两个主要部分.选择器决定将格式化应用到哪些元素.声明则定义要应用的格式化. 构造选择器 选择器可以定义五个不同的标准来选择要进行格式化的元素. 元素的类型或者名称.如下所示. h ...

  2. Android开发在路上:少去踩坑,多走捷径

    转自:http://djt.qq.com/article/view/1193   最近一朋友提了几个Android问题让我帮忙写个小分享,我觉得对新人还是挺有帮助的,所以有了这个小分享. 1.目前, ...

  3. 读书笔记_Effective_C++_条款四十五:运用成员函数模板接受所有兼容类型

    比如有一个Base类和一个Derived类,像下面这样: class BaseClass {…}; class DerivedClass : public BaseClass {…}; 因为是父类与子 ...

  4. annotation:@Override出现The method of type must override asuperclass解决方案

    原因追踪及解决办法: 1. 查阅资料发现说在jdk1.5下要使用@Override这个annotation必须保证被标注的方法来源于class而不是interface. 2. 即使自己的jdk是1.6 ...

  5. JS移动端滑屏事件

    来看看在pc上面的几个事件:onmousedown,onmousemove,onmouseup 我相信大家对这几个事件一定不陌生,第一个onmousedown表示鼠标按下,第二个onmousemove ...

  6. Rendering Path

    Rendering Path:渲染路径 设置:1.Player Setting,2.Camera(会覆盖PlayerSetting中的设置) 选择:根据渲染内容和目标平台来选择合适的Rendering ...

  7. Tips5:通过 alt+鼠标左键 来完全展开或收缩层级

    通过 alt+点击 可以完全地展开或关闭unity中的各种层级关系,包括Hierarchy视图 或 Project视图中的. 上图中,第一次点击没有按住ALT键,可以发现子项目并没有被展开,而第二次是 ...

  8. [git]通过commit_id找回文件

    git checkout commit_id 这样会切换到这个commit_id的上,文件的内容就是这个commit保存的内容. git checkout -b new_branch_name com ...

  9. java io系列15之 DataOutputStream(数据输出流)的认知、源码和示例

    本章介绍DataOutputStream.我们先对DataOutputStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解. 转载请注明出处:http://www.cnblog ...

  10. android SQLite(安卓数据库的插入显示删除)

    1.利用android自带数据库实现增加.删除.显示用户等操作 只是一个基本模型,为即将的与 复利计算apk整合做牺牲. 就不上传百度云供大家下载了 等整合了复利计算再上传. 数据的插入和显示:   ...