一、堆外内存组成

通常JVM的参数我们会配置

-Xms 堆初始内存
-Xmx 堆最大内存
-XX:+UseG1GC/CMS 垃圾回收器
-XX:+DisableExplicitGC 禁止显示GC
-XX:MaxDirectMemorySize 设置最大堆外内存,默认是-xmx-survivor,也就是基本上和-xmx大小相等
-Xss:每个线程的堆栈大小,默认1M
-Xmn: 年轻代大小(eden区+2 survivor)
-XX:newRatio: 4 年轻代与老年代1:4
-XX:survivorRatio: 8Eden区与survivor大小比值

java整个进程占用的内存:
- 堆内存
- metaspace(堆内) JDK8使用metaspace来替代了permsize:永久代大小
- 堆外内存使用
- 线程栈空间

堆外内存回收: 堆外内存的回收是通过system.gc()来的,依赖于目前的gc机制。
通常是通过DirectByteBuffer对象来分配堆外内存,gc的时候就是判断这个对象是否被引用,来决定是否回收。

二、堆外内存参数配置

-XX:InitialCodeCacheSize=64M \
-XX:CodeCacheExpansionSize=1M \
-XX:CodeCacheMinimumFreeSpace=1M \
-XX:ReservedCodeCacheSize=200M \
-XX:MinMetaspaceExpansion=1M \
-XX:MaxMetaspaceExpansion=8M \
-XX:MaxDirectMemorySize=96M \
-XX:CompressedClassSpaceSize=256M \

三、问题排查

3.1、首先确认堆占用

1、用jmap,jmap 查看heap内存使用情况

  1. jmap -heap pid

可以查看到MetaspaceSize,CompressedClassSpaceSize,MaxMetaSize
jmap和jdk版本有关系,有些jdk版本会查看不到内存信息,可以使用jstat来查看统计信息

2、jstat 收集统计信息

  1. jstat -gc pid 1000
  2. S0C/S0U S1C/S1U EC/EU CCSC/CCSU YGC/YGCT FGC/FCGT GCT
    survivor0容量和使用 survivor1容量和使用 Eden jdk8meta,以前应该是PC,PC young gc次数和耗时 full gc次数和耗时 total gc时间

如果能排除掉heap的问题,就要分析堆外内存情况了。

3.2、分析堆外情况

NMT(native memory tracking)
使用
在JVM参数中添加 -XX:NativeMemoryTracking=[off | summary | detail]

  1. -XX:NativeMemoryTracking=detail

在JVM运行过程中,使用jcmd获取相关信息
jcmd pid VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown] [scale= KB | MB | GB]

  1. jcmd pid VM.native_memory detail

baseline个基准,之后会输出diff参数,来和这个基线版本进行比较,可以两次的内存差

NMT报告会显示内存使用情况

类别                  含义
Java Heap        堆大小
Thread              线程
Thread Stack    线程栈

NMT可以得到线程栈大小,排除栈空间影响。

pmap 查看进程内存地址空间

  1. pmap -x pid | sort xx

可以结合pmap,和nmt得到内存地址空间。和堆外占用情况了。

接下来需要做的就是分析堆外内存的内容了。

gdb dump查看内存空间内容

gdb dump查看内存空间内容

  1. (gdb) dump binary memory ./file BEGIN_ADDRESS END_ADDRESS

将内存内容dump到文件中,就可以查看到文件中的内容了。
但是这种方式不直观,所以可以使用其他工具

gperf
google的,使用gperf2.5即可,网上很多安装都说一定要安装libunwind,其实都是瞎抄抄,老版本确实需要,2.5的版本不需要了。

https://blog.csdn.net/unix21/article/details/79161250
另外一个注意点就是虽然heap文件只有1M,但是可以分析出堆外内存的大小。
不过我在实际使用过程中,gperf并没有分析出实际的堆外内存情况,通过pmap可以看出堆外内存占用有几个G,但是gperf始终只有200M

Jemalloc
https://github.com/jemalloc/jemalloc/releases
安装

