JVM007--JVM8为什么要增加元空间
虚拟机栈,本地方法栈以及程序计数器为线程隔离。方法区和堆是所有线程共享的数据区域。
方法区用于存储已被虚拟机加载的类信息、常量、静态变量、动态生成的类等数据。实际上在Java虚拟机的规范中方法区是堆中的一个逻辑部分,但是它却拥有一个叫做非堆(Non-Heap)的别名。
对于方法区的实现,不同虚拟机中策略也不同。以我们常用的HotSpot虚拟机为例,其设计团队使用永久带来实现方法区,并把GC的分代收集扩展至永久带。这样设计的好处就是能够省去专门为方法区编写内存管理的代码。但是在实际的场景中,这样的实现并不是一个好的方式,因为永久带有MAX上限,所以这样做会更容易遇到内存溢出问题。
关于方法区的GC回收,Java虚拟机规范并没有严格的限制。虚拟机在实现中可以自由选择是否实现垃圾回收。主要原因还是方法区内存回收的效果比较难以令人满意,方法区的内存回收主要是针对常量池(1.7已经将常量池逐步移除方法区)以及类型的卸载,但是类型卸载在实际场景中的条件相当苛刻。
另外还需要注意的是在HotSpot虚拟机中永久带和堆虽然相互隔离,但是他们的物理内存是连续的。而且老年代和永久带的垃圾收集器进行了捆绑,因此无论谁满了都会触发永久带和老年带的GC。
因此在JDK 1.8中,HotSpot虚拟机已经将方法区(永久带)移除,取而代之的就是元空间。
Java1.8的运行时数据区域如图所示。方法区已经不见了踪影,多出来的是叫做元数据区(元空间)的区域。
元空间在1.8中不在与堆是连续的物理内存,而是改为使用本地内存(Native memory)。元空间使用本地内存也就意味着只要本地内存足够,就不会出现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释放空间导致的垃圾收集。