Davids原理探究:JDK8将永久代(PermGen)替换为元空间(MetaSpace)的原因

JDK8将永久代(PermGen)替换为元空间(MetaSpace)的原因

关注可以查看更多粉丝专享blog~

什么是方法区

方法区(Method Area)和Java堆一样,是各个线程共享的内存区域,它用于存储已经被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。虽然《Java虚拟机规范》中把方法区描述为堆的一个逻辑部分,但是它却有着一个别名叫做“非堆”(Non-Heap),目的是与Java堆区分开。

永久代产生的原因

说到方法区不得不提一下“永久代”这个概念,尤其是在JDK8之前,许多Java程序员都习惯在HotSpot虚拟机上开发、部署程序,很多人都更愿意把方法区称呼为“永久代”(Permanent Generation),或将二者混为一谈。本质二者并不是等价的,因为仅仅是当时的HotSpot虚拟机设计团队选择把收集器的分代设计扩展至方法区,或者说使用永久代来实现方法区(J9和JRockit虚拟机是没有永久代这个概念的,永久代相当于HotSpot针对于《Java虚拟机规范》中方法区的一种实现方式,如何实现方法区属于虚拟机实现细节,不受《Java虚拟机规范》管束,并不统一要求),这样HotSpot的垃圾回收器就能够像管理Java堆一样管理这部分内存,省去专门为方法区编写内存管理代码的工作。

将永久代替换为源空间的原因

  1. Oracle收购了BEA获得了JRockit的所有权后,准备把JRockit中的优秀功能,譬如Java Mission Control管理工具,移植到HotSpot虚拟机时,因为两者对方法区实现的差异而面临诸多困难。考虑到HotSpot未来的发展,在JDK6的时候HotSpot开发团队就有放弃永久代,逐步改为本地内存(Native Memory)来实现方法区的计划了。而到了JDK7,已经把原本放在永久代的字符串常量、静态变量等移出;到了JDK8,终于完全废弃了永久代的概念,改用与JRockit、J9一样的本地内存中实现元空间(Metaspace)来代替,把JDK7中永久代剩余的内容(主要是类型信息)全部移到元空间中。
  2. 永久代这种设计导致了Java应用更容易遇到内存溢出的问题(永久代有-XX:MaxPermSize的上限,即使不设置也有默认大小,而J9和JRockit只要没有触碰到进程可用上限,例如32位系统中的4GB限制,就不会出问题。)
  3. 有极少数方法(例如String::intern())会因为永久代的原因而导致不同虚拟机下有不同的表现
  4. 《Java虚拟机规范》对方法区的约束非常宽松,除了和Java堆一样不需要连续的内存和可以选择固定大小或者可扩展外,甚至还可以选择不实现垃圾回收。

相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入了方法区就如永久代的名字一样,“永久”存在了。这区域的内存回收目标主要是针对常量池和对类型的卸载,一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻,但是这部分区域的回收有时又确实是有必要的。以前Sun公司的Bug list中,曾出现过的若干个严重Bug就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。
类型卸载条件(满足以下三个条件代表可以回收,但是不一定会回收):

  1. 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
  2. 加载该类的 ClassLoader 已经被回收。
  3. 该类对应的 java.lang.Class 对象没有在任何地⽅被引⽤,⽆法在任何地⽅通过反射访问该类
    的⽅法

根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。

参考文献:
《深入理解Java虚拟机》(第三版)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章