./configurate –enable-prof
make
sudo make install
配置

export LD_PRELOAD=/usr/local/lib/libjemalloc.so
export MALLOC_CONF=prof:true,lg_prof_interval:31,lg_prof_sample:17,prof_prefix:/output/jeprof
https://github.com/jemalloc/jemalloc/wiki/Getting-Started

环境:基于B\S的点子考试系统,为了发现客户端能实时地从服务端接收考试数据,系统使用了逆向AJAX技术(也称Comet或Server Side Push),选用CometD1.1.1作为服务端推送框架,服务器是Jetty7.1.4,硬件为一台普通PC机,Core i5 CPU,

4G内存,运行32位Windows操作系统。

说明:测试期间发现服务端不定时抛出内存溢出异常,服务器不一定每次都会出现异常,但是假如正式考试时奔溃一次,那估计整场考试都会全乱套,网站管理员尝试过把堆开到最大,32位系统最多到1.6GB基本无法再加大了,而且开大量也基本没效果,抛出

内存溢出异常好像更加繁琐了。加入-XX:+HeapDumpOnOutOfMemoryError,居然也没有任何反应,抛出内存溢出异常时什么文件都没产生。无奈之下只好挂着jstat使劲盯屏幕,发现GC并不频繁,Eden区,Survivor区,老年代及拥挤代内存全部

表示"情绪稳定,压力不大",但是照样不停的抛出内存溢出异常,管理员鸭梨很大。最后,在内存溢出后从系统日志中找到异常堆栈。

分析:大家都知道操作系统对每个进程能管理的内存是有限的,这台服务器使用的32位Windows平台的限制是2GB,其中给了Java堆1.6GB,而Direct Memory 并不算在1.6GB的堆之内,因此它只能在剩余的0.4GB空间分出一部分。在此应用中导致内

存溢出的关键是:垃圾收集进行时,虚拟机虽然会对Direct Memory进行回收,但是Direct Memory 却不能像新生代和老年代那样,发现空间不足了就通知收集器进行垃圾回收,他只能等到抛出内存溢出异常时,先catch掉,再在catch块里面“大喊”

“System.gc”.要是虚拟机还是不听(如:打开了-XX:+DisableExplicitGC开关),那就只能眼睁睁地看着堆中还有许多空闲内存,自己却不得不抛出内存异常了。而本案例中使用的Comet1.1.1框架,正好有大量的NIO操作需要用到Direct Memory。

总结:从实践经验来看,除了java堆和永久代之外,我们注意到下面这些区域也会占用较多的内存,这里所有的内存总和会受到操作系统进程最大内存的限制:

1.Direct Memory:可以通过-XX:MaxDirectMemorySize调整大小,内存不足时抛出OutOfMemoryError或OutOfMemoryError:Direct buffer memory。

2.线程堆栈:可通过-Xss调整大小内存不足时抛出StackoverflowErroe(纵向无法分配,即无法分配新的栈帧)或OutOfMemoryError:unable to create new native thread(横向无法分配,即无法建立新的线程)。

3.Socket缓存区:每个Socket连接都Receive和Send两个缓存区,分别占大约37KB和25KB的内存,连接多的话这块内存占用也比较可观。如果无法分配,则可能会抛出IOException:Too many open files异常。

4.JNI代码:如果代码中使用JNI调用本地库,那么本地库使用内存也不在堆中

5.虚拟机和GC:虚拟机和GC的代码执行也要消耗一定的内存。

