一 前言

在生产环境,Java应用程序设置了最大JVM内存后,经常发现实际使用的内存,可能超过设置的JVM最大内存数jmap -heap pid 通过这个命令可以方便查看java的内存分配情况。一般情况下,是因为系统使用了堆外内存,比如使用了netty框架,里面就使用了堆外内存,堆外内存减少了java的heap和系统直接内存的复制,所以在网络应用中作为连接的缓存使用比较合适。堆外内存默认使用的最大大小是JVM虚拟机的最大内存大小,当然可以通过-XX:MaxDirectMemorySize来限定最大大小,有些情况有效,有些情况无效,无效的情况下,此篇文章是针对无效的一种可能情况分析。

二 堆外内存使用查看

由于堆外内存是调用系统默认的内存分配器分配内存的,所以查看内存不能仅从java上面看,最好还要看下各类内存占用情况分类,可以用pmap 命令或者直接cat /proc/pid/maps 来查看内存来的更直接:

[root@izbp14xswj2tx6qgnz9dllz ~]# pmap 2888|grep anon
0000000000ed3000    132K rw---   [ anon ]
00007efea4461000     20K rw---   [ anon ]
00007efea4c70000     84K rw---   [ anon ]
00007efea4e97000     20K rw---   [ anon ]
00007efea4ea4000      8K rw---   [ anon ]
00007efea4ea8000      4K rw---   [ anon ]
00007fff795e9000      8K r-x--   [ anon ]
ffffffffff600000      4K r-x--   [ anon ]

来查看内存分配,匿名内存多数是不是文件或动态库映射的内存,申请的heap内存就在这里面。为了方便说明,我写个java的测试程序,代码如下:

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;public class test   {private static int i;private static CountDownLatch cd = new CountDownLatch(10);public static void main(String[] args) throws InterruptedException {int i = 0;CountDownLatch cd = new CountDownLatch(10);List<Thread> lst = new ArrayList<>();for (int j = 0; j < 10; j++) {lst.add(new Thread() {@Overridepublic void run() {System.out.println("Thread" + Thread.currentThread().getName()+"Start.");ByteBuffer buffer = ByteBuffer.allocateDirect(1);try {Thread.sleep(500000);cd.countDown();System.out.println("Thread" + Thread.currentThread().getName()+"Over.");} catch (InterruptedException e) {e.printStackTrace();}}});}for (Thread thread : lst) {thread.start();}cd.await();System.out.println("Running is over!");}
}

如果直接简单编译运行:

javac test.java
java test

这时候直接用top看占用内存大概为19MB左右,但是用pmap去查看的时候觉得有些异常,显示大概如下:

[root@localhost ~]# pmap 10377
10377:   java -Xmx1g -XX:NativeMemoryTracking=summary test
0000000000400000      4K r-x-- java
0000000000600000      4K rw--- java
0000000001d46000    132K rw---   [ anon ]
00000000c0000000 339968K rw---   [ anon ]
00000000d4c00000 359424K -----   [ anon ]
00000000eab00000 169984K rw---   [ anon ]
00000000f5100000 179200K -----   [ anon ]
...
...
ffffffffff600000      4K r-x--   [ anon ]total          4220956K

占用内存4220956K,明显比我们内存中申请的要多的多,算起来有4GB,虽然没有真正使用,但是占这么多内存,也有点奇怪,来分析下内存:

[root@localhost ~]# pmap 10377|grep anon|grep 65404K
00007fe270021000  65404K -----   [ anon ]
00007fe274021000  65404K -----   [ anon ]
00007fe278021000  65404K -----   [ anon ]
...

统计下:

[root@localhost ~]# pmap 10377|grep anon|grep 65404K|wc -l
22

这些匿名的内存65404K 即是神秘的64MB内存,这些内存来自哪里那?原来是java堆外内存,调用系统的默认的c的内存分配器来分配内存,glibc在 2.11版本以后,其内存分配器ptmalloc2为每个线程都分配了一个thread arena,以前只有一个main thread arena。

一个32位的应用程序进程,最大可创建 2 CPU总核数个arena内存池(MALLOC_ARENA_MAX),每个arena内存池大小为1MB。
一个64位的应用程序进程,最大可创建 8 CPU总核数个arena内存池(MALLOC_ARENA_MAX),每个arena内存池大小为64MB。

