内存分布

首先,列举一下一个JVM进程主要占用内存的一些地方:

  • Young
  • Old
  • metaspace
  • java thread count * Xss
  • other thread count * stacksize (非Java线程)
  • Direct memory
  • native memory
  • codecache

说明:包括但不限于此。

接下来一步一步验证每个区域占用的内存。并且为了验证这个问题,写了一个工具类,里面有给每个区域分配内存的方法,源码在文末。

  • JVM参数

运行过程中的JVM参数如下:

-verbose:gc -XX:+PrintGCDetails -Xmx2g -Xms2g -Xmn1g

-XX:PretenureSizeThreshold=2M -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

-XX:CMSInitiatingOccupancyFraction=90 -XX:+UseCMSInitiatingOccupancyOnly

-XX:MaxDirectMemorySize=512m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m

Young+Old

我们先从最简单的堆占用内存开始,即Xmx和Xms参数申明,它包括young和old区。分别分配800M和200M内存,main方法如下:

public static void main(String[] args) throws Exception{

youngAllocate(800);

oldAllocate(200);

Thread.sleep(300000);

}

通过TOP命令查看,RES为1G:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND

22481 afei 20 0 4366m 1.0g 11m S 0.5 27.0 0:02.41 java

通过jstat命令也能看到,Old和Eden分别占用200M和800M。

这里再增加一个有趣的测试,young和old区分别分配1000M和1000M内存,main方法如下:

public static void main(String[] args) throws Exception{

youngAllocate(1000);

oldAllocate(1000);

// 为了CMS GC顺利触发,这里需要sleep 5s以上,建议时间长一点,让整个CMS GC顺利完成。

Thread.sleep(300000);

}

这样就会导致发生一次YGC和一个CMS GC,那么你认为这时候通过TOP命令查看RES结果是多少呢?这时候应该是1.8G,除了S0/S1两个区域,eden和Old区域都写入过数据,而JVM使用过的内存就不会归还给操作系统,除非JVM进程宕机或者重启,这个结论很重要:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND

22707 afei 20 0 4366m 1.8g 11m S 0.0 48.7 0:00.90 java

Young+Old+Metaspace

接下来,我们再通过程序在Metaspace中重复加载20w个对象,即metaspace分配200M左右的内存,main方法如下:

public static void main(String[] args) throws Exception{

youngAllocate(1000);

oldAllocate(1000);

metaspaceAllocate(200000);

Thread.sleep(60000);

}

通过TOP命令查看,RES为2.0G:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND

22781 afei 20 0 4472m 2.0g 12m S 0.0 54.7 0:07.51 java

即前面分析的1.8G+208M(213822/1024),在JVM进程退出时有一行这样的日志:

 Metaspace       used 213822K, capacity 215618K, committed 215936K, reserved 1165312K

Young+Old+Metaspace+DirectMemory

接下来,我们再通过程序给堆外分配400M,main方法如下:

public static void main(String[] args) throws Exception{

youngAllocate(1000);

oldAllocate(1000);

metaspaceAllocate(200000);

directMemoryAllocate(400);

Thread.sleep(60000);

}

通过TOP命令查看,RES为2.4G:

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND

23329 afei 20 0 4874m 2.4g 12m S 0.0 65.2 0:12.67 java

Young+Old+Metaspace+DirectMemory+线程栈

最后就是线程栈,笔者试图通过启动20个线程,并且设置-Xss10240k,但是并没有达到预期,这里作为一个遗留问题。等笔者哪天搞懂了,再发文说明。

  • Xss案例

曾经群里有一个朋友就是因为Xss配置相当大导致RES占用13G左右。大概情况是这样,-Xms4g,-Xss40940k,dubbo的provider服务。熟悉dubbo服务同学知道,dubbo服务provider默认采用固定200个线程处理的方式。所以200个线程占用8G,加上4G堆,以及一些其他内存,导致RSS高达13G,恐怖!!!

codecache

这部分内存一般占用比较少,在JVM崩溃的文件hs_err_pid18480.log中有其内存占用情况:

CodeCache: size=245760Kb used=47868Kb max_used=47874Kb free=197891Kb
bounds [0x00007f00b4de4000, 0x00007f00b7d54000, 0x00007f00c3de4000]
total_blobs=12973 nmethods=12383 adapters=500
compilation: enabled

知识总结

HotSpot VM自己在JIT编译器、GC工作等的一些时候都会额外临时分配一些native memory,在JDK类库也有可能会有些功能分配长期存活或者临时的native memory,然后就是各种第三方库的native部分可能分配的native memory。

总之,RES占比异常时,一一排查,不要忽略任何一部分可能消耗的内存。

jvm使用了的内存,即使GC后也不会还给操作系统。

Direct Memory内存查看:如果是JDK 7及以上版本,可以用jconsole或者VisualVM的MBeans窗口查看java.nio.BufferPool.direct属性。

