http://www.infoq.com/cn/articles/Java-PERMGEN-Removed

在Java虚拟机(以下简称JVM)中,类包含其对应的元数据,比如类的层级信息,方法数据和方法信息(如字节码,栈和变量大小),运行时常量池,已确定的符号引用和虚方法表。

在过去(当自定义类加载器使用不普遍的时候),类几乎是“静态的”并且很少被卸载和回收,因此类也可以被看成“永久的”。另外由于类作为JVM实现的一部分,它们不由程序来创建,因为它们也被认为是“非堆”的内存。

在JDK8之前的HotSpot虚拟机中,类的这些“永久的”数据存放在一个叫做永久代的区域。永久代一段连续的内存空间,我们在JVM启动之前可以通过设置-XX:MaxPermSize的值来控制永久代的大小,32位机器默认的永久代的大小为64M,64位的机器则为85M。永久代的垃圾回收和老年代的垃圾回收是绑定的,一旦其中一个区域被占满,这两个区都要进行垃圾回收。但是有一个明显的问题,由于我们可以通过‑XX:MaxPermSize 设置永久代的大小,一旦类的元数据超过了设定的大小,程序就会耗尽内存,并出现内存溢出错误(OOM)。

 

备注:在JDK7之前的HotSpot虚拟机中,纳入字符串常量池的字符串被存储在永久代中,因此导致了一系列的性能问题和内存溢出错误。想要了解这些永久代移除这些字符串的信息,请访问这里查看。

辞永久代,迎元空间

随着Java8的到来,我们再也见不到永久代了。但是这并不意味着类的元数据信息也消失了。这些数据被移到了一个与堆不相连的本地内存区域,这个区域就是我们要提到的元空间。

这项改动是很有必要的,因为对永久代进行调优是很困难的。永久代中的元数据可能会随着每一次Full GC发生而进行移动。并且为永久代设置空间大小也是很难确定的,因为这其中有很多影响因素,比如类的总数,常量池的大小和方法数量等。

 

同时,HotSpot虚拟机的每种类型的垃圾回收器都需要特殊处理永久代中的元数据。将元数据从永久代剥离出来,不仅实现了对元空间的无缝管理,还可以简化Full GC以及对以后的并发隔离类元数据等方面进行优化。

移除永久代的影响

由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间。因此,我们就不会遇到永久代存在时的内存溢出错误,也不会出现泄漏的数据移到交换区这样的事情。最终用户可以为元空间设置一个可用空间最大值,如果不进行设置,JVM会自动根据类的元数据大小动态增加元空间的容量。

注意:永久代的移除并不代表自定义的类加载器泄露问题就解决了。因此,你还必须监控你的内存消耗情况,因为一旦发生泄漏,会占用你的大量本地内存,并且还可能导致交换区交换更加糟糕。

元空间内存管理

元空间的内存管理由元空间虚拟机来完成。先前,对于类的元数据我们需要不同的垃圾回收器进行处理,现在只需要执行元空间虚拟机的C++代码即可完成。在元空间中,类和其元数据的生命周期和其对应的类加载器是相同的。话句话说,只要类加载器存活,其加载的类的元数据也是存活的,因而不会被回收掉。

我们从行文到现在提到的元空间稍微有点不严谨。准确的来说,每一个类加载器的存储区域都称作一个元空间,所有的元空间合在一起就是我们一直说的元空间。当一个类加载器被垃圾回收器标记为不再存活,其对应的元空间会被回收。在元空间的回收过程中没有重定位和压缩等操作。但是元空间内的元数据会进行扫描来确定Java引用。

元空间虚拟机负责元空间的分配,其采用的形式为组块分配。组块的大小因类加载器的类型而异。在元空间虚拟机中存在一个全局的空闲组块列表。当一个类加载器需要组块时,它就会从这个全局的组块列表中获取并维持一个自己的组块列表。当一个类加载器不再存活,那么其持有的组块将会被释放,并返回给全局组块列表。类加载器持有的组块又会被分成多个块,每一个块存储一个单元的元信息。组块中的块是线性分配(指针碰撞分配形式)。组块分配自内存映射区域。这些全局的虚拟内存映射区域以链表形式连接,一旦某个虚拟内存映射区域清空,这部分内存就会返回给操作系统

上图展示的是虚拟内存映射区域如何进行元组块的分配。类加载器1和3表明使用了反射或者为匿名类加载器,他们使用了特定大小组块。 而类加载器2和4根据其内部条目的数量使用小型或者中型的组块。

元空间调优与工具

正如上面提到的,元空间虚拟机控制元空间的增长。但是有些时候我们想限制其增长,比如通过显式在命令行中设置-XX:MaxMetaspaceSize。默认情况下,-XX:MaxMetaspaceSize的值没有限制,因此元空间甚至可以延伸到交换区,但是这时候当我们进行本地内存分配时将会失败。

对于一个64位的服务器端JVM来说,其默认的–XX:MetaspaceSize值为21MB。这就是初始的高水位线。一旦触及到这个水位线,Full GC将会被触发并卸载没有用的类(即这些类对应的类加载器不再存活),然后这个高水位线将会重置。新的高水位线的值取决于GC后释放了多少元空间。如果释放的空间不足,这个高水位线则上升。如果释放空间过多,则高水位线下降。如果初始化的高水位线设置过低,上述高水位线调整情况会发生很多次。通过垃圾回收器的日志我们可以观察到Full GC多次调用。为了避免频繁的GC,建议将–XX:MetaspaceSize设置为一个相对较高的值。

经过多次GC之后,元空间虚拟机自动调节高水位线,以此来推迟下一次垃圾回收到来。

有这样两个选项 ‑XX:MinMetaspaceFreeRatio和‑XX:MaxMetaspaceFreeRatio,他们类似于GC的FreeRatio选项,用来设置元空间空闲比例的最大值和最小值。我们可以通过命令行对这两个选项设置对应的值。

下面是一些改进的工具,用来获取更多关于元空间的信息。

  • jmap -clstats PID 打印类加载器数据。(-clstats是-permstat的替代方案,在JDK8之前,-permstat用来打印类加载器的数据)。下面的例子输出就是DaCapo’s Avrora benchmark程序的类加载器数据

    $ jmap -clstats
    Attaching to process ID 6476, please wait...
    Debugger attached successfully.
    Server compiler detected.
    JVM version is 25.5-b02
    finding class loader instances ..done.
    computing per loader stat ..done.
    please wait.. computing liveness.liveness analysis may be inaccurate ...
    class_loader classes      bytes parent_loader     alive? type       655  1222734     null      live  
    0x000000074004a6c0    0    0    0x000000074004a708    dead      java/util/ResourceBundle$RBClassLoader@0x00000007c0053e20
    0x000000074004a760    0    0      null      dead      sun/misc/Launcher$ExtClassLoader@0x00000007c002d248
    0x00000007401189c8     1     1471 0x00000007400752f8    dead      sun/reflect/DelegatingClassLoader@0x00000007c0009870
    0x000000074004a708    116   316053    0x000000074004a760   dead      sun/misc/Launcher$AppClassLoader@0x00000007c0038190
    0x00000007400752f8    538  773854    0x000000074004a708   dead      org/dacapo/harness/DacapoClassLoader@0x00000007c00638b0
    total = 6      1310   2314112           N/A       alive=1, dead=5     N/A    
  • jstat -gc LVMID 用来打印元空间的信息,具体内容如下

  • jcmd PID GC.class_stats 一个新的诊断命令,用来连接到运行的JVM并输出详尽的类元数据的柱状图。

注意:在JDK 6 build 13下,需要加上‑XX:+UnlockDiagnosticVMOptions 才能正确使用jcmd这个命令。

$ jcmd  help GC.class_stats
9522:
GC.class_stats
Provide statistics about Java class meta data. Requires -XX:+UnlockDiagnosticVMOptions. Impact: High: Depends on Java heap size and content.  Syntax : GC.class_stats [options] []  Arguments:
     columns : [optional] Comma-separated list of all the columns to show. If not specified, the following columns are shown: InstBytes,KlassBytes,CpAll,annotations,MethodCount,Bytecodes,MethodAll,ROAll,RWAll,Total (STRING, no default value) Options: (options must be specified using the or = syntax)
     -all : [optional] Show all columns (BOOLEAN, false)
     -csv : [optional] Print in CSV (comma-separated values) format for spreadsheets (BOOLEAN, false)
     -help : [optional] Show meaning of all the columns (BOOLEAN, false)

提示:如果想了解字段的更多信息,请访问这里

使用jcmd的示例输出:

 $ jcmd  GC.class_stats 

7140:
Index Super InstBytes KlassBytes annotations   CpAll MethodCount Bytecodes MethodAll   ROAll   RWAll   Total ClassName
    1    -1    426416        480           0       0           0         0         0      24     576     600 [C
    2    -1    290136        480           0       0           0         0         0      40     576     616 [Lavrora.arch.legacy.LegacyInstr;
    3    -1    269840        480           0       0           0         0         0      24     576     600 [B
    4    43    137856        648           0   19248         129      4886     25288   16368   30568   46936 java.lang.Class
    5    43    136968        624           0    8760          94      4570     33616   12072   32000   44072 java.lang.String
    6    43     75872        560           0    1296           7       149      1400     880    2680    3560 java.util.HashMap$Node
    7   836     57408        608           0     720           3        69      1480     528    2488    3016 avrora.sim.util.MulticastFSMProbe
    8    43     55488        504           0     680           1        31       440     280    1536    1816 avrora.sim.FiniteStateMachine$State
    9    -1     53712        480           0       0           0         0         0      24     576     600 [Ljava.lang.Object;
   10    -1     49424        480           0       0           0         0         0      24     576     600 [I
   11    -1     49248        480           0       0           0         0         0      24     576     600 [Lavrora.sim.platform.ExternalFlash$Page;
   12    -1     24400        480           0       0           0         0         0      32     576     608 [Ljava.util.HashMap$Node;
   13   394     21408        520           0     600           3        33      1216     432    2080    2512 avrora.sim.AtmelInterpreter$IORegBehavior
   14   727     19800        672           0     968           4        71      1240     664    2472    3136 avrora.arch.legacy.LegacyInstr$MOVW


1299  1300         0        608           0     256           1         5       152     104    1024    1128 sun.util.resources.LocaleNamesBundle
 1300  1098         0        608           0    1744          10       290      1808    1176    3208    4384 sun.util.resources.OpenListResourceBundle
 1301  1098         0        616           0    2184          12       395      2200    1480    3800    5280 sun.util.resources.ParallelListResourceBundle
              2244312     794288        2024 2260976       12801    561882   3135144 1906688 4684704 6591392 Total
                34.0%      12.1%        0.0%   34.3%           -      8.5%     47.6%   28.9%   71.1%  100.0%
Index Super InstBytes KlassBytes annotations   CpAll MethodCount Bytecodes MethodAll   ROAll   RWAll   Total ClassName

存在的问题

前面已经提到,元空间虚拟机采用了组块分配的形式,同时区块的大小由类加载器类型决定。类信息并不是固定大小,因此有可能分配的空闲区块和类需要的区块大小不同,这种情况下可能导致碎片存在。元空间虚拟机目前并不支持压缩操作,所以碎片化是目前最大的问题。

关于作者

Monica Beckwith是一位在硬件行业有着10多年经验的性能研究工程师。她目前在Servergy公司任性能架构师一职。该公司为一家提供高效服务器的创业公司。此外,Monica曾在Sun,Oracle和AMD等公司致力于服务器端JVM优化。Monica还是JavaOne 2013会议的演讲嘉宾。想要关注的可以在twitter上查找@mon_beck。

查看英文原文:Where Has the Java PermGen Gone?

Java永久代去哪儿了的更多相关文章

  1. (翻译)什么是Java的永久代(PermGen)内存泄漏

    http://www.codelast.com/?p=7248 转载请注明出处:http://www.codelast.com/ 本文是我对这篇文章的翻译:What is a PermGen leak ...

  2. 深入理解java虚拟机---JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)(十二)

    引用:https://www.cnblogs.com/yulei126/p/6777323.html JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)   1.背景 2.为什么废 ...

  3. 牛客网Java刷题知识点之垃圾回收算法过程、哪些内存需要回收、被标记需要清除对象的自我救赎、对象将根据存活的时间被分为:年轻代、年老代(Old Generation)、永久代、垃圾回收器的分类

    不多说,直接上干货! 首先,大家要搞清楚,java里的内存是怎么分配的.详细见 牛客网Java刷题知识点之内存的划分(寄存器.本地方法区.方法区.栈内存和堆内存) 哪些内存需要回收 其实,一般是对堆内 ...

  4. java虚拟机-JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)

    一.背景 1.1 永久代(PermGen)在哪里? 根据,hotspot jvm结构如下(虚拟机栈和本地方法栈合一起了): 上图引自网络,但有个问题:方法区和heap堆都是线程共享的内存区域. 关于方 ...

  5. Java堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)?

    JVM的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存.它在JVM启动的时候被创建.对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收. 堆内存是由存活和死亡的对象组成的.存活的对象是应 ...

  6. java GC jvm 内存分布 和新生代,老年代,永久代,(详细)

    如果大家想深入的了解JVM,可以读读周志明<深入理解Java虚拟机:JVM高级特性与最佳实践> 需要掌握的东西,包括以下内容.判断对象存活还是死亡的算法(引用计数算法.可达性分析算法).常 ...

  7. 【转】Java中的新生代、老年代、永久代和各种GC

    JVM中的堆,一般分为三大部分:新生代.老年代.永久代: 1 新生代 主要是用来存放新生的对象.一般占据堆的1/3空间.由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收. 新生代又分为 ...

  8. Java方法区和永久代

    Java方法区和永久代 目前有三大Java虚拟机:HotSpot,oracle JRockit,IBM J9. JRockit是oracle发明的,用于其WebLogic服务器,IBM JVM是IBM ...

  9. java 8中撤销永久代,引入元空间

    撤销永久代,引入元空间: 在 JDK 1.8 中,HotSpot 已经没有 “PermGen space”这个空间了,取而代之是一个叫做 Metaspace(元空间) 的东西. Java7中已经将字符 ...

随机推荐

  1. Linux多线程实践(10) --使用 C++11 编写 Linux 多线程程序

    在这个多核时代,如何充分利用每个 CPU 内核是一个绕不开的话题,从需要为成千上万的用户同时提供服务的服务端应用程序,到需要同时打开十几个页面,每个页面都有几十上百个链接的 web 浏览器应用程序,从 ...

  2. XBMC源代码分析 7:视频播放器(dvdplayer)-输入流(以libRTMP为例)

    前文分析了XBMC的基本结构: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 XBMC源代码分 ...

  3. HTML移动开发参考

    小强的HTML5移动开发之路 http://blog.csdn.net/dawanganban/article/details/17591373 其他: http://blog.csdn.net/gf ...

  4. Intellij Idea配置提高速度

    主要介绍一下Intellij Idea的关于速度和最大最大方法数目 提高速度 1.命令: 2.找到./Library/Preferences/IntelliJIdea2016.1/idea.vmopt ...

  5. css3学习之旅-css的基本语法(1)

    后面就将要介绍css的全面语法: 1.css介绍 2.css基本语法 3.css高级语法 4.css派生选择器 5.css的id选择器 6.css类选择器 7.css属性选择器 !!!!!css介绍 ...

  6. mysql进阶(二)索引简易教程

    Mysql索引简易教程 基本概念 索引是指把你设置为索引的字段A的内容储存在一个独立区间S里,里面只有这个字段的内容.在找查这个与这个字段A的内容时会直接从这个独立区间里查找,而不是去到数据表里查找. ...

  7. 从"按层次输出二叉树"到"求解二叉树深度"的总结

    本文是在学习中的总结,欢迎转载但请注明出处:http://write.blog.csdn.net/postedit/41964669 最近在刷LeetCode上的算法题,发现好多题目的解题思路大体是一 ...

  8. wing带你玩转自定义view系列(2) 简单模仿qq未读消息去除效果

    上一篇介绍了贝塞尔曲线的简单应用 仿360内存清理效果 这一篇带来一个  两条贝塞尔曲线的应用 : 仿qq未读消息去除效果. 转载请注明出处:http://blog.csdn.net/wingicho ...

  9. Xcode相关常用快捷键搜集

    command + L:  跳转到指定行     control + i:  格式化代码 command + control + 上/下 在*.h和*.m之间切换. command + control ...

  10. AngularJS进阶(二十三)ANGULAR三宗罪之版本陷阱

    ANGULAR三宗罪之版本陷阱 坑!碰到个大坑,前面由于绑定日期时将angular版本换为angular-1.3.0-beta.1时,后来午睡后,登录系统,发现无论如何都登陆不进去了,经过调试,发现数 ...