三 如何解决

3.1 更改配置参数

我们可以限制arena内存池的总个数,通过设置环境变量来更改:

export MALLOC_ARENA_MAX=4

3.2 用TCMalloc内存分配器来替换ptmalloc2

TCMalloc 库全称Thread-Caching Malloc 是谷歌开源工具google-perftools中的成员,是一个内存分配器,这个内存分配器与我们上面说的linux系统下默认的glibc中的ptmalloc2内存分配器有什么不同那。主要优点:

  • tcmalloc 一次malloc或free操作更快,据说ptmalloc2需要300ns,tcmalloc 需要50ns。

  • tcmalloc 优化小对象的存储,需要更少的空间。

  • tcmalloc 对多线程分配内存,小对象基本不存在锁竞争,因为分配器会给现场分配本地缓存,长期空闲的情况下也不会被回收。当然也不能无脑的都采用这种方式,因为TCMalloc 虽然适合多线程情况下的小内存分配,但是如果大内存分配,大于256KB的内存分配,性能并没有ptmalloc2好。

四 TCMalloc 安装和使用

4.1 基本前置工具安装:

yum -y install gcc make
yum -y install gcc gcc-c++
yum -y perl

4.2 libunwind 安装

这个为什么安装我不是太明白,在64位系统下必须安装,据说gperftools使用glibc内置的stack-unwinder可能会引发死锁。

wget http://download.savannah.gnu.org/releases/libunwind/libunwind-1.1.tar.gz
tar xvf libunwind-1.1.tar.gz
cd libunwind-1.1
./configure && make && make install

4.3 安装perftools

 wget http://code.google.com/p/google-perftools/gperftools-2.7.tar.gztar zxvf google-perftools-2.7.tar.gz
./configure
make
make install

4.4 java使用

export LD_PRELOAD="/usr/local/lib/libtcmalloc.so"; java -Xmx1g  -XX:NativeMemoryTracking=summary  test

再次通过pmap去查看内存,虚拟内存占用会少很多,也看出里面已经使用了tcmalloc

[root@localhost soft]# pmap -p 17484
17484:   java -Xmx1g -XX:NativeMemoryTracking=summary test
0000000000400000      4K r-x-- /usr/java/jdk1.8.0_77/bin/java
0000000000600000      4K rw--- /usr/java/jdk1.8.0_77/bin/java
...
00007fd904341000    280K r-x-- /usr/local/lib/libtcmalloc.so.4.5.3
00007fd904387000   2044K ----- /usr/local/lib/libtcmalloc.so.4.5.3
00007fd904586000      4K r---- /usr/local/lib/libtcmalloc.so.4.5.3
00007fd904587000      4K rw--- /usr/local/lib/libtcmalloc.so.4.5.3
...
00007ffdebdcc000      8K r-x--   [ anon ]
ffffffffff600000      4K r-x--   [ anon ]total          2612376K

长期使用,可以用:

export LD_PRELOAD=/usr/local/lib/libtcmalloc.so

如果需要调试可以通过下面文件生成profile文件:

mkdir  /home/logs/gperftools/tcmalloc/heap
chmod 0777 /home/logs/gperftools/tcmalloc/heap
export HEAPPROFILE=/home/logs/gperftools/tcmalloc/heap

分析这些文件通过:

pprof --pdf  java xxx.heap > xxx.pdf

4.5 C 程序使用

编译时候连接下:

g++ main.cpp -o main -ltcmalloc -g -O0

五 诗词欣赏

八归·秋江带雨
[宋] [史达祖]秋江带雨,寒沙萦水,人瞰画阁愁独。
烟蓑散响惊诗思,还被乱鸥飞去,秀句难续。
冷眼尽归图画上,认隔岸、微茫云屋。
想半属、渔市樵村,欲暮竞然竹。
须信风流未老,凭持酒、慰此凄凉心目。
一鞭南陌,几篙官渡,赖有歌眉舒绿。
只匆匆眺远,早觉闲愁挂乔木。
应难奈,故人天际,望彻淮山,相思无雁足。

