JVM 堆外内存查看方法
JVM 堆外内存查看方法
JVM 堆外内存查看方法
1.概述
是否曾经想过为什么Java应用程序通过众所周知的*-Xms和-Xmx调整标志消耗的内存比指定的数量大得多 ?由于各种原因和可能的优化,JVM可能会分配额外的本机内存。这些额外的分配最终可能使消耗的内存超出-Xmx* 限制。
在本教程中,我们将枚举JVM中本机内存分配的一些常见来源,以及它们的大小调整标志,然后学习如何使用本机内存跟踪来监视它们。
2.本机分配
通常,堆是Java应用程序中最大的内存消耗者,但是还有其他一些。**除了堆之外,JVM从本地内存中分配了相当大的块来维护其类元数据,应用程序代码,由JIT生成的代码,内部数据结构等。**在以下各节中,我们将探讨其中的一些分配。
可以看到整个memory主要包含了Java Heap、Class、Thread、Code、GC、Compiler、Internal、Other、Symbol、Native Memory Tracking、Arena Chunk这几部分;其中reserved表示应用可用的内存大小,committed表示应用正在使用的内存大小
Java Heap部分表示heap内存目前占用了463MB;
Class部分表示已经加载的classes个数为8801,其metadata占用了50MB;
Thread部分表示目前有225个线程,占用了27MB;
Code部分表示JIT生成的或者缓存的instructions占用了17MB;
GC部分表示目前已经占用了15MB的内存空间用于帮助GC;
Compiler部分表示compiler生成code的时候占用了26MB;
Internal部分表示命令行解析、JVMTI等占用了5MB;
Other部分表示尚未归类的占用了2MB;
Symbol部分表示诸如string table及constant pool等symbol占用了10MB;
Native Memory Tracking部分表示该功能自身占用了5MB;
Arena Chunk部分表示arena chunk占用了63MB
一个arena表示使用malloc分配的一个memory chunk,这些chunks可以被其他subsystems做为临时内存使用,比如pre-thread的内存分配,它的内存释放是成bulk的
2.1 元空间 Class
为了维护有关已加载类的某些元数据,JVM使用了称为*Metaspace的专用非堆区域。在Java 8之前,等效项称为PermGen或Permanent Generation*。Metaspace或PermGen包含有关已加载类的元数据,而不是包含在堆中的有关它们的实例的元数据。
这里重要的是,由于元空间是堆外数据区域,因此堆大小调整配置不会影响元空间的大小。为了限制元空间的大小,我们使用其他调整标志:
- -XX:MetaspaceSize和-XX:MaxMetaspaceSize设置最小和最大元空间大小
- 在Java 8之前,使用*-XX:PermSize和-XX:MaxPermSize*来设置最小和最大PermGen大小
2.2 线程数 Thread
JVM中最消耗内存的数据区域之一是堆栈,它与每个线程同时创建。堆栈存储局部变量和部分结果,在方法调用中起着重要作用。
默认的线程堆栈大小取决于平台,但是在大多数现代的64位操作系统中,大约为1 MB。此大小可通过*-Xss *调整标志进行配置。
与其他数据区域相比,当对线程数没有限制时,分配给堆栈的总内存实际上是不受限制的。 还值得一提的是,JVM本身需要一些线程来执行其内部操作,例如GC或即时编译。
2.3 代码缓存 Code
为了在不同平台上运行JVM字节码,需要将其转换为机器指令。在执行程序时,JIT编译器负责此编译。
JVM将字节码编译为汇编指令时,会将这些指令存储在称为*代码缓存***的特殊非堆数据区域中 。 可以像JVM中的其他数据区域一样管理代码缓存。-XX:InitialCodeCacheSize **和 **-XX:ReservedCodeCacheSize **调谐标志确定用于代码高速缓存中的初始和最大可能大小。
2.4 垃圾收集 GC
JVM附带了几种GC算法,每种算法都适合不同的用例。所有这些GC算法都有一个共同的特征:它们需要使用一些堆外数据结构来执行任务。这些内部数据结构消耗更多的本机内存。
2.5 Symbols
让我们从字符串开始 , 它是应用程序和库代码中最常用的数据类型之一。由于它们无处不在,因此它们通常占据堆的很大一部分。如果大量的这些字符串包含相同的内容,那么堆的很大一部分将被浪费。
为了节省一些堆空间,我们可以存储每个String的一个版本, 并让其他版本引用存储的版本。 此过程称为字符串实习。由于JVM只能内生 编译时间字符串常量,因此 我们可以对要内生的字符串手动调用intern() 方法。
JVM将内联的字符串存储在特殊的本机固定大小的哈希表中,该哈希表称为String Table,也称为String Pool。我们可以通过**-XX:StringTableSize** 调整标志来配置表的大小(即桶数) 。
除了字符串表外,还有另一个本机数据区域,称为运行时常量池。 JVM使用此池存储必须在运行时解析的常量,例如编译时数字文字,方法和字段引用。
2.6 本机字节缓冲区 Native Byte Buffers
JVM通常是大量本机分配的可疑对象,但有时开发人员也可以直接分配本机内存。最常见的方法是通过JNI和NIO的直接ByteBuffers进行**malloc **调用 。
2.7 Additional Tuning Flags
在本节中,我们针对不同的优化方案使用了少数JVM调整标志。使用以下技巧,我们几乎可以找到与特定概念相关的所有调整标志:
$ java -XX:+PrintFlagsFinal -version | grep <concept>
该PrintFlagsFinal打印所有- *XX *在JVM选项。例如,要查找所有与Metaspace相关的标志:
$ java -XX:+PrintFlagsFinal -version | grep Metaspace // truncated uintx MaxMetaspaceSize = 18446744073709547520 {product} uintx MetaspaceSize = 21807104 {pd product} // truncated
3.本机内存跟踪(NMT)
既然我们知道了JVM中本机内存分配的常见来源,那么该是时候找出如何监视它们了。**首先,我们应该使用另一个JVM调整标志启用本地内存跟踪:*-XX:NativeMemoryTracking = off | sumary | detail。 ***默认情况下,NMT处于关闭状态,但我们可以使它查看其观测结果的摘要或详细视图。
假设我们要跟踪典型的Spring Boot应用程序的本机分配:
$ java -XX:NativeMemoryTracking=summary -Xms300m -Xmx300m -XX:+UseG1GC -jar app.jar
在这里,我们使用G1作为GC算法,在分配300 MB堆空间的同时启用NMT。
3.1 即时快照
启用NMT后,我们可以随时使用*jcmd *命令获取本机内存信息 :
$ jcmd <pid> VM.native_memory
为了找到JVM应用程序的PID,我们可以使用 jps命令:
$ jps -l 7858 app.jar // This is our app7899 sun.tools.jps.Jps
现在,如果我们将 jcmd *与适当的*pid一起使用, *VM.native_memory *将使JVM打印出有关本机分配的信息:
$ jcmd 7858 VM.native_memory
让我们逐节分析NMT输出。
3.2。总分配
NMT报告保留和提交的内存总量,如下所示:
Native Memory Tracking:Total: reserved=1731124KB, committed=448152KB
保留的内存代表我们的应用程序可能使用的内存总量。相反,已提交的内存等于我们的应用程序当前正在使用的内存量。
尽管分配了300 MB的堆,但我们的应用程序的总保留内存几乎为1.7 GB,远不止于此。同样,已提交的内存大约为440 MB,这又远远超过了300 MB。
在合计部分之后,NMT报告每个分配源的内存分配。因此,让我们深入探讨每个来源。
3.3 堆
NMT按预期报告了我们的堆分配:
Java Heap (reserved=307200KB, committed=307200KB) (mmap: reserved=307200KB, committed=307200KB)
300 MB的保留和提交内存,与我们的堆大小设置匹配。
3.4 元空间
NMT关于已加载类的类元数据的说明如下:
Class (reserved=1091407KB, committed=45815KB) (classes #6566) (malloc=10063KB #8519) (mmap: reserved=1081344KB, committed=35752KB)
保留了将近1 GB的空间,并有45 MB的空间用于加载6566类。
3.5 线程
这是关于线程分配的NMT报告:
Thread (reserved=37018KB, committed=37018KB) (thread #37) (stack: reserved=36864KB, committed=36864KB) (malloc=112KB #190) (arena=42KB #72)
总共为37个线程的堆栈分配了36 MB的内存–每个堆栈几乎1 MB。JVM在创建时将内存分配给线程,因此保留和提交的分配是相等的。
3.6 代码缓存
让我们看看NMT对JIT生成和缓存的汇编指令的评价:
Code (reserved=251549KB, committed=14169KB) (malloc=1949KB #3424) (mmap: reserved=249600KB, committed=12220KB)
当前,将近有13 MB的代码被缓存,并且该数量可能会增加到大约245 MB。
3.7 GC
这是有关G1 GC内存使用情况的NMT报告:
GC (reserved=61771KB, committed=61771KB) (malloc=17603KB #4501) (mmap: reserved=44168KB, committed=44168KB)
我们可以看到,几乎有60 MB的空间被保留并致力于帮助G1。
让我们看看一个简单得多的GC(例如串行GC)的内存使用情况:
$ java -XX:NativeMemoryTracking=summary -Xms300m -Xmx300m -XX:+UseSerialGC -jar app.jar
串行GC几乎不使用1 MB:
GC (reserved=1034KB, committed=1034KB) (malloc=26KB #158) (mmap: reserved=1008KB, committed=1008KB)
显然,我们不应该仅仅因为内存使用率就选择了GC算法,因为串行GC的“停滞不前”性质可能会导致性能下降。但是,有几个GC可供选择,它们的内存和性能平衡各不相同。
3.8 符号Symbol
这是有关符号分配的NMT报告,例如字符串表和常量池:
Symbol (reserved=10148KB, committed=10148KB) (malloc=7295KB #66194) (arena=2853KB #1)
将近10 MB分配给符号。
3.9 随着时间的NMT
**NMT使我们能够跟踪内存分配如何随时间变化。**首先,我们应将应用程序的当前状态标记为基线:
$ jcmd <pid> VM.native_memory baseline
然后,过一会儿,我们可以将当前内存使用量与该基准进行比较:
$ jcmd <pid> VM.native_memory summary.diff
NMT使用+和–符号将告诉我们在此期间内存使用量如何变化:
Total: reserved=1771487KB +3373KB, committed=491491KB +6873KB- Java Heap (reserved=307200KB, committed=307200KB) (mmap: reserved=307200KB, committed=307200KB) - Class (reserved=1084300KB +2103KB, committed=39356KB +2871KB)// Truncated
保留和提交的总内存分别增加了3 MB和6 MB。可以很容易地发现内存分配中的其他波动。
3.10 详细的NMT
NMT可以提供有关整个内存空间映射的非常详细的信息。要启用此详细报告,我们应该使用*-XX:NativeMemoryTracking = detail *调整标志。
4 结论
在本文中,我们列举了JVM中本机内存分配的不同贡献者。然后,我们学习了如何检查正在运行的应用程序以监视其本机分配。借助这些见解,我们可以更有效地调整应用程序并调整运行时环境的大小。
JVM 堆外内存查看方法的更多相关文章
- 解Bug之路-记一次JVM堆外内存泄露Bug的查找
解Bug之路-记一次JVM堆外内存泄露Bug的查找 前言 JVM的堆外内存泄露的定位一直是个比较棘手的问题.此次的Bug查找从堆内内存的泄露反推出堆外内存,同时对物理内存的使用做了定量的分析,从而实锤 ...
- 一次完整的JVM堆外内存泄漏故障排查记录
前言 记录一次线上JVM堆外内存泄漏问题的排查过程与思路,其中夹带一些JVM内存分配机制以及常用的JVM问题排查指令和工具分享,希望对大家有所帮助. 在整个排查过程中,我也走了不少弯路,但是在文章中我 ...
- JVM堆外内存随笔
一 JVM堆外内存 1)java与io(file,socket)的操作都需要堆外内存与jvm内存进行互相拷贝,因为操作系统是不懂jvm的内存结构的(jvm的内存结构是自管理的),所以堆外内存存放的是操 ...
- 问题排查-JVM堆外内存问题排查
首先确认堆占用 jmap 查看heap内存使用情况 jmap -heap pid 1 可以查看到MetaspaceSize,CompressedClassSpaceSize,MaxMetaSize j ...
- JVM - 堆外内存
看了不少资料,总结下: 堆外内存 / 直接内存(Direct Memory)JDK1.4中引入的NIO类,基于channel和Buffer的I/O方式,可用Native库直接分配堆外内存,然后利用一个 ...
- JVM初探- 使用堆外内存减少Full GC
JVM初探-使用堆外内存减少Full GC 标签 : JVM 问题: 大部分主流互联网企业线上Server JVM选用了CMS收集器(如Taobao.LinkedIn.Vdian), 虽然CMS可与用 ...
- Java堆外内存之突破JVM枷锁
对于有Java开发经验的朋友都知道,Java中不需要手动的申请和释放内存,JVM会自动进行垃圾回收:而使用的内存是由JVM控制的. 那么,什么时机会进行垃圾回收,如何避免过度频繁的垃圾回收?如果JVM ...
- 十二、jdk工具之jcmd介绍(堆转储、堆分析、获取系统信息、查看堆外内存)
目录 一.jdk工具之jps(JVM Process Status Tools)命令使用 二.jdk命令之javah命令(C Header and Stub File Generator) 三.jdk ...
- JVM源码分析之堆外内存完全解读
JVM源码分析之堆外内存完全解读 寒泉子 2016-01-15 17:26:16 浏览6837 评论0 阿里技术协会 摘要: 概述 广义的堆外内存 说到堆外内存,那大家肯定想到堆内内存,这也是我们 ...
- Java堆外内存之七:JVM NativeMemoryTracking 分析堆外内存泄露
Native Memory Tracking (NMT) 是Hotspot VM用来分析VM内部内存使用情况的一个功能.我们可以利用jcmd(jdk自带)这个工具来访问NMT的数据. NMT介绍 工欲 ...
随机推荐
- 诸多老牌数据仓库厂商当前,Snowflake如何创近12年最大IPO金额
摘要:在数据仓库/分析领域,有传统厂商Oracle,Teradata,开源软件Hadoop,云厂商AWS Redshift,Google Bigquery,Snowflake成功的技术原因是什么? 1 ...
- 毕业季 | 华为专家亲授面试秘诀:如何拿到大厂高薪offer?
摘要:了解大厂面试流程.要求以及技巧,做好充分准备,赢在职场起跑线. 本文分享自华为云社区<毕业季 | 华为专家亲授面试秘诀:如何拿到大厂高薪offer?>,作者:华为云社区精选. 同学们 ...
- 华为AppCube通过中国信通院“低代码开发平台通用能力要求”评估!
摘要:华为AppCube应用魔方顺利通过信通院评估,被认证为具备 "低代码开发平台通用能力"的企业服务平台. 本文分享自华为云社区<华为AppCube通过中国信通院" ...
- 【教程】app备案流程简单三部曲即可完成
[教程]app备案流程简单三部曲即可完成 APP备案流程包括以下步骤: 1. 开发者实名认证:在提交备案申请之前,开发者需要通过移动应用开发平台进行实名认证.这个步骤需要提供身份证号码.姓名.联系 ...
- IaC 存储最佳实践
往往一些成功的软件公司在构建解决方案的时候十分注重其可重复性.可审计性.和简便性,而基础设施即代码(IaC)的出现让开发人员能够将这些时间应用于基础设施的分配.目前的存储 IaC 的实践有以下三种: ...
- 【每日一题】2.合并回文子串 (字符串处理 + 区间DP)
题目链接:Here 遇到这种数据范围较小的计数问题应该优先考虑dp,本题就是如此. 那么应该怎么样考虑转移呢? 首先最后C中的那个价值最大的子串一定是由字符串A的一个区间和字符串B的一个区间合并得到的 ...
- 《深入理解计算机系统》(CSAPP)实验四 —— Attack Lab
这是CSAPP的第四个实验,这个实验比较有意思,也比较难.通过这个实验我们可以更加熟悉GDB的使用和机器代码的栈和参数传递机制. @ 目录 实验目的 准备工作 内容简介 代码注入攻击 Level 1 ...
- S3C2440移植uboot之支持NAND启动
上一节S3C2440移植uboot之新建单板_时钟_SDRAM_串口移植uboot初始化了时钟,配置了支持串口,这一节我们继续修改uboot支持NAND启动. 目录 1.去掉 "-pie ...
- vmware超融合基础安装与配置
目录 vmware超融合 安装配置ESXI 安装VMware vCenter Server 安装vCenter插件 安装vCenter 使用VMware Vsphere Client登录Vcenter ...
- idea 中添加查看字节码工具
本文为博主原创,未经允许不得转载: jdk 的 bin 目录下存在 javap.exe 的工具,可通过 以下命令查看编译的字节码文件: javap -c Test.class 在 idea 中添加查看 ...