“嘿,你能来看看奇怪的东西吗?” 这就是我开始研究一个支持案例的方式,该案例将我引向了这篇博客文章。 当前的特殊问题与不同的工具报告了有关可用内存的不同数字有关。

简而言之,一位工程师正在研究特定应用程序的过多内存使用情况,据他所知,该应用程序可以使用2G的堆。 但是无论出于什么原因,JVM工具本身似乎都没有决定该进程真正拥有多少内存。 例如, jconsole猜测总可用堆等于1,963M,而jvisualvm声称其等于2,048M。 那么哪个工具是正确的,为什么另一个却显示不同的信息呢?

确实确实很奇怪,尤其是看到通常的可疑对象都被淘汰了– JVM并没有采取任何明显的技巧,例如:

  • -Xmx-Xms相等,因此在运行时堆增加期间报告的数字不会更改
  • 通过关闭自适应大小调整策略( -XX:-UseAdaptiveSizePolicy ),防止JVM动态调整内存池大小。

重现差异

理解问题的第一步是放大工具实现。 通过标准API访问可用的内存信息非常简单,如下所示:

System.out.println("Runtime.getRuntime().maxMemory()="+Runtime.getRuntime().maxMemory());

确实,这就是手头工具似乎正在使用的工具。 回答此类问题的第一步是拥有可重现的测试用例。 为此,我编写了以下代码段:

package eu.plumbr.test;
//imports skipped for brevitypublic class HeapSizeDifferences {static Collection<Object> objects = new ArrayList<Object>();static long lastMaxMemory = 0;public static void main(String[] args) {try {List<String> inputArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();System.out.println("Running with: " + inputArguments);while (true) {printMaxMemory();consumeSpace();}} catch (OutOfMemoryError e) {freeSpace();printMaxMemory();}}static void printMaxMemory() {long currentMaxMemory = Runtime.getRuntime().maxMemory();if (currentMaxMemory != lastMaxMemory) {lastMaxMemory = currentMaxMemory;System.out.format("Runtime.getRuntime().maxMemory(): %,dK.%n", currentMaxMemory / 1024);}}static void consumeSpace() {objects.add(new int[1_000_000]);}static void freeSpace() {objects.clear();}
}

该代码通过循环中的新int [1_000_000]分配内存块,并检查当前已知可用于JVM运行时的内存。 每当发现最后一个已知的内存大小发生更改时,它都会通过打印Runtime.getRuntime()。maxMemory()的输出来报告该更改,如下所示:

Running with: [-Xms2048M, -Xmx2048M]
Runtime.getRuntime().maxMemory(): 2,010,112K.

确实- 即使我已指定JVM使用2G堆,运行时仍无法以某种方式找到其中的85M 。 您可以通过将Runtime.getRuntime()。maxMemory()的输出除以2,010,112K除以1024来转换为MB,从而仔细检查我的数学运算。结果将等于1,963M,与2048M的差值为85M。

寻找根本原因

在能够重现案例之后,我记下了以下笔记–使用不同的GC算法运行似乎也会产生不同的结果:

GC算法 Runtime.getRuntime()。maxMemory()
-XX:+ UseSerialGC 2,027,264千
-XX:+ UseParallelGC 2,010,112千
-XX:+ UseConcMarkSweepGC 2,063,104千
-XX:+ UseG1GC 2,097,152千

除了G1完全消耗了我给该进程分配的2G内存外,其他所有GC算法似乎都始终丢失半随机的内存。

现在是时候深入研究JVM 的源代码了,在CollectedHeap的源代码中,我发现了以下内容:

// Support for java.lang.Runtime.maxMemory():  return the maximum amount of
// memory that the vm could make available for storing 'normal' java objects.
// This is based on the reserved address space, but should not include space
// that the vm uses internally for bookkeeping or temporary storage
// (e.g., in the case of the young gen, one of the survivor
// spaces).
virtual size_t max_capacity() const = 0;

我不得不承认答案是非常隐蔽的。 但是仍然有一些真正的好奇心可以找到的暗示–指的是在某些情况下,堆大小计算中可能会排除一个幸存空间

从这里一直是顺风顺水–打开GC日志记录发现,实际上,使用2G堆,串行,并行和CMS算法都将幸存空间的大小设置为恰好缺少了差异。 例如,在上面的ParallelGC示例中,GC日志记录演示了以下内容:

Running with: [-Xms2g, -Xmx2g, -XX:+UseParallelGC, -XX:+PrintGCDetails]
Runtime.getRuntime().maxMemory(): 2,010,112K.... rest of the GC log skipped for brevity ...PSYoungGen      total 611840K, used 524800K [0x0000000795580000, 0x00000007c0000000, 0x00000007c0000000)eden space 524800K, 100% used [0x0000000795580000,0x00000007b5600000,0x00000007b5600000)from space 87040K, 0% used [0x00000007bab00000,0x00000007bab00000,0x00000007c0000000)to   space 87040K, 0% used [0x00000007b5600000,0x00000007b5600000,0x00000007bab00000)ParOldGen       total 1398272K, used 1394966K [0x0000000740000000, 0x0000000795580000, 0x0000000795580000)

从中可以看到Eden空间设置为524,800K,两个幸存者空间(从和到)都设置为87,040K,旧空间的大小为1,398,272K。 将Eden,Old和一个幸存者空间加在一起总计为2,010,112K,这证实丢失的85M或87,040K确实是剩余的幸存者空间

摘要

阅读该文章后,您现在对Java API实施细节有了新的了解。 下次某些工具将总可用堆大小可视化为略小于Xmx指定的堆大小时,您会知道差异等于您的Survivor空间之一的大小。

