您的位置  > 互联网

Java虚拟机规范中方法区的内存回收效果比较难以令人满意

其中,虚拟机栈、本地方法栈和程序计数器是线程隔离的。 方法区和堆是所有线程共享的数据区域。

这里我们主要要介绍的是方法区。

方法区用于存储类信息、常量、静态变量、动态生成的类以及虚拟机已经加载的其他数据。 事实上,在Java虚拟机的规范中,方法区是堆的逻辑部分,只不过它有一个别名,叫做Non-Heap。

关于方法区的实现,不同虚拟机中的策略也不同。 以我们常用的虚拟机为例,设计团队使用永久带来实现方法区,并将GC的分代收集扩展到永久带。 这种设计的优点是不需要专门为方法区编写内存管理代码。 但在实际场景中,这种实现方式并不是一个好办法,因为MAX有一个永久的上限,所以这样做会更容易遇到内存溢出问题。

关于方法区的GC回收,Java虚拟机规范并没有严格的限制。 虚拟机在执行时可以自由选择是否执行垃圾收集。 主要原因是方法区内存回收的效果比较不理想。 方法区的内存回收主要是针对常量池(1.7中已经逐渐将常量池从方法区中移除)和类型的卸载。 然而,类型卸载在实际场景中并不有效。 条件相当苛刻。

另外,需要注意的是,虽然永久带和堆在虚拟机中是相互隔离的,但它们的物理内存是连续的。 而且老年代和永久区垃圾收集器是捆绑在一起的,所以无论谁满了,都会触发永久区和老年代GC。

因此,在Java 1.8中,虚拟机已经去掉了方法区(永久区),代之以元空间。

Java1.8的运行时数据区如图所示。 方法区消失了,剩下的是一个叫做元数据区的区域。

1.8中的元空间不再与物理内存中的堆连续,而是使用本地内存()。 使用本地内存,这意味着只要本地内存足够,就不会出现OOM错误。

默认情况下,元空间大小是无限的,但 JVM 还提供了参数来控制其使用:

-XX:MetaspaceSize
    class metadata的初始空间配额,以bytes为单位,达到该值就会触发垃圾收集进行类型卸载,
    同时GC会对该值进行调整:如果释放了大量的空间,就适当的降低该值;如果释放了很少的空间,
    那么在不超过MaxMetaspaceSize(如果设置了的话),适当的提高该值。
-XX:MaxMetaspaceSize
    可以为class metadata分配的最大空间。默认是没有限制的。
-XX:MinMetaspaceFreeRatio
    在GC之后,最小的Metaspace剩余空间容量的百分比,减少为class metadata分配空间导致的垃圾收集。
-XX:MaxMetaspaceFreeRatio
    在GC之后,最大的Metaspace剩余空间容量的百分比,减少为class metadata释放空间导致的垃圾收集。