JVM-方法区

1 方法区介绍

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域。方法区在JVM启动的时候被创建,并且它的实际的物理内存和Java堆区一样都可以是不连续的。关闭JVM就会释放这个区域的内存。

方法区的大小也可以选择固定大小或者可扩展。方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:OOM。

在jdk7及以前,习惯上把方法区称为永久代。jdk8开始,使用元空间取代了永久代。本质上,方法区和永久代并不等价,仅是对hostspot而言的。元空间和永久代最大的区别:元空间不在JVM设置的内存中,而是使用本地内存。

2 方法区的作用

方法区:用于存储已被虚拟机加载的类型信息、常量、静态变量、即使编译器后的代码缓存等。

类型信息:类型的完整有效名称 (全类名=包名.类名)、类型的直接父类的完整有效名、类型的修饰符、类型直接接口的一个有序列表。

全局静态常量在编译的时候就已经分配内存的,无需类加载,所以即使没有类实例也可以访问

运行时常量池:方法区中,内部包含了运行时常量池,字节码文件中内部包含了常量池。运行时常量池在加载类和接口到虚拟机后,就会创建对应的运行时常量池。JVM为每个已加载的类型都维护一个常量池,池中的数据项像数组项一样是通过索引访问的。运行常量池中包含多种不用的常量,包括编译期就已经明确的数组字面量,也包括到运行期解析后才能获得的方法或者字段引用,此时不再是常量池的符号地址,而是真实地址。运行常量池,相对于class文件常量池的另一个重要特征是:具备动态性。当创建类或接口的运行时常量池时,如果构造器运行时常量池所需的内存空间超过了方法区所能提供的最大值,则JVM会抛OutOfMemoryError异常。

运行时常量池的作用:一个Java源文件中的类、接口,编译后产生一个字节码文件。而Java中的字节码文件需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,此时可以通过常量池,而字节码包含指向常量池的引用。在动态链接的时候会用到运行时常量池。

常量池可以看做是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型。这部分内容在类加载后存放到方法区的运行时常量池中。

3 方法区的演变

Hotspot的方法区演进

jdk版本描述
JDK1.6有永久代(permanent generation),静态变量存放在永久代上
JDK1.7有永久代,但已经逐步”去永久代”,字符串常量池、静态变量保存在堆中
JDK1.8无永久代,类型信息、字段、方法、常量保存在本地内存的元空间,字符串常量池、静态变量保存在堆中

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

永久代是在JVM内存中的,永久代的空间大小很难确定,不知道该设置多大合适。而元空间在本地内存,默认情况下,元空间的大小仅受本地内存限制。而且永久代调优比较困难,因为永久代中的某个类型信息是否被回收需要判断的条件很多,所以当对某个类型信息的回收是很消耗性能的。

StringTable为什么要从永久代中调整到堆中?

永久代的回收效率很低,在Full GC的时候才会触发。而Full GC是老年代的空间不足、永久代空间不足时才会触发。这就导致StringTable回收效率不高,而我们开发中会有大量的字符串被创建,回收效率低永久代空间也就会不足。如果是放在堆中,就能及时回收内存。

4 方法区的GC

方法区的垃圾回收主要回收:常量池中废弃的常量和不再使用的类型

  • 常量池之中主要存放两大类常量:字面量和符号引用。字面量比较接近Java语言层次的常量概念,如文本字符串、被final修饰的常量等。 符号引用则属于编译原理方面的概念,主要包括:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。Hotspot虚拟机对常量池的回收策略是:只要常量池中的常量没有被任何地方引用,就可以被回收(与回收Java堆中对象非常类似)。
  • 类是否被允许回收,需要满足下面三个条件:
    • 该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。

    • 加载该类的类加载器已经被回收,这个条件非常难以达成。

    • 该类对应的java.lang.Class对象没有在任何敌方被引用,无法在任何地方通过反射访问该类的方法。

5 方法区的参数设置

方法区的大小可以是固定的也可以是可扩展的,JVM可以根据应用的需要动态调整:

JDK7

  • 通过-XX:PermSize来设置永久代初始分配空间,默认值是20.75M
  • 通过-XX:MaxPermSize来设置永久代最大可分配空间,32位机器默认是64M,64位机器默认是82M

JDK8

  • 通过-XX:MetaspaceSize设置元空间的初始分配空间,默认值是21M
  • 通过-XX:MaxMetaspaceSize设置元空间最大可分配空间,默认值是-1,即没有限制

注意:为了避免频繁的GC,建议将-XX:MetaspaceSize设置为一个相对较高的值。因为防止初始化的高水位线设置过低,导致高水位线调整情况会发生很多次,导致性能下降。


 参考:尚硅谷宋红康JVM全套教程(详解java虚拟机)_哔哩哔哩_bilibili

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值