Java堆外内存之六:堆外内存溢出问题排查的更多相关文章

  1. 深入了解java虚拟机(JVM) 第三章 内存区域----堆空间

    一.堆的含义 jvm堆的区域主要是用来存放对象的实例,它的空间大小是JVM内存区域中占比重最大的,也是jvm最大的内存管理模块,最重要的是,这个区域是垃圾收集器主要管理的区域,这意味着我们在考虑垃圾回 ...

  2. java中栈内存与堆内存(JVM内存模型)

    java中栈内存与堆内存(JVM内存模型) Java中堆内存和栈内存详解1 和 Java中堆内存和栈内存详解2 都粗略讲解了栈内存和堆内存的区别,以及代码中哪些变量存储在堆中.哪些存储在栈中.内存中的 ...

  3. 求你了,别再说Java对象都是在堆内存上分配空间的了!

    Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解.可以说,关于JVM的相关知识,基本是每个Java开发者 ...

  4. 别再说Java对象都是在堆内存上分配空间的了!

    Java作为一种面向对象的,跨平台语言,其对象.内存等一直是比较难的知识点,所以,即使是一个Java的初学者,也一定或多或少的对JVM有一些了解.可以说,关于JVM的相关知识,基本是每个Java开发者 ...

  5. JVM知识(一) 求你了,别再说Java对象都是在堆内存上分配空间的了!

    求你了,别再说Java对象都是在堆内存上分配空间的了! https://baijiahao.baidu.com/s?id=1661296872935371634&wfr=spider& ...

  6. Java直接内存与堆内存

    NIO的Buffer提供了一个可以不经过JVM内存直接访问系统物理内存的类——DirectBuffer. DirectBuffer类继承自ByteBuffer,但和普通的ByteBuffer不同,普通 ...

  7. 【转载】java项目中经常碰到的内存溢出问题: java.lang.OutOfMemoryError: PermGen space, 堆内存和非堆内存,写的很好,理解很方便

    Tomcat Xms Xmx PermSize MaxPermSize 区别 及 java.lang.OutOfMemoryError: PermGen space 解决 解决方案 在 catalin ...

  8. JAVA面试题:String 堆内存和栈内存

    java把内存划分为两种:一种是栈(stack)内存,一种是堆(heap)内存 在函数中定义的一些基本类型的变量和对象的引用变量都在栈内存中分配,当在一段代码块定义一个变量时,java就在栈中为这个变 ...

  9. Java虚拟机内存区域堆(heap)的管理

    在上一节中Java 出现内存溢出的定位以及解决方案 中对于Java虚拟机栈以及方法区的内存出现的异常以及处理方式进行了解析,由于Java虚拟机对于堆的管理十分复杂,并且Java虚拟机中最基本的内存区域 ...

随机推荐

  1. web测试小结

    今年5月份开始接触web测试,经过大半年的测试及学习,简单总结下 测试过程: 1.需求理解 2.测试策略.方案.用例编写及评审 3.测试环境搭建 4.测试执行 5.bug提单.问题跟踪 6.回归测试 ...

  2. SharePoint 2010: Change welcome page on PowerShell

    摘要: SharePoint 2010之后呢, 建立一个 Team Site会有两个 default page, 分别是 Sitepages/home.aspx and default.aspx. 这 ...

  3. pandas 操作 excel

    1. 多重 sheet Using Pandas to pd.read_excel() for multiple worksheets of the same workbook pd.read_exc ...

  4. Python CAN

    /********************************************************************************* * Python CAN * 说明 ...

  5. Texas Instruments matrix-gui-2.0 hacking -- submenu.php

    <?php /* * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * * * Redistrib ...

  6. stm32 继电器的配置

    你可以把继电器当成一个led来控制,只不过就是电路图不一样,但配置原理是一样的, 控制相对应的GPIO口,推挽输出,就行了,然后仿真一下就会发现哒哒哒的声音.

  7. pymysql模块操作数据库

    pymysql模块是python操作数据库的一个模块   connect()创建数据库链接,参数是连接数据库需要的连接参数 使用方式: 模块名称.connect() 参数: host=数据库ip po ...

  8. BZOJ1369/BZOJ2865 【后缀数组+线段树】

    Description XX在进行字符串研究的时候,遇到了一个十分棘手的问题. 在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件: ...

  9. js获取 gps坐标

    if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(pos) { model.address. ...

  10. POJ 2407:Relatives(欧拉函数模板)

    Relatives AC代码 Relatives Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 16186   Accept ...