【JVM9】方法区


在这里插入图片描述

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

  1. 运行时数据结构图:从线程共享与否的角度看
    在这里插入图片描述

在这里插入图片描述

2、方法区的理解

  1. 方法区与堆空间一样,是各个线程共享的区域
  2. 方法区是JVM启动时创建的,并且它也是逻辑上连续,物理上不连续。
  3. 如果你系统家加载了太多的类,导致HotSpot的方法区溢出,虚拟机会抛出OOM:MeatSpace;
  4. 关闭虚拟机就会释放方法区的内存;
  5. 到了Java8,HotSpot虚拟机用元空间代替了永久代的概念;元空间使用的是本地内存,如果加载的类太多,本地内存放不下,照样会抛出异常:OOM:MeatSpace;

3、设置方法区大小与OOM

  1. 方法区初始默认大小是21M
  2. 最大就是你电脑内存大小
  3. 设置方法区大小的参数

-XX:MetaspaceSize=100m(初始值设置)
-XX:MaxMetaspaceSize=200m(最大值设置)

举例OOM:
(了解)

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;

public class OOMTest extends ClassLoader{
    public static void main(String[] args) {
        int j=0;
        try{
            OOMTest test = new OOMTest();
            for (int i = 0; i < 10000; i++) {
                //创建ClassWriter对象,用于生成类的二进制字节码
                ClassWriter classWriter = new ClassWriter(0);
                classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC,"Class"+i,null,"java/lang/Object",null);
                //返回byte
                byte[] code = classWriter.toByteArray();
                //类的加载
                test.defineClass("Class"+i,code,0,code.length);//Class对象
                j++;
            }
        }finally {
            System.out.println(j);
        }

    }
}

在这里插入图片描述

如何解决OOM?
在这里插入图片描述
在以后篇章会细说!

4、方法区内部结构*

(1)方法区存储些什么?

  1. 《深入理解Java虚拟机》说,方法区用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等,当然这也不是绝对的,现在jdk更新太快,有一些小的变化,上述描述的是经典版本;
  2. 方法区还保存着一个类是被哪个加载器加载的一些信息
  3. 那么方法区还保存着一个加载器加载过哪些类的信息

(2)类型信息

  1. 对于每加载的类型(类class、接口interface、枚举enum、注解annotation),JVM必须在方法区存储一下信息
    ①这个类型完整有效名称(全名=包名.类名)
    ②这个类型直接父类的完整有效名(接口和Object没有父类)
    ③这个类型的修饰符(public,abstract,final的某个子集)
    ④这个类型直接接口的有序列表(比如一个类实现了多个接口,那么这些被实现的接口有一个有序的列表)

(3)域(Field)信息

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

volatile:是Java提供的一种轻量级的同步机制

(4)方法(Method)信息

  1. 方法名称
  2. 方法返回类型
  3. 方法参数的数量和类型
  4. 方法的修饰符(public、private、protected、static、final、synchronized、native、abstract)
  5. 方法的字节码(bytecode)、操作数栈和局部变量表的大小,(abstract、native)除外;
  6. 异常表(abstract、native)除外
    ①每个异常的开始位置、结束位置、代码处理在在程序计数器中的偏移地址;

(5)补充:

1、non-final的类变量
  1. 静态变量(类变量)是属于类的,随着类的加载而加载;
  2. 类变量被类的所有实例实例所共享,即使没有实例也可以访问他们:
public class MethodAreaTest {
    public static void main(String[] args) {

        Order order=null;
        System.out.println(order.count);
        System.out.println(order.a);

    }
}
class Order{
    public int count=10;
    public static int a=90;
}

上面代码中访问a是90,访问count却报空指针错误

2 、全局常量:static final

每个被声明为final的变量,即static fianl 两个关键词修饰的属性或者只被final修饰的属性,在编译阶段就会初始化

public class MethodAreaTest {
    public static void main(String[] args) {

        Order order=null;
        System.out.println(order.count);
        System.out.println(order.a);

    }
}
class Order{
    public int count=10;
    public static int a=90;
    public final int b=100;
    public static final int c=200;
    public static final String d="abcd";
}

在这里插入图片描述
可以看出final 修饰的属性在编译期间就已经被初始化了
回顾:static静态变量在类的加载阶段的第二阶段(链接)的准备阶段(prepare)被默认赋值为0,然后第三阶段的初始化阶段进行真正的赋值。

(6)常量池

常量池就像是一张表,JVM指令根据这张表找到要执行的类名、方法名、参数类型、参数名、字面量等类型;

常量池是字节码文件中的一部分!!!
在这里插入图片描述

(6)运行时常量池

  1. 常量池表是Class文件的一部分,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中
  2. 当加载类或者接口到JVM后,会创建对应的运行时常量池
  3. JVM为每个已经加载的类型(类或者接口)都会维护一个常量池。池中的数据项(真实地址)像数组项一样通过索引访问(像数组通过下标索引)。
  4. 运行时常量池存储的不再是常量池中的符号地址了而是真实地址

5、方法区使用举例

。。。

6、方法区的演进细节

  1. 首先明确只有Hotspot才有永久代。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3. 为什么要用原空间替代永久代?
答:
①永久代设置空大小很难确定,实际的web工程中,功能点较多,需要动态加载很多类,经常OOM
②对永久代调优很困难。(涉及Full GC<很浪费时间>)
4. Stringtable为什么要调整?
jdk7中将stringTable放到了堆空间中。因为永久代的回收效率很低,在full gc的时候才会触发。而full gc是老年代的空间不足、永久代不足时才会触发。这就导致stringTable回收效率不高。而我们开发中会有大量的字符串被创建,回收效率低,导致永久代内存不足。放到堆里,能及时回收内存。
5. 静态变量存放在哪里?(堆空间)
在这里插入图片描述

7、方法区的垃圾回收

  1. 方法区需不需要回收,《Java虚拟机规范》说,你可以回收,也可以不回收。
  2. 主要是这个区域的回收效果比较难令人满意,尤其是类型卸载(回收不使用的类),条件相当苛刻,但是回收有时候又有必要;
  3. 方法区垃圾收集主要两个部分内容,常量池中废弃的常量和不再使用的类型

8、总结

在这里插入图片描述

9、常见面试题

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值