Java内存中神奇的64MB相关推荐

  1. 占内存少的java开发工具_Java所占内存中神奇的64MB

    一 前言 在生产环境,Java应用程序设置了最大JVM内存后,经常发现实际使用的内存,可能超过设置的JVM最大内存数jmap -heap pid 通过这个命令可以方便查看java的内存分配情况.一般情 ...

  2. java内存中的栈、方法区 、堆

    栈(stack):主要保存基本类型(或者叫内置类型)(char.byte.short.int.long.double.float.boolean)和对象的引用,数据可以共享,速度仅次于寄存器(regi ...

  3. java内存中读文件_关于内存管理:读取Java中的大文件

    我需要一个非常了解Java和内存问题的人的建议. 我有一个大文件(大约1.5GB),我需要将此文件切成许多小文件(例如100个小文件). 我通常知道如何做到这一点(使用BufferedReader), ...

  4. 痴情研究java内存中的对象

    前记: 几天前,在浏览网页时偶然的发现一道以前就看过很多遍的面试题,题目是:"请说出'equals'和'=='的区别",当时我觉得我还是挺懂的,在心里答了一点(比如我们都知道的:' ...

  5. Java内存中的堆和栈

    今天复习基础内容,看到static修饰的成员在静态区,不太知道静态区和堆栈的区分,看到一篇博客写的很详细,转载于此. https://www.cnblogs.com/langtianya/p/4441 ...

  6. java 内存堆和栈_java中堆内存与栈内存的知识点总结

    一.概述 在Java中,内存分为两种,一种是栈内存,另一种就是堆内存. 二.堆内存 1.什么是堆内存? 堆内存是Java内存中的一种,它的作用是用于存储Java中的对象和数组,当我们new一个对象或者 ...

  7. java 内存屏障类型_Java内存模型精讲

    1.JAVA 的并发模型 共享内存模型 在共享内存的并发模型里面,线程之间共享程序的公共状态,线程之间通过读写内存中公共状态来进行隐式通信 该内存指的是主内存,实际上是物理内存的一小部分 2.JAVA ...

  8. 【JAVA编码专题】 JAVA字符编码系列三:Java应用中的编码问题

    这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记录下来以便日后参考. 为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问 ...

  9. unix系统编码 java_JAVA字符编码系列三:Java应用中的编码问题

    这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记录下来以便日后参考. 为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问 ...

最新文章

  1. GridView的DataKeyNames属性 转载的
  2. Delphi实现类似Android锁屏的密码锁控件
  3. 碎片化趋势下手机浏览器或成赢家
  4. C++11 标准新特性:Defaulted 和 Deleted 函数
  5. 论文浅尝 | 低资源文本风格迁移数据集
  6. es6 Generator.prototype.throw()方法
  7. 简单的用堆栈实现的表达式计算
  8. Windows Server 2003 SP2 中文版下载
  9. 邮件定时发送微博热搜
  10. 2022-2027年中国非人寿保险市场竞争态势及行业投资前景预测报告
  11. 北科大小学期计算机实践报告,北京科技大学小学期C实践报告.pdf
  12. 【机器学习】吴恩达:机器学习的六个核心算法
  13. ​从机械工程师到机器学习工程师,我也是个数据科学家了
  14. 苏宁11.11:搜索引擎Solr在苏宁易购商品评价系统中的应用
  15. 本人GitHub网址https://github.com/LH34128
  16. matlab的一点内容
  17. html在线添加页码,Wkhtmltopdf添加页码
  18. m.444lu.co show.php,vml圆角矩形最简布局_javascript技巧
  19. MEMS加速度计性能测评
  20. 图像相关——DPI、PPI、像素、屏幕密度等关系与解释

热门文章

  1. linux安装系统如何查看硬盘,如何检查Linux系统服务器的硬盘是SSD还是HDD?
  2. 查看mysql是否区分大小写
  3. 计算机一级插入页码,计算机一级WPS辅导:用WPSOffice2007插入特色页码
  4. 解决跨域(六)--- document.domian
  5. 《这个历史挺靠谱·上·袁腾飞讲中国史》读后感
  6. 刚刚!鸿蒙OS 2升级用户破千万!(1000+企业共建鸿蒙生态)
  7. java Double去掉科学计数E
  8. oppo实现appium 自动化测试
  9. Origin使用自定义函数拟合曲线函数
  10. vue中trigger用法