【JVM之内存与垃圾回收篇】方法区 (4)

【JVM之内存与垃圾回收篇】方法区

最后调用 invokevirtual(虚方法调用),然后返回

【JVM之内存与垃圾回收篇】方法区

返回时

【JVM之内存与垃圾回收篇】方法区

程序计数器始终计算的都是当前代码运行的位置,目的是为了方便记录方法调用后能够正常返回,或者是进行了 CPU 切换后,也能回来到原来的代码进行执行。

方法区的演进细节

首先明确:只有 Hotspot 才有永久代。BEA JRockit、IBMJ9 等来说,是不存在永久代的概念的。原则上如何实现方法区属于虚拟机实现细节,不受《Java 虚拟机规范》管束,并不要求统一

Hotspot 中方法区的变化:

JDK 版本 方法区的变化
JDK1.6 及以前   有永久代,静态变量存储在永久代上  
JDK1.7   有永久代,但已经逐步“去永久代”,字符串常量池,静态变量移除,保存在堆中  
JDK1.8   无永久代,类型信息,字段,方法,常量保存在本地内存的元空间,但字符串常量池、静态变量仍然在堆中。  

JDK6 的时候

【JVM之内存与垃圾回收篇】方法区

JDK7 的时候

【JVM之内存与垃圾回收篇】方法区

JDK8 的时候,元空间大小只受物理内存影响

【JVM之内存与垃圾回收篇】方法区

为什么永久代要被元空间替代?

官方解释:

JRockit 是和 HotSpot 融合后的结果,因为 JRockit 没有永久代,所以他们不需要配置永久代

随着 Java8 的到来,HotSpot VM 中再也见不到永久代了。但是这并不意味着类的元数据信息也消失了。这些数据被移到了一个与堆不相连的本地内存区域,这个区域叫做元空间(Metaspace)。

由于类的元数据分配在本地内存中,元空间的最大可分配空间就是系统可用内存空间,这项改动是很有必要的,原因有:

为永久代设置空间大小是很难确定的。
在某些场景下,如果动态加载类过多,容易产生 Perm 区的 OOM。比如某个实际 Web 工
程中,因为功能点比较多,在运行过程中,要不断动态加载很多类,经常出现致命错误。
“Exception in thread‘dubbo client x.x connector'java.lang.OutOfMemoryError:PermGen space”
而元空间和永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。
因此,默认情况下,元空间的大小仅受本地内存限制。

对永久代进行调优是很困难的。

主要是为了降低 Full GC

有些人认为方法区(如 HotSpot 虚拟机中的元空间或者永久代)是没有垃圾收集行为的,其实不然。《Java 虚拟机规范》对方法区的约束是非常宽松的,提到过可以不要求虚拟机在方法区中实现垃圾收集。事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如 JDK11 时期的 ZGC 收集器就不支持类卸载)。
一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻。但是这部分区域的回收有时又确实是必要的。以前 sun 公司的 Bug 列表中,曾出现过的若干个严重的 Bug 就是由于低版本的 HotSpot 虚拟机对此区域未完全回收而导致内存泄漏

方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不在使用的类型

StringTable为什么要调整位置

jdk7 中将 StringTable 放到了堆空间中。因为永久代的回收效率很低,在 full gc 的时候才会触发。而 full gc 是老年代的空间不足、永久代不足时才会触发。
这就导致 stringTable 回收效率不高。而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足。放到堆里,能及时回收内存。

静态变量存放在那里?

静态引用对应的对象实体始终都存在堆空间

可以使用 jhsdb.ext,需要在 jdk9 的时候才引入的

staticObj 随着 Test 的类型信息存放在方法区,instanceObj 随着 Test 的对象实例存放在 Java 堆,localObject 则是存放在 foo() 方法栈帧的局部变量表中。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwsyjj.html