为什么JVM指定-Xmx参数后占用内存会变少?
嘿,你能顺便过来看看这个奇怪的事情吗?” 就是让我提供支持的这个事情,驱使我写下这篇博客的。这个特殊的问题是,不同工具给出的可用内存的报告是不一样的。
简而言之,工程师正在调查特定应用程序的内存使用。根据以往的经验,他给这个应用指定了2G堆内存。但是不知道什么原因,JVM工具似乎不能确定这个程序到底有多少内存。例如 jconsole 探测可用堆总共为1963M,但 jvisualvm 报告称堆为2048M。到底哪一个是正确的呢?为什么另一个给出了不一样的信息呢?
这的确很不可思议,特别是以往的认知被突然改变。表面上JVM没有耍任何花招:
- -Xmx 和 -Xms 是相等的,这就使得报告的数字不会随着堆实时增加。
- JVM避免通过内存的自适应策略(-XX:-UseAdaptiveSizePolicy)动态改变内存池的大小。
重现不同
搞懂这个问题的第一步是深入这些工具的实现方式。一般通过标准API查看可用内存会像下面这样:
1
|
System.out.println( "Runtime.getRuntime().maxMemory()=" +Runtime.getRuntime().maxMemory()); |
的确,这好像是工具首先会被用到的方式。寻找答案的第一步是找出可复现的测试用例。为了这个目的,我写了下面这段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
package eu.plumbr.test; //imports skipped for brevity public 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(); } } |
这段代码通过在一个 new int[1000000] 的循环中分配内存块,检测当前在实时JVM中的可用内存。无论何时,只要最后知道的内存大小改变时,都会通过打印出 ofRuntime.getRuntime().maxMemory()__ 报告出来,类似于如下这样:
1
2
|
Running with: [-Xms2048M, -Xmx2048M] Runtime.getRuntime().maxMemory(): 2,010,112K. |
结果确实如此——有时甚至指定JVM有2G可用堆,但是运行着莫名其妙地发现其中的85M找不到了。你可以通过运用 2,010,112K 除以 1024 转化Runtime.getRuntime().maxMemory() 的输出到MB来复查我的计算。实际结果等于1963M,比起实际的 2048M 少了 85M。
寻求根本原因
重现这个现象之后,我做了如下的笔记——采用不同的GC算法运行似乎也产生不同的结果:
GC algorithm | Runtime.getRuntime().maxMemory() |
-XX:+UseSerialGC | 2,027,264K |
-XX:+UseParallelGC | 2,010,112K |
-XX:+UseConcMarkSweepGC | 2,063,104K |
-XX:+UseG1GC | 2,097,152K |
除了G1消费了我实际给的2G之外,任何其它GC算法似乎始终会半随机地丢失一部分内存。
现在是时候剖析一下JVM的源代码了,在CollectedHeap 的源代码中,我发现了下面这些:
1
2
3
4
5
|
//对java.lang.Runtime.maxMemory()的支持: //返回虚拟机提供给“标准”java对象的最大内存。 //这个基于保留的地址空间,但是不应该包括虚拟机使用内部统计或临时存储的这部分空间。 //(例如:在青年代中,残留空间之一) virtual size_t max_capacity() const = 0 ; |
不得不承认答案隐藏得很深。但真相还是在好奇心的驱使下找到——事实上,某些情况下残留空间其中一些可能被排除在内存计算之外。
从这里开始就一帆风顺了。打开GC日志发现,确实在设置2G内存时,Parallel和CMS算法都会在不同程度上,设置残留的空间是可变的。例如,以Parallel算法为例GC的日志演示如下所示:
1
2
3
4
5
6
7
8
9
10
|
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空间被设置为了524800K,残留空间都被设为了 87040K,Old空间大小为 1398272K。把Eden、Old和残留空间之一加在一起等于2010112K,确认丢失的 85 或 87040K 确实是保留的残留空间。
总结
读完这篇文章后,相信你现在已经准备好以一种新的视角深入到Java API的实现细节。下次遇到可视化工具的总可用堆大小略低于Xmx规定的大小时,你就知道少的那部分等于你一个残留空间的大小。
不得不承认的一个事实是,在日常的编程中不是特别有用,但是这不是我写这篇文章的初衷。相反地,写这篇文章目的是为了强调我在优秀工程师身上看到的特质——好奇心。优秀的工程师总是想去知道,那些东西的工作方式并探究为什么它们会像那样工作。有时候答案藏匿地很深,但仍然建议你去试图寻求答案。最终,在这个过程中获取的知识,将会让你受益无穷。
为什么JVM指定-Xmx参数后占用内存会变少?的更多相关文章
- Linux的php-fpm优化心得-php-fpm进程占用内存大和不释放内存问题(转)
原文地址:https://wzfou.com/php-fpm/ 最近发现博客的内存老是隔三差五地被“吃掉”了,登录到后台后偶尔会出卡顿的情况,一开始怀疑是Swap不够导致的,于是给VPS主机增加了几个 ...
- unity texture 占用内存大小对比
打包多种类型的项目,空项目和10张放在Resources文件夹中的图为比较案例.以下是比较数据. IPHONE: 1.空项目----空间占用量42.3MB----IPA大小10MB 2.10张1200 ...
- Jmeter-调整占用内存解决内存溢出
启动jmeter.从启动jmeter的输出就可以看到,Modify HEAP “” in the jmeter batch file -Xmx512m -Xms512m -Xms是初始内存,-X ...
- JVM内存参数( -Xms -Xmx -Xmn -Xss 直接内存)
JVM调优总结 -Xms -Xmx -Xmn -Xss jvm 内存 在不同的情况下如何增大 及 PermGen space 相关 JVM日志和参数的理解 JVM崩溃Log日志分析 -Xms 为jvm ...
- JVM参数配置及内存调优
一.JVM常见参数配置 堆内存相关参数 参数名称 含义 默认值 -Xms 初始堆大小 物理内存的1/64(<1GB) 默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40% ...
- JVM常用启动参数+常用内存调试工具
一.JVM常用启动参数 -Xms:设置堆的最小值. -Xmx:设置堆的最大值. -Xmn:设置新生代的大小. -Xss:设置每个线程的栈大小. -XX:NewSize:设置新生代的初始值. -XX:M ...
- Redis达到最大占用内存后的淘汰策略
1. 查询Redis最大占用内存 # 查询最大占用内存 config get maxmemory # 为0时在64操作系统中不限制内存,在32位操作系统中最大为3GB 2. Redis设置最大占用内存 ...
- 为什么要指定HashMap的容量?HashMap指定容量初始化后,底层Hash数组已经被分配内存了吗?
为什么要指定HashMap的容量? 首先创建HashMap时,指定容量比如1024后,并不是HashMap的size不是1024,而是0,插入多少元素,size就是多少: 然后如果不指定HashMap ...
- Android中一张图片加载后所占用内存大小的获取与测试
Android程序中一旦加载的图片比较多,就有可能出现Out of Memory而导致程序崩溃.这个一方面是因为Android系统本身对于每个单独的进程有内存大小的限制(有16M,64M,128M,2 ...
随机推荐
- weblogic12c配置免密码启动
在运行startWeblogic.sh时需要输入有效的账号密码才能启动weblogic,为简化操作,可以配置boot.properties来免输账号密码,配置方法如下:1.查看在./domains/x ...
- 使用select2插件并添加拼音首字母检索
项目中要使用下拉检索的时候要支持拼音首字母.本来拼音可以写后台,这里放前台了. 放代码 1. pinyin.js ,最后为了使用方便,直接为string对象添加了扩展方法 /* File Create ...
- 关于"implicit declaration of function 'gettimeofday' is invalid in c99"的解决
http://blog.csdn.net/macmini/article/details/10503799 当我们使用 gettimeofday(&time, NULL);时,会出现这样一个W ...
- Linux中tty框架与uart框架之间的调用关系剖析【转】
转自:http://developer.51cto.com/art/201209/357501.htm 之前本人在"从串口驱动的移植看linux2.6内核中的驱动模型 platform de ...
- 【linux高级程序设计】(第十三章)Linux Socket网络编程基础 4
网络调试工具 tcpdump 功能:打印指定网络接口中与布尔表达式匹配的报头信息 关键字: ①类型:host(默认).net.port host 210.27.48.2 //指明是一台主机 net 2 ...
- Appium+python自动化15-在Mac上环境搭建【转载】
前言 mac上搭建appium+python的环境还是有点复杂的,需要准备的软件 1.nodejs 2.npm 3.cnpm 4.appium 5.pip 6.Appium-Python-Client ...
- 通过GitHub部署项目到Nginx服务器
1.更新源: 2.安装nginx 3.安装成功 4.DNS域名解析 5.访问域名就会找到相应IP地址的主机,一个IP可对应多个域名 6.提交到gitHub 复制这两行 填上邮箱和密码 7.提交成功 8 ...
- Netty源码学习(七)FastThreadLocal
0. FastThreadLocal简介 如同注释中所说:A special variant of ThreadLocal that yields higher access performance ...
- 天梯L2-003-测试点
测试点3无法过,题目说是正整数用了int,结果得用double输入才能AC.
- 【bzoj1977】【严格次小生成树】倍增维护链上最大次大值
(上不了p站我要死了,侵权度娘背锅) Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C ...