方法区

1、堆、栈、方法区的交互关系

在这里插入图片描述

2、方法区基本概述

  • 方法区(Method Area) 与Java堆一样,是各个线程共享的内存区域。
  • 方法区在JVM启动的时候被创建,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的。
  • 方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展。
  • 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:
    java.lang.OutOfMemoryError:PermGen space(JDK7及之前)或者java.lang.OutOfMemoryError: Metaspace(JDK8及之后)

加载大量的第三方的jar包;
Tomcat部署的工程过多(30-50个) ;
大量动态的生成反射类

  • 关闭JVM就会释放这个区域的内存。
    在这里插入图片描述
    永久代使用的是jvm的内存,而元空间使用的是本地内存。 更好的防止了OOM。

3、方法区大小设置

JDK8+

  • 元数据区大小可以使用参数-XX :MetaspaceSize和-XX :
    MaxMetaspaceSize指定,替代上述原有的两个参数(JDK7 -XX:PermSize、-XX:MaxPermSize)。
  • 默认值依赖于平台。windows下,-XX :MetaspaceSize是21M,-XX
    :MaxMetaspaceSize的值是-1,即没有限制。
  • 与永久代不同,如果不指定大小,默认情况下,虚拟机会耗尽所有的可用系统内存。
  • 如果元数据区发生溢出,虚拟机一样会抛出异常OutOfMemoryError: Metaspace
  • -XX :MetaspaceSize:设置初始的元空间大小。对于一个64位的服务器端JVM来说,其默认的-XX :MetaspaceSize值为21MB.这就是初始的高水位线,一旦触及这个水位线,FullGC将会被触发并卸载没用的类(即这些类对应的类加载器不再存活),然后这个高水位线将会重置。新的高水位线的值取决于GC后释放了多少元空间。如果释放的空间不足,那么在不超MaxMetaspaceSize时,适当提高该值。如果释放空间过多,则适当降低该值。如果初始化的高水位线设置过低,上述高水位线调整情况会发生很多次。通过垃圾回收器的日志可以观察到Full GC多次调用。为了避免频繁地GC,建议将-XX:MetaspaceSize设置为一个相对较高的值。

4、方法区内部结构

在这里插入图片描述
在这里插入图片描述

类型信息
对每个加载的类型( 类class、接口interface、枚举enum、注解annotation),JVM必
须在方法区中存储以下类型信息:
①这个类型的完整有效名称(全名=包名.类名)
②这个类型直接父类的完整有效名(对于interface或是java.lang.object,都没有父类)
③这个类型的修饰符(public, abstract, final的某个子集)
④这个类型直接接口的一个有序列表

域(Field)信息
JVM必须在方法区中保存类型的所有域的相关信息以及域的声明顺序。
域的相关信息包括:域名称、域类型、域修饰符(public, private,protected, static, final, volatile, transient的某个子集)

方法信息
JVM必须保存所有方法的以下信息,同域信息一样包括声明顺序:

  • 方法名称
  • 方法的返回类型(或void)
  • 方法参数的数量和类型(按顺序)
  • 方法的修饰符(public, private, protected, static, final,synchronized,
    native, abstract的一个子集)
  • 方法的字节码(bytecodes)、操作数栈、局部变量表及大小( abstract和native方法除外)
  • 异常表( abstract和native方法除外)

➢每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引

常量池VS运行时常量池
方法区,内部包含了运行时常量池。
.class字节码文件,内部包含了常量池。

常量池经过类加载后,会将常量池的字面量与符号引用存放到方法区的运行时常量池中,并将符号地址转为真实地址。
要弄清楚方法区,需要理解清楚ClassFile,因为加载类的信息都在方法区。
要弄清楚方法区的运行时常量池,需要理解清楚ClassFile中的常量池。

5、non-final的类变量

  • 静态变量和类关联在一起,随着类的加载而加载,它们成为类数据在逻辑上的一部分
  • 类变量被类的所有实例共享,即使没有类实例时你也可以访问它。
public class MethodAreaTest{
    public static void main(string[] args) {
            Order order = null;
            order.hello();
            //虽然order实例为空,但还是可以访问类中的静态变量
            system.out.println(order.count);
        }
    }
class Order{
    public static int count = 1;
    public static final int num = 2;
    public static void hello(){
        System. out. println("hello!");
    }
}

补充说明:全局常量: static final
被声明为final的类变量的处理方法则不同,每个全局常量在编译的时候就会被分配了。

反编译order 查看字节码文件:

在这里插入图片描述

count 编译时没有赋值,只是在类加载器时准备赋值为0然后初始化赋值为1,
而number编译时已经被显式赋值。

6、方法区的演进过程

在这里插入图片描述

7、方法区垃圾回收

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr-Wanter

感谢大佬

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值