1.概述

是否曾经想过为什么Java应用程序通过众所周知的

-Xms和-Xmx

调整标志消耗的内存比指定的数量大得多 ?由于各种原因和可能的优化,JVM可能会分配额外的本机内存。这些额外的分配最终可能使消耗的内存超出-Xmx限制。

在本教程中,我们将枚举JVM中本机内存分配的一些常见来源,以及它们的大小调整标志,然后学习如何使用本机内存跟踪来监视它们。

2.本机分配

通常,堆是Java应用程序中最大的内存消耗者,但是还有其他一些。**除了堆之外,JVM从本地内存中分配了相当大的块来维护其类元数据,应用程序代码,由JIT生成的代码,内部数据结构等。**在以下各节中,我们将探讨其中的一些分配。

2.1 元空间

为了维护有关已加载类的某些元数据,JVM使用了称为

Metaspace*****的专用非堆区域

。在Java 8之前,等效项称为PermGen或

Permanent Generation*。Metaspace或PermGen包含有关已加载类的元数据,而不是包含在堆中的有关它们的实例的元数据。

这里重要的是,由于元空间是堆外数据区域,因此堆大小调整配置不会影响元空间的大小。为了限制元空间的大小,我们使用其他调整标志:-XX:MetaspaceSize

-XX:MaxMetaspaceSize*设置最小和最大元空间大小

在Java 8之前,使用

-XX:PermSize

-XX:MaxPermSize

来设置最小和最大PermGen大小

2.2 线程数

JVM中最消耗内存的数据区域之一是堆栈,它与每个线程同时创建。堆栈存储局部变量和部分结果,在方法调用中起着重要作用。

默认的线程堆栈大小取决于平台,但是在大多数现代的64位操作系统中,大约为1 MB。此大小可通过*-Xss *调整标志进行配置。

与其他数据区域相比,当对线程数没有限制时,分配给堆栈的总内存实际上是不受限制的。 还值得一提的是,JVM本身需要一些线程来执行其内部操作,例如GC或即时编译。

2.3 代码缓存

为了在不同平台上运行JVM字节码,需要将其转换为机器指令。在执行程序时,JIT编译器负责此编译。

JVM将字节码编译为汇编指令时,会将这些指令存储在称为代码缓存的特殊非堆数据区域中 *。 可以像JVM中的其他数据区域一样管理代码缓存。的 *-XX:InitialCodeCacheSize *和 *-XX:ReservedCodeCacheSize *调谐标志确定用于代码高速缓存中的初始和最大可能大小。

2.4 垃圾收集

JVM附带了几种GC算法,每种算法都适合不同的用例。所有这些GC算法都有一个共同的特征:它们需要使用一些堆外数据结构来执行任务。这些内部数据结构消耗更多的本机内存。

2.5 Symbols

让我们从字符串开始 *, *它是应用程序和库代码中最常用的数据类型之一。由于它们无处不在,因此它们通常占据堆的很大一部分。如果大量的这些字符串包含相同的内容,那么堆的很大一部分将被浪费。

为了节省一些堆空间,我们可以存储每个*String的 *一个版本, 并让其他版本引用存储的版本。

此过程称为

字符串实习。

**由于JVM只能内生 *编译时间字符串常量,因此

我们可以对要内生的字符串手动调用

intern() *方法。

JVM将内联的字符串存储在特殊的本机固定大小的哈希*****表中,*****该哈希表称为*****String Table,*****也称为

*****。* 我们可以通过*-XX:StringTableSize *调整标志来配置表的大小(即桶数) 。

除了字符串表外,还有另一个本机数据区域,称为*运行时常量池。 *JVM使用此池存储必须在运行时解析的常量,例如编译时数字文字,方法和字段引用。

2.6 本机字节缓冲区 Native Byte Buffers

JVM通常是大量本机分配的可疑对象,但有时开发人员也可以直接分配本机内存。最常见的方法是通过JNI和NIO的直接

ByteBuffers

进行*malloc *调用

2.7 Additional Tuning Flags

在本节中,我们针对不同的优化方案使用了少数JVM调整标志。使用以下技巧,我们几乎可以找到与特定概念相关的所有调整标志:$ java -XX:+PrintFlagsFinal -version | grep 复制代码

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  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  VM.native_memory baselineBaseline succeeded复制代码

然后,过一会儿,我们可以将当前内存使用量与该基准进行比较:$ jcmd  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中本机内存分配的不同贡献者。然后,我们学习了如何检查正在运行的应用程序以监视其本机分配。借助这些见解,我们可以更有效地调整应用程序并调整运行时环境的大小。

日本java69_Java应用监控(10)-NMT堆外内存分析2相关推荐

  1. java visualvm远程监控_如何监控和诊断堆外内存使用

    如何监控和诊断堆外内存使用 可以使用综合性的图形化工具,如 JConsole.VisualVM ,这些工具比较直观,直接连接到 Java 进程,图形化界面. 可以使用命令工具进行查询,如 jstat ...

  2. 一文探讨堆外内存的监控与回收

    引子 记得那是一个风和日丽的周末,太阳红彤彤,花儿五颜六色,96 年的普哥微信找到我,描述了一个诡异的线上问题:线上程序使用了 NIO FileChannel 的 堆内内存作为缓冲区,读写文件,逻辑可 ...

  3. netty数据流堆外内存排查

    背景 行情服务每次在开盘与收盘期间的堆外内存都会上涨,并且需要周期性手动重启,影响到服务的稳定性 排查过程 1.堆外内存的计算标准 此matrix(used_direct_memory)计算标准由ne ...

  4. java堆外内存泄漏分析排查

    JAVA堆外内存分析 文章目录 JAVA堆外内存分析 1.前言 2.准备 3.具体分析 3.1堆外溢出风险判断 3.1.1确认java进程号 3.1.2查看此java进程的jvm参数 3.1.3查看j ...

  5. haddler处理队列 netty_Netty堆外内存泄漏排查,这一篇全讲清楚了

    上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参 ...

  6. java nio 李林峰_Netty堆外内存泄漏排查,这一篇全讲清楚了

    上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参 ...

  7. Java堆外内存泄露分析

    查看堆内存占用正常,jvm垃圾回收也没有异常.而top出来显示java占用内存是几个G,那么可能想到了是堆外内存泄漏. 需要安装google-perftools工具进行分析 1.先安装g++ 不然编译 ...

  8. java 监控 native 内存_JVM NativeMemoryTracking 分析堆外内存泄露

    Native Memory Tracking (NMT) 是Hotspot VM用来分析VM内部内存使用情况的一个功能.我们可以利用jcmd(jdk自带)这个工具来访问NMT的数据. NMT介绍 工欲 ...

  9. java 查看堆外内存占用_如何监控和诊断JVM堆内和堆外内存使用?

    上一讲我介绍了 JVM 内存区域的划分,总结了相关的一些概念,今天我将结合 JVM 参数.工具等方面,进一步分析 JVM 内存结构,包括外部资料相对较少的堆外部分. 今天我要问你的问题是,如何监控和诊 ...

最新文章

  1. 【ES6】Promise对象详解
  2. oracle sqlstate 22023,DB2 开发常遇到一些错误
  3. Zookeeper源码解读
  4. 【转】 浏览器分析模拟登陆过程
  5. “懒”的妙用——浅析图片懒加载技术
  6. 题目1028:继续畅通工程
  7. 用树莓派获取天气状况
  8. torch torchvision 下载安装与使用
  9. Java中的门面设计模式,非常有用!
  10. Py之paddlehub:paddlehub的简介、安装、使用方法之详细攻略
  11. 一千行MySQL学习笔记(十二)
  12. 【数据结构与算法】之深入解析“H指数II”的求解思路与算法示例
  13. java jpa 注解_Java : JPA相关以及常用注解
  14. 草稿-xpath了解-python 操作xpath小例子
  15. es的doc_value对排序字段的作用
  16. “网友”叫你先上STM32,51是小朋友玩的,所以你就不学51了
  17. JS等比例缩小图片尺寸
  18. 四维图新地图坐标_四维图新:自动驾驶的“高精度地图世界观”
  19. 服务器怎么装虚拟打印机,pdfFactory pdf虚拟打印机安装使用教程[图文]
  20. 无线路由器显示无服务器,路由器没信号怎么办?三招教你解决问题!

热门文章

  1. 撕下 Coding iPad 悬赏单的小小感触
  2. 在白宫插上了五星红旗!编程也能保护国家,还不好好学习吗
  3. 拳皇97 for Mac(街机格斗游戏)
  4. npm install electron 卡在 Downloading
  5. MySQL-数据查询操作-基本查询-条件查询-排序查询
  6. 安卓手机抓https,vmos pro + charles实现
  7. 阿林要振作,一定要无事平安!
  8. 数据结构与算法-实验1-多项式的计算:合并同类项、升幂排序、多项式加法、减法、乘法
  9. [转载]关于浙江大学新校长人选的另一种声音
  10. server2008服务器可以远程桌面连接,但是ping不通