内容来着《Java 程序员面试笔试宝典 第2版》
在JDK1.8以前的版本中,由于类大多是“static”的,很少被卸载或收集,因此这部分数据被称为“永久的(Permanent)”。同时,因为类class是JVM实现的一部分,而不是由应用创建的,所以又被认为是“非堆(non-heap)”内存。在JDK1.8之前的HotSpot JVM中,存放这些“永久的”的区域被称为“永久代(Permanent Generation)”。“永久代”是一片连续的堆空间。
从JDK1.7开始,HopSpot JVM已经逐步开始把永久代的数据向其他存储空间转移了,例如在JDK1.7中把字符串常量池从永久代转移到了JVM的堆空间中,但是永久代并没有完全被移除。从JDK1.8开始彻底把永久代从JVM中移除了,而把类的元数据放到本地化的堆内存(native heap)中,这一块本地化的堆内存区域被称为叫Metaspace(元空间)。为什么要移除永久代呢?主要有以下几个原因:
1)由于Permanent Generation内存经常不够用或发生内存泄漏,而抛出异常:java.lang.OutOfMemoryError:PermGen。尤其是在JavaWeb开发的时候经常需要动态生成类,而永久代又是一块非常小的存储空间,动态生成过多的类会导致永久代的空间被用完而导致上述异常的出现。显然元空间有非常大的存储空间,因此从一定程度上可以 避免这个问题。当然,永久代的移除并不意味着内存泄漏的问题就没有了。因此,仍然需要监控内存的消耗,因为内存泄漏仍然会耗尽整个本地内存。
2)移除永久代可以促进HotSpot JVM与JRockit VM的融合,因为JRockit没有永久代。
3)在HotSpot中,每个垃圾回收器都需要专门的代码来处理存储在PermGen中的类的元数据信息。从把类的元数据从永久代转移到Metaspace后,由于Metaspace的分配具有和Java Heap相同的地址空间,因此可以实现Metaspace和Java Heap的无缝化管理,而且简化了FullGC的过程,以至将来可以并行的对元数据信息进行垃圾收集,而没有GC暂停。
Metaspace是如何进行内存分配的呢?
Metaspace VM通过借鉴内存管理的方式来管理Metaspace,把原来由多个垃圾回收器完成的工作全部移到Metaspace VM(由C++实现的)上了。Metaspace VM实现垃圾回收的思想非常简单:类与类加载器有着相同的生命周期,也就是说,只要类加载器还存活,在Metaspace中存储的类的元数据就不能被释放。
Metaspace VM通过一个块分配器来管理Metaspace内存的分配。块的大小取决于类加载器的类型。Metaspace VM维护着一个全局的可使用的块列表。当一个类加载器需要一个块的时候,它会从这个全局块列表中取走一个块,然后添加到它自己维护的块列表中。当类加载器的生命周期结束的时候,它的块将会被释放,从而把申请的块归还给全局的块列表。每个块又被分成多个block,每个block存储一个元数据单元。