我必须承认,这一事实在日常编程活动中并不是特别有用,但这不是该帖子的重点。 取而代之的是,我写了一篇文章,描述了我一直在优秀工程师中寻找的一个特殊特性- 好奇心 。 优秀的工程师一直在寻找了解事物如何以及为什么以某种方式起作用的方式。 有时答案仍然隐藏,但我仍然建议您尝试寻求答案。 最终,沿途积累的知识将开始带来红利。

翻译自: https://www.javacodegeeks.com/2015/02/jvm-access-less-memory-specified-via-xmx.html

为什么我的JVM访问的内存少于通过-Xmx指定的内存?相关推荐

  1. jvm内存 大于 xmx_为什么我的JVM访问的内存少于通过-Xmx指定的内存?

    jvm内存 大于 xmx "嘿,你能来看看奇怪的东西吗?" 这就是我开始研究支持案例的方式,将我引向了这篇博客文章. 眼前的具体问题与报告可用内存数量不同的不同工具有关. 简而言之 ...

  2. JVM从零开始(二) -垃圾回收机制以及内存分代模型

    JVM中垃圾回收的判定标准 最终目的是将内存中无用的对象回收掉.具体的判定方法有: 引用计数法,不采用,指的是维护对象被引用的次数,次数为0则意味着是垃圾. 可达性算法-GC Roots tracin ...

  3. 【Linux 内核 内存管理】Linux 内核堆内存管理 ② ( 动态分配堆内存方式 | brk 系统调用 | mmap 系统调用 | brk 系统调用源码介绍 )

    文章目录 一.Linux 系统 动态分配堆内存 方式 二.brk 系统调用 动态分配堆内存 一.Linux 系统 动态分配堆内存 方式 Linux 系统中 , 提供了 222 种方式 进行 " ...

  4. JVM运行时结构、Java内存管理、JVM实例、HotSpot VM对象的创建、内存布局和访问定位

    1.JVM运行时结构 Java 运行时数据区域有程序计数器.Java虚拟机栈.本地方法栈.Java堆和方法区.其中前三个线程私有,随线程生而生,线程灭而灭:后面两个是线程间共享. 1.1 程序计数器 ...

  5. 《深入理解JVM.2nd》笔记(二):Java内存区域与内存溢出异常

    文章目录 概述 运行时数据区域 程序计数器 Java虚拟机栈 本地方法栈 Java堆 方法区 运行时常量池 直接内存 HotSpot虚拟机对象探秘 对象的创建 第一步 第二步 第三步 第四步 最后一脚 ...

  6. JVM类加载机制_字节码执行引擎_Java内存模型

    类加载机制: 类加载生命期:加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Usi ...

  7. 使用未初始化的内存是什么意思_他们都说JVM能实际使用的内存比-Xmx指定的少?这是为什么呢...

    这确实是个挺奇怪的问题,特别是当最常出现的几种解释理由都被排除后,看来JVM并没有耍一些明显的小花招: -Xmx和-Xms是相等的,因此检测结果并不会因为堆内存增加而在运行时有所变化. 通过关闭自适应 ...

  8. linux无效内存访问,x86_64 Linux 3.0:无效的内存地址

    x86_64体系结构上的Linux 3.0上的进程具有64位虚拟地址空间. 很明显,在该地址空间中,保证0是无效的内存地址[请参见下面的定义],因为该地址用于指示NULL指针. 保证哪些其他64位数字 ...

  9. JVM内存与垃圾回收篇——直接内存

    一.直接内存 Direct Memory介绍 直接内存不是虚拟机运行时数据区的一部分,也不是<Java虚拟机规范>中定义的内存区域,直接内存是在Java堆外的,直接向系统申请的内存区间.来 ...

最新文章

  1. 2、使用 kubeadm 方式快速部署K8S集群
  2. Vue-路由模式 hash 和 history
  3. win7如何配置access数据源
  4. IAR调试按钮功能说明及调试主要看哪些内容
  5. think php ajax分页,thinkPHP5框架实现基于ajax的分页功能示例
  6. Java使用PDFBox开发包实现对PDF文档内容编辑与保存
  7. 浅谈wcscpy_s之用法
  8. 傅里叶变换的初级理解二
  9. boost::errinfo_errno的用法测试程序
  10. 撒花!中文翻译仓库链接已加入 ML.NET 官方示例网站首页
  11. mysql linux附加数据库文件夹,Linux全攻略--MySQL数据库配置与管理
  12. 360内部监控系统Wonder实践之路
  13. php artisan key,Laravel:php artisan key:generate三种报错解决方案,修改默认PHP版本(宝塔面板)...
  14. C# 7.0 新特性3: 模式匹配
  15. Win8 64位安装Oracle 11g时错
  16. 用GPU进行TensorFlow计算加速
  17. 事业环境因素对项目现场实施的影响
  18. 电影在计算机中用什么形式保存,教你如何将 DVD 电影永久保存到电脑里
  19. 旋转屏幕时数据的保存与恢复
  20. 一段Js代码开启任意网站的匿名在线聊天室

热门文章

  1. Vue.js基础体验(一)
  2. 当字段过长,鼠标移上去才展示全部,默认只展示部分防止表格比例发生变化
  3. 2018蓝桥杯省赛---java---B---7(螺旋折线)
  4. [Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated c
  5. Linux ss命令 报错,ECS Linux中ss命令显示连接状态的使用说明
  6. python菱形画法解释_用Python画棱形
  7. python大神交流网站_学习Python必去的8个网站
  8. 使用工具将SQLServer转MYSQL的方法(连数据)
  9. poi中文api文档
  10. 递归 反转字符串_使用递归反转字符串