JVM内存占用情况深入分析的更多相关文章

  1. 通过JDK常用工具监控Java进程的内存占用情况

    目录 1 JDK 工具的使用 2 查看 GC 日志信息 3 添加 JMS 远程监控 Tomcat是一款常用的Web容器, 它是运行在 JVM(Java Virtual Machine) 中的一个Jav ...

  2. 如何在eclipse dump Java内存占用情况和打印GC LOG

     当使用java开发应用程序发生内存泄露的时候,经常会需要dump内存,然后使用内存分析工具,比如Eclipse Memory Analyzer(一般称作MAT)工具. 本文将介绍如何在eclipse ...

  3. 监控JVM内存使用情况,剩余空间小于2M时报警

    一个简单的类,用来监控JVM内存使用情况,剩余空间小于2M时报警. import java.lang.management.ManagementFactory; import java.lang.ma ...

  4. Tomcat中查看JVM内存使用情况

    TOMCAT运行时,实时监控当前应用JVM的使用情况:可以利用Tomcat自带的应用manager查看详情. 首先,确认服务目录webapps下有manager应用 其次,需要创建角色manager和 ...

  5. 阿里云下 centos7下启动程序总是被killed ,看内存占用情况以检查哪些服务存在问题并调整参数作调优

    很久不搭理自己的网站了,几天突然发现启动程序总是被killed, 于是查看了系统日志 vi /var/log/messages 发现出现 kernel: Out of memory: Kill pro ...

  6. [PHP] 循环查看php-fpm的内存占用情况

    在webmail的业务中进行发信,如果携带了附件,会把附件拼接内嵌到邮件正文里,这时会极大的占用内存,可以使用以下命令查看fpm的进程内存占用 ps --no-headers --sort -rss ...

  7. 查看LINUX进程内存占用情况

    可以直接使用top命令后,查看%MEM的内容.可以选择按进程查看或者按用户查看,如想查看oracle用户的进程内存使用情况的话可以使用如下的命令: (1)top top命令是Linux下常用的性能分析 ...

  8. 查看LINUX进程内存占用情况(转)

    可以直接使用top命令后,查看%MEM的内容.可以选择按进程查看或者按用户查看,如想查看oracle用户的进程内存使用情况的话可以使用如下的命令: (1)top top命令是Linux下常用的性能分析 ...

  9. JVM内存管理 《深入分析java web 技术内幕》第八章

    8.1 物理内存与虚拟内存 物理内存RAM(随机存储器),寄存单元为寄存器,用于存储计算单元执行指令的中间结果. 连接处理器和RAM或者处理器和寄存器的是地址总线,这个地址的宽度影响了物理地址的索引范 ...

随机推荐

  1. Django WSGI,MVC,MTV,中间件部分,Form初识

    一.什么是WSGI? WEB框架的本质是一个socket服务端接收用户请求,加工数据返回给客户端(Django),但是Django没有自带socket需要使用 别人的 socket配合Django才能 ...

  2. C++面向对象高级编程(六)转换函数与non-explicit one argument ctor

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 1.conversion function 转换函数 //1.转换函数 //conversion function //只要你认为合理 你可以任 ...

  3. iOS实现程序长时间未操作退出

    大部分银行客户端都有这样的需求,在用户一定时间内未操作,即认定为token失效,但未操作是任何判定的呢?我的想法是用户未进行任何touch时间,原理就是监听runloop事件.我们需要进行的操作是创建 ...

  4. Java数据封装类

    项目中用到,比较好用!! 用户前端与后台的交互数据,这个封装类中包含分页.数据.错误码.状态码等信息!! 亲测好用哦! 一个类DataWrapper public class DataWrapper& ...

  5. 重写alert 方法(我胡汉三又回来了)

    window.alert = function (txt) { var shield = document.createElement("DIV"); shield.id = &q ...

  6. Python 之文件上传

    基于form表单提交 # 需要指定form-data,不能直接拼键值对 可以指定name照片存在位置 views.py from django.shortcuts import render,redi ...

  7. 原生js实现div拖拽+按下鼠标计时

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> ...

  8. ios逆向工程-动态分析

    先说说为什么要分析应用吧,如果你想从一个ios应用中获取有用的信息,或者你想修改该应用的一些功能,前提当然是要先知道该app的逻辑和结构了. 动态分享工具比较少,我们先分析个简单的,全民工具Cycri ...

  9. [Aizu2784]Similarity of Subtrees

    vjudge Description 给一棵\(n\)个节点的有根树,定义两棵树同构当且仅当他们每个深度的节点个数相同.问这个树上有多少对子树满足同构.\(n\le100000\). sol 树\(h ...

  10. VScode插件

    Visual Studio Code (简称 VS Code / VSC) 是一款免费开源的现代化轻量级代码编辑器,支持几乎所有主流的开发语言的语法高亮.智能代码补全.自定义热键.括号匹配.代码片段. ...