这篇是由一个问题引起的,一个生产环境在跑的服务频繁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,不由得在想,面试题是不是都得改一改了哈哈~
相关推荐
http://blog.csdn.net/notsaltedfish/article/details/77866472 该博客用于测试metaspace内存溢出的代码,启动ClassMetadataLeakSimulator类当中的main方法即可测试,另外将JVM metaspace内存设置小一点,这样能够更...
7. **元空间(Metaspace)替换持久代(PermGen)**:Java 7末期, PermGen 空间被废弃,取而代之的是Metaspace,用于存储类元数据。Metaspace的大小可动态调整,减少了因 PermGen 空间溢出而导致的 Full GC 发生。 ...
1.8.0_45版本的JVM优化了垃圾回收机制,提升了性能,并引入了新的内存区域,如元空间(Metaspace),以替代以前的永久代(Permanent Generation)。 3. **Java运行时环境(JRE)**:包含JVM以及运行Java应用程序所...
在Java 8中,这部分被MetaSpace取代,可通过`-XX:MaxMetaspaceSize`进行设置。 5. **考虑使用G1垃圾收集器**:对于大型应用,可以考虑使用G1(Garbage First)垃圾收集器,它提供了更好的性能和可预测的停顿时间。 ...
在现代JVM如Java 8及之后的版本中,这部分已经被元空间(Metaspace)取代。 3. **Java Stack(Java栈)**:每个线程都有自己的Java栈,用于存储方法调用的局部变量、操作数栈和方法返回地址。当栈溢出时,会抛出`...
Java错误在IDEA64中通常涉及到Java虚拟机(JVM)的问题,特别是当出现“Out Of Memory”(OOM)异常时。这个错误表明程序在运行过程中耗尽了可用内存,导致JVM无法正常工作。在这种情况下,程序员会生成一个heap ...
在Java 8之前,这部分内存被称为永久代(PermGen),在Java 8后被元空间(Metaspace)取代。元空间使用的是本地内存,大小可以动态调整,但如果类加载过多,可能导致元空间溢出(OutOfMemoryError: Metaspace)。 ...
在JDK1.4至JDK6期间,永久代的概念与现在略有不同,但在JDK7中被元空间(Metaspace)所取代,其位置从堆内存外移到了本地内存中,从而不再受堆内存大小的限制。 ### 虚拟内存(Virtual Memory) 在讨论Java内存模型时...
JVM也进行了重大改造,如默认使用更高效的G1垃圾收集器,以及对Metaspace的引入,替代了原来的PermGen空间。 2017年,Java 9的发布引入了模块化系统(Project Jigsaw),旨在提高大型应用程序的可维护性和性能。...
永久代是JVM中存储类元数据的区域,但在Java 8中,元数据被存储在本机内存的MetaSpace中,不再占用堆内存,从而避免了内存溢出的问题。元空间的大小可以通过JVM参数进行调整,并且可以自动扩展,以适应应用的需求。 ...
需要注意的是,在Java 7及以后版本中,永久代已经被Metaspace取代。 - **Native Heap (C-Heap)**:所有JVM厂商都会使用的一种内存区域,主要用于存储非Java代码的内存需求。 ##### **1.2 学习JVM内存管理的重要性**...
- 调整永久代(PermGen space,JDK 7及以前版本使用)或元空间(Metaspace,JDK 8及以上版本使用)大小。对于永久代,可以使用`-XX:PermSize`和`-XX:MaxPermSize`参数进行配置;对于元空间,则通过`-XX:...
6. **元空间(Metaspace)**:Java8替代了永久代(PermGen),使用元空间来存储类元数据。元空间位于 native memory 中,避免了永久代因类数量过多导致的 OutOfMemoryError。 7. **并行与并发**:JVM支持多线程执行...
本文总结了 Java 8 的一些重要特性和面试问题,涵盖了 Java 8 的新特性、编程范式、MetaSpace、功能性接口、default 方法、静态方法、λ表达式等方面。 Java 8 的新特性 Java 8 中添加了许多新的特性,包括 Lambda...
永久代(PermGen)在Java 8中已经被元空间(Metaspace)所替代。 垃圾回收与算法是Java内存管理的核心内容。JVM通过垃圾回收机制来自动管理内存,包括如何确定垃圾、引用计数法、可达性分析、标记清除算法、复制...
在Java 8之后被元空间(Metaspace)取代。 4. JVM运行时内存 - 新生代(Young Generation):新生代分为Eden区、SurvivorFrom区和SurvivorTo区,用于存放新创建的对象。当Eden区满时,会触发Minor GC,将存活的...
- 需要注意的是,从Java 8开始,永久代被元空间(Metaspace)所替代,因此如果使用的是Java 8及以上版本,应该使用 `-XX:MetaspaceSize` 和 `-XX:MaxMetaspaceSize` 来代替 `-XX:PermSize` 和 `-XX:MaxPermSize`。...
Java SE 7中,这部分被称为元空间(Metaspace),与之前版本的永久代(Permanent Generation)有所不同,元空间的大小可动态调整,并减少了Full GC的发生。 5. 虚拟机栈(JVM Stack):每个方法调用对应一个栈帧,...
- 移除了PermGen,由Metaspace取代 #### Java 9新特性详解 随着Java的发展,Java 9带来了许多重要的改进,其中最显著的是模块化系统(Modularity)的引入。 - **模块化系统** Java 9中最重要的变化就是实现了...