深入方法区

继上文:深入栈帧

方法区(Method Area)是什么?

方法区(Method Area)属于jvm运行时数据区的一块,也是跟堆一样被所有线程共享,并且方法区在物理层面是属于堆中的一块。方法区在JVM启动的时候被创建。

特性:

不连续;

JVM启动时被创建;

会抛出内存溢出;

方法区、栈、堆之间的关系?

方法区内部有什么 ?

方法区中主要为:类信息和运行时常量池;

类信息包含:类型信息、域信息、方法信息

运行时常量池(Runtime Constant Pool):运行时常量池、JIT代码缓存

类型信息

对每个加载的类型(类Class、接口 interface、枚举enum、注解 annotation),JM必须在方法区中存储以下类型信息:

这个类型的完整有效名称(全名 = 包名.类名)

这个类型直接父类的完整有效名(对于 interface或是java.lang. Object,都没有父类)

这个类型的修饰符( public, abstract,final的某个子集)

这个类型直接接口的一个有序列表

域信息

域信息,即为类的属性,成员变量

JVM必须在方法区中保存类所有的成员变量相关信息及声明顺序。

域的相关信息包括:域名称、域类型、域修饰符(pυblic、private、protected、static、final、volatile、transient的某个子集)

方法信息

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

方法名称方法的返回类型(或void)

方法参数的数量和类型(按顺序)

方法的修饰符public、private、protected、static、final、synchronized、native,、abstract的一个子集

方法的字节码bytecodes、操作数栈、局部变量表及大小( abstract和native方法除外)

异常表( abstract和 native方法除外)。每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引

non-fianl的类变量

静态变量和类关联在一起,随着类的加载而加载,它们成为类数据在逻辑上的一部分

类变量被类的所有实例共享,即使没有实例时你也可以访问它

常量池

存放编译期间生成的各种字面量与符号引用,包含内容有:数量值、字符串值、类引用、字段引用、方法引用

运行时常量池

当类加载后会将常量池表的内容加载到运行时常量池中,所以常量池表在运行时的表现形式。

参考:https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-2.html#jvms-2.5.5

//javap命令
javap -v JVMDebug.class
//类信息
Classfile /D:/ideaWorkSpace/jdk8/src/main/java/com/jvm/JVMDebug.class
  Last modified 2021-4-6; size 611 bytes
  MD5 checksum 868a6b188641f869d297950f67c1b34f
  Compiled from "JVMDebug.java"
public class com.jvm.JVMDebug
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER // 类权限修饰符
  //常量池
Constant pool:
   #1 = Methodref #6.#24 // java/lang/Object."<init>":()V
   #2 = Integer 100000
   #3 = Methodref #4.#25 // com/jvm/JVMDebug.doubleValue:(I)I
   #4 = Class #26 // com/jvm/JVMDebug
   #5 = Methodref #4.#27 // com/jvm/JVMDebug.calcSum:()J
   #6 = Class #28 // java/lang/Object
   #7 = Utf8 NUM
   #8 = Utf8 I
   #9 = Utf8 ConstantValue
  #10 = Integer 15000
  #11 = Utf8 <init>
  #12 = Utf8 ()V
  #13 = Utf8 Code
  #14 = Utf8 LineNumberTable
  #15 = Utf8 doubleValue
  #16 = Utf8 (I)I
  #17 = Utf8 StackMapTable
  #18 = Utf8 calcSum
  #19 = Utf8 ()J
  #20 = Utf8 main
  #21 = Utf8 ([Ljava/lang/String;)V
  #22 = Utf8 SourceFile
  #23 = Utf8 JVMDebug.java
  #24 = NameAndType #11:#12 // "<init>":()V
  #25 = NameAndType #15:#16 // doubleValue:(I)I
  #26 = Utf8 com/jvm/JVMDebug
  #27 = NameAndType #18:#19 // calcSum:()J
  #28 = Utf8 java/lang/Object
//方法信息被加载到方法区
{
    //域名称
  public static final int NUM;
  //域类型
    descriptor: I
    //域权限
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 15000

  public com.jvm.JVMDebug();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1 // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 8: 0

  public static int doubleValue(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: ldc #2 // int 100000
         5: if_icmpge 14
         8: iinc 1, 1
        11: goto          2
        14: iload_0
        15: iconst_2
        16: imul
        17: ireturn
      LineNumberTable:
        line 13: 0
        line 14: 14
      StackMapTable: number_of_entries = 2
        frame_type = 252 /* append */
          offset_delta = 2
          locals = [ int ]
        frame_type = 250 /* chop */
          offset_delta = 11

  public static long calcSum();
    descriptor: ()J
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=3, args_size=0
         0: lconst_0
         1: lstore_0
         2: iconst_1
         3: istore_2
         4: iload_2
         5: bipush 100
         7: if_icmpgt 24
        10: lload_0
        11: iload_2
        12: invokestatic #3 // Method doubleValue:(I)I
        15: i2l
        16: ladd
        17: lstore_0
        18: iinc 2, 1
        21: goto          4
        24: lload_0
        25: lreturn
      LineNumberTable:
        line 18: 0
        line 19: 2
        line 20: 10
        line 19: 18
        line 22: 24
      StackMapTable: number_of_entries = 2
        frame_type = 253 /* append */
          offset_delta = 4
          locals = [ long, int ]
        frame_type = 250 /* chop */
          offset_delta = 19

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: iconst_0
         1: istore_1
         2: iload_1
         3: sipush 15000
         6: if_icmpge 19
         9: invokestatic #5 // Method calcSum:()J
        12: pop2
        13: iinc 1, 1
        16: goto          2
        19: return
      LineNumberTable:
        line 26: 0
        line 27: 9
        line 26: 13
        line 30: 19
      StackMapTable: number_of_entries = 2
        frame_type = 252 /* append */
          offset_delta = 2
          locals = [ int ]
        frame_type = 250 /* chop */
          offset_delta = 16
}
SourceFile: "JVMDebug.java"

注意:

jdk8之前方法实现是永久代,jdk8开始方法区实现是元空间。

StringTable 触发Full GC才会回收,所以会导致老年代的空间不足,所以jdk7被移到堆空间中,能及时回收空间。

方法区的内存溢出 ?

jvm配置:-XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M

package com.jvm.outofmemoryerror;

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

/**
 * @author: csh
 * @Date: 2021/4/6 14:09
 * @Description:方法区内存溢出
 */
public class Ares extends  ClassLoader {
    public static void main(String[] args) {
        int i = 0;
        Ares ares = new Ares();
        while (true) {
            i++;
            ClassWriter cw = new ClassWriter(0);
            cw.visit(Opcodes.V1_8,Opcodes.ACC_PUBLIC,"Class"+i,null,"java/lang/Object",null);
            byte[] code = cw.toByteArray();
            ares.defineClass("Class"+i,code,0,code.length);
            System.out.println(i);
        }
    }
}

最后

方法区也是存在垃圾回收的,这块等到垃圾回收统一了解。方法区是线程共享,存储已经被jvm加载的类信息、常量、静态变量等等,在jdk8之前统称永久代,jdk8开始称为元空间,区别在于元空间不在虚拟机设置的内存中,并且相应的内存结构也调整了。

参考:

https://blog.csdn.net/zuodaoyong/article/details/107031191

https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4

https://blog.csdn.net/qq_43040688/article/details/104982648


系列文章:

jvm相关知识

java发展史及虚拟机历史

对象的内存是如何布局的?

jvm的类加载器(classloader)及类的加载过程

java模块化系统

深入栈帧

....

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值