方法区:jdk1.6,jdk1.7,jdk1.8下的方法区变迁插图

JDK1.7及以前,HotSpot虚拟机将java类信息、常量池、静态变量、即时编译器编译后的代码等数据,存储在Perm(永久带)里(对于其他虚拟机如BEA JRockit、IBM J9等是不存在永久带概念的),类的元数据和静态变量在类加载的时候被分配到Perm里,当常量池回收或者类被卸载的时候,垃圾收集器会回收这一部分内存,但效果不太理想。

JDK1.8时,HotSpot虚拟机对JVM模型进行了改造,将类元数据放到了本地内存中,将常量池静态变量放到了Java里,HotSpot VM将会为类的元数据明确的分配与释放本地内存,
在这种架构下,类元数据就突破了-XX:MaxPermSize的限制,所以此配置已经失效,现在可以使用更多的本地内存。这样一定程度上解决了原来在运行时生成大量的类,从而经常Full GC的问题——如运行时使用反射、代理等。

jdk7版本以前的实现

方法区:jdk1.6,jdk1.7,jdk1.8下的方法区变迁插图1

jdk7版本的改动是把字符串常量池移到了堆中。

jdk8 MetaSpace

jdk1.8中则把永久代给完全删除了,取而代之的是MetaSpace

方法区:jdk1.6,jdk1.7,jdk1.8下的方法区变迁插图2

运行时常量池和静态变量都存储到了堆中,MetaSpace存储类的元数据,MetaSpace直接申请在本地内存中(Native memory),这样类的元数据分配只受本地内存大小的限制,OOM问题就不存在了。除此之外,还有其他很多好处:

  • Take advantage of Java Language Specification property : Classes and associated metadata lifetimes match class loader’s
  • Linear allocation only
  • No individual reclamation (except for RedefineClasses and class loading failure)
  • No GC scan or compaction
  • No relocation for metaspace objects

 jdk1.8中,常量池和静态变量都存储到了堆中,可以通过《JVM体系结构之七:持久代、元空间(Metaspace) 常量池==了解String类的intern()方法、常量池介绍、常量池从Perm–>Heap》中示例说明。其它元数据信息存放在元空间内,可通过《JVM异常之:方法区溢出OutOfMemoryError: PermGen space》的示例说明。

方法区:jdk1.6,jdk1.7,jdk1.8下的方法区变迁插图3

为什么jdk1.8要把方法区从JVM里(永久代)移到直接内存(元空间)

原因一:因为直接内存,JVM将会在IO操作上具有更高的性能,因为它直接作用于本地系统的IO操作。而非直接内存,也就是堆内存中的数据,如果要作IO操作,会先复制到直接内存,再利用本地IO处理。
    从数据流的角度,非直接内存是下面这样的作用链:本地IO –> 直接内存 –> 非直接内存 –> 直接内存 –> 本地IO
    而直接内存是:本地IO –> 直接内存 –> 本地IO
原因二:整个永久代有一个 JVM 本身设置固定大小上线,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,并且永远不会得到java.lang.OutOfMemoryError。
    可以使用 -XX:MaxMetaspaceSize 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。
    -XX:MetaspaceSize 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。



方法区:jdk1.6,jdk1.7,jdk1.8下的方法区变迁插图4

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:http://www.choupangxia.com/2020/08/28/jdk1-6-jdk1-7-jdk1-8/