java metaspace

这篇是由一个问题引起的,一个生产环境在跑的服务频繁full gc,gc日志如下:

 

1.855: [GC (Metadata GC Threshold) [PSYoungGen: 524299K->10758K(1223168K)] 524299K->10846K(4019712K), 0.0119061 secs] [Times: user=0.10 sys=0.01, real=0.01 secs] 
1.867: [Full GC (Metadata GC Threshold) [PSYoungGen: 10758K->0K(1223168K)] [ParOldGen: 88K->10044K(2796544K)] 10846K->10044K(4019712K), [Metaspace: 20875K->20875K(1069056K)], 0.0325416 secs] [Times: user=0.30 sys=0.00, real=0.03 secs] 
5.855: [GC (Metadata GC Threshold) [PSYoungGen: 968907K->29712K(1223168K)] 978951K->39772K(4019712K), 0.0240853 secs] [Times: user=0.19 sys=0.02, real=0.02 secs] 
5.879: [Full GC (Metadata GC Threshold) [PSYoungGen: 29712K->0K(1223168K)] [ParOldGen: 10060K->33274K(2796544K)] 39772K->33274K(4019712K), [Metaspace: 34806K->34806K(1081344K)], 0.0580298 secs] [Times: user=0.59 sys=0.05, real=0.06 secs] 
48.066: [GC (Allocation Failure) [PSYoungGen: 1048576K->37080K(1223168K)] 1081850K->70389K(4019712K), 0.0224996 secs] [Times: user=0.20 sys=0.10, real=0.03 secs] 
51.885: [GC (Allocation Failure) [PSYoungGen: 1085656K->42448K(1223168K)] 1118965K->75765K(4019712K), 0.0170016 secs] [Times: user=0.35 sys=0.10, real=0.02 secs] 
55.655: [GC (Allocation Failure) [PSYoungGen: 1091024K->59680K(1223168K)] 1124341K->93006K(4019712K), 0.0193636 secs] [Times: user=0.38 sys=0.03, real=0.02 secs] 
59.423: [GC (Allocation Failure) [PSYoungGen: 1108256K->70128K(1280512K)] 1141582K->120301K(4077056K), 0.0266714 secs] [Times: user=0.37 sys=0.42, real=0.03 secs] 
64.170: [GC (Metadata GC Threshold) [PSYoungGen: 1137631K->86792K(1297408K)] 1187804K->136981K(4093952K), 0.0330375 secs] [Times: user=0.54 sys=0.25, real=0.03 secs] 
64.203: [Full GC (Metadata GC Threshold) [PSYoungGen: 86792K->0K(1297408K)] [ParOldGen: 50189K->115028K(2796544K)] 136981K->115028K(4093952K), [Metaspace: 57876K->57876K(1101824K)], 0.1935495 secs] [Times: user=3.67 sys=0.12, real=0.20 secs] 

看了日志就有点懵圈,full gc的时候,新生代老年代都还没满呢,咋回事,然后最后面还有个metaspace的数值纹丝不动,这又是个什么妖怪。

 

好吧好吧,既然你这么牛逼,我还是复习一下java内存模型先。。。,先上图



这图眼熟吧,嗯,在很多文章里都是这张图,版权未知。。。

那么jvm内存大体由以下几部分组成:

堆、栈、方法区、程序计数器,栈又分为虚拟机栈和本地方法栈。方法区呢,在hotspot里的实现就叫做PermGen space,翻译为永久代,为啥叫永久代,不知道,可能脑洞开太大了,其实full gc的时候会回收permgen。

额好吧就复习到这里,那么问题来了metaspace是个什么东西。。。,搜了一下原来在jdk8时代,hotspot就已经干掉了permgen,把部分内容移到了heap(nterned Strings and class statics),部分内容放到了本地内存(class meta-data),这就是metaspace。

为啥要移除permgen,请看JEP 122: Remove the Permanent Generation,大致的意思是:

1、程序猿们厌倦了提心吊胆的设置permgen大小,一不小心就oom

2、hotspot要向JRockit靠拢

 

而metaspace呢,它有这些特点:

1、所在的服务器内存有多大,它就可以分配多少内存,如果不设置上限的话

2、给每个classloader分配若干个大小不一的块以保存各类元信息,不够时还可以新增块,块的大小依赖于应用程序的行为,调整块大小是为了减少内部、外部碎片。当classloader die的时候可以同时释放所有他的块。

 

metaspace相关的jvm参数有以下这些:

 

-XX:MetaspaceSize=<NNN>
where <NNN> is the initial amount of space(the initial
high-water-mark) allocated for class metadata (in bytes) that may induce a
garbage collection to unload classes. The amount is approximate. After the
high-water-mark is first reached, the next high-water-mark is managed by
the garbage collector

-XX:MaxMetaspaceSize=<NNN>
where <NNN> is the maximum amount of space to be allocated for class
metadata (in bytes). This flag can be used to limit the amount of space
allocated for class metadata. This value is approximate. By default there
is no limit set.

-XX:MinMetaspaceFreeRatio=<NNN>
where <NNN> is the minimum percentage of class metadata capacity
free after a GC to avoid an increase in the amount of space
(high-water-mark) allocated for class metadata that will induce a garbage
collection.
-XX:MaxMetaspaceFreeRatio=<NNN>
where <NNN> is the maximum percentage of class metadata capacity
free after a GC to avoid a reduction in the amount of space
(high-water-mark) allocated for class metadata that will induce a garbage
collection.

 

 

那么既然大小可以扩大到服务器内存的大小,free了一下服务器内存还不少啊,怎么会full gc的呢。

原来metaspace维护着一个HWM(high-water-mark),这个值初始化时等于MetaspaceSize(12Mbytes on the 32bit client VM and 16Mbytes on the 32bit server VM with larger sizes on the 64bit VM's,有的文章里说是21M,自己测试后发现也不一定),当达到初始值后,就会触发gc ,hwm也会随着gc后剩余空间的大小而变化。

另外,metaspace的resize貌似并不是由gc触发的,resize的触发机制还有待研究。。。

 

以下是自己理解的,有误的话欢迎指正

MinMetaspaceFreeRatio,表示gc后剩余空间最小的比例,小于这个比例,就会触发resize(增大) ,同时hmw也会升高;

MaxMetaspaceFreeRatio,表示gc后剩余空间最大的比例,大于这个比例,也会触发 resize(减小) ,同时hmw也会降低;

 

那好吧,问题就在这里了,我得设置一下初始的hwm,本来根据以前设置heap的经验,min和max设置为相同可以减少扩容时的效率损失,但是现在metaspace最小值貌似是没法设置的,所以这个目的达不到。MetaspaceSize = MaxMetaspaceSize的话,可以在oom之前都不full gc,不过这样一来,貌似也没有达到干掉permgen的初衷,就是跳过烦人的oom。就先这样吧,毕竟full gc常有而oom不常有。。。

 

另外,metaspace也是可以dump的,jmap -clstats PID,但是得用jdk8的jmap,不然报版本不匹配的错误。

 

题外话,看完java8 metaspace,不由得在想,面试题是不是都得改一改了哈哈~

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值