JVM运行时占用的内存

一个线上服务内存占用带来的问题
在Oracle官网给出的JVM调优文档中关于堆的调整技巧有以下三个方面:

1.应将堆大小设置为不超过最大可用物理RAM量。如果超过此值,操作系统将开始分页,性能会显着下降。VM总是使用比堆大小更多的内存。除了堆大小设置之外,还分配内部VM功能所需的内存,VM外部的本地类库和永久区(仅适用于Sun虚拟机:存储类和方法所需的内存)。

2.使用分代垃圾收集方案时,年轻代大小不应超过Java堆总大小的一半。通常,堆大小的25%到40%就足够了。

3.在生产环境中,将最小堆大小和最大堆大小设置为相同的值,以防止浪费用于
不断增长和收缩堆的VM资源。

第三点将初始堆大小设置和最大堆大小相同,可以减少JVM重新分配内存,伸缩J堆大小时候的GC压力,在线上实际运行的时候给-Xms和-Xmx参数均设置成2048m,可是使用htop命令查看进程消耗的内存时候,发现只使用了1098m。Xms是设置初始堆和最小堆的大小,难道出问题了么?使用jmap命令输出堆的实际情况,如下:

[root@pc ~]# jmap -heap 15189
Attaching to process ID 15189, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08

using thread-local object allocation.
Parallel GC with 2 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 2147483648 (2048.0MB)
   NewSize                  = 715653120 (682.5MB)
   MaxNewSize               = 715653120 (682.5MB)
   OldSize                  = 1431830528 (1365.5MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 677380096 (646.0MB)
   used     = 359083872 (342.4490661621094MB)
   free     = 318296224 (303.5509338378906MB)
   53.01069135636368% used
From Space:
   capacity = 18874368 (18.0MB)
   used     = 8472112 (8.079635620117188MB)
   free     = 10402256 (9.920364379882812MB)
   44.886864556206596% used
To Space:
   capacity = 18350080 (17.5MB)
   used     = 0 (0.0MB)
   free     = 18350080 (17.5MB)
   0.0% used
PS Old Generation
   capacity = 1431830528 (1365.5MB)
   used     = 45051784 (42.96472930908203MB)
   free     = 1386778744 (1322.535270690918MB)
   3.14644667221399% used

31785 interned Strings occupying 3781800 bytes.

查看以上信息发现,堆分配大小没有问题的呀!这是为什么呢。。查了一会资料,JVM启动的时候,将虚拟地址与本机物理内存地址进行映射,实际运行的时候并没有使用到2048m内存,在堆内存不够用的时候回向操作系统申请物理内存,而操作系统htop命令查看的内存是RES即实际占用的物理内存,刚启动的时候比实际Xms来的小。

这里会带来两个问题:

第1次YGC之前Eden区分配对象的速度较慢;
YGC的时候,Young区的对象要晋升到Old区的时候,这个时候需要操作系统真正分配内存,这样就会加大YGC的停顿时间;
我们可以给JVM添加-XX:+AlwaysPreTouch这个参数优化这个问题,不过这个参数在JDK8下有一个副作用会大大提高启动时间。

在没有配置-XX:+AlwaysPreTouch参数即默认情况下,JVM参数-Xms申明的堆只是在虚拟内存中分配,而不是在物理内存中分配:它被以一种内部数据结构的形式记录,从而避免被其他进程使用这些内存。这些内存页直到被访问时,才会在物理内存中分配。当JVM需要内存的时候,操作系统将根据需要分配内存页。

配置-XX:+AlwaysPreTouch参数后,JVM将-Xms指定的堆内存中每个字节都写入’0’,这样的话,除了在虚拟内存中以内部数据结构保留之外,还会在物理内存中分配。并且由于touch这个行为是单线程的,因此它将会让JVM进程启动变慢。所以,要么选择减少接下来对每个缓存页的第一次访问时间,要么选择减少JVM进程启动时间

不过在JDK9中可以使用并行操作,Parallelize Memory Pretouch。

这里又有疑问了,当JVM内存申请使用完毕后会返还给物理内存吗?

不会,一旦虚拟机申请分配了物理内存,不会返还。
设置了Xmx参数,JVM一定不会超过这个大小么?超过一定会OOM或者SOF吗?

还有堆外内存。。。
JVM内存占用情况
JVM进程主要占用内存的一些地方,其中JDK8之前JMM模型中共享的永久区取消变为元空间(metaspace)依旧存放于堆外内存之中。

由以上可以看出,JVM实际占用的内存实际上包含堆内和堆外的,而虚拟机启动参数Xms和Xmx限制的是堆的大小,实际虚拟机内存可能大得多。

总结
JVM在默认情况下启动的时候,

JVM参数-Xms申明的堆只是在虚拟内存中分配,而不是在物理内存中分配;

-XX:+AlwaysPreTouch参数可以申请提前占用内存,减少GC消耗,但会提高启动时间;

JVM申请的物理内存,一旦申请不会返还,除非重启或者关闭;

JVM实际占用的内存包括堆内和堆外内存,也就是会大于Xmx设置的值。

作者:taj3991
链接:https://www.jianshu.com/p/ec9e9af53687
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值