JVM-方法区

方法区是什么

1、理解方法区

在这里插入图片描述

  • 在以后JDK的演进中,方法区 和 堆 肯定是要分开的两个内存空间。具体原因官方给定的是要和JRockit 对齐(将JRockit和Hotspot合二为一)。
  • 方法区(Method Area)与Java堆一样,是各个线程共享的内存区域。
  • 方法区生命周期和堆一样随着JVM的启动而创建,销毁而终止,并且它的实际的物理内存空间中和Java堆区一样都可以是不连续的。
  • 方法区的大小,跟堆空间一样,可以选择固定大小或者可扩展
  • 方法区 主要存放的就是类信息,即我们在类加载时,需要将class字节码文件加载到内存并解析为元数据模板放入到 方法区,就是这个方法区。

在这里插入图片描述

2、方法区、栈、堆 关系

  • Person:存放在元空间,也可以说方法区
  • person:存放在Java栈的局部变量表中
  • new Person():存放在Java堆中

3、方法区 在 JDK6、JDK7、JDK8演变

  • JDK6时 方法区称为 永久代,并且 静态变量存储在永久代上。

  • JDK7时 方法区称为 永久代,其内存大小由JVM分配,与堆内存一样 需要人为分配调整,否则超出OOM。字符串常量池,静态变量保存在堆中

  • JDK8时 方法区修改为 元空间 ,彻底的和堆区划分开,并直接使用物理内存,相交 之前的永久代,oom出现几率变小,并且其内部结构也发生了变化。类型信息,字段,方法,常量保存在本地内存的元空间,但字符串常量池、静态变量仍然在堆中。

  • 所有的对象实体(不论是否static)都存储在堆中

  • 所有的成员变量(不论是否基本数据类型和引用类型)实体都存放在堆中,作为对象的一部分

  • 局部变量如果是基本数据类型则直接将实体放在局部变量表,如果是引用类型,将引用存放在 栈,实体存放在堆。

  • 字符串常量池,静态变量保存在堆中,占比很大放在老年代也有GC不过得等到FGC,而堆中Major GC即可回收

  • 元空间主要保存 类信息

4、设置方法区大小与OOM

方法区的大小不必是固定的,JVM可以根据应用的需要动态调整。

方法区在JDK8后最好指定内存大小,并且为其指定相对较高的值。 和堆 初始值和最大值设置成一样一个道理,减少其动态调整带来的性能消耗

JDK7

  • -XX:Permsize来设置永久代初始分配空间。默认值是20.75M
  • -XX:MaxPermsize来设定永久代最大可分配空间。32位机器默认是64M,64位机器模式是82M
  • 当JVM加载的类信息容量超过了这个值,会报异常 OutofMemoryError:PermGen space

JDK8

  • -XX:MetaspaceSize来设置元数据区初始分配空间。默认值是21M
  • -XX:MaxMetaspaceSize来设定元数据区最大可分配空间,默认值是-1,即没有限制
  • 最大值为-1,如果消耗完物理内存则也会报异常 OutofMemoryError:PermGen space

方法区内部信息

在这里插入图片描述

1、 类型信息

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

  • 这个类型的完整有效名称(全名=包名.类名)
  • 这个类型直接父类的完整有效名(对于interface或是java.lang.object,都没有父类)
  • 这个类型的修饰符(public,abstract,final的某个子集)
  • 这个类型直接接口的一个有序列表

2、域信息

JVM必须在方法区中保存类型的所有域的相关信息以及域的声明顺序。

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

3、方法(Method)信息

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

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

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

如下是一个类的字节码文件:

Classfile /E:/projects/我的练习项目/my_springcloud/cloud-exercise/target/classes/com/gao/JVM/MethodInnerStructure.class
  Last modified 2023-10-26; size 1680 bytes
  MD5 checksum fff8daab4df533e91f11820420ee446e
  Compiled from "MethodInnerStructure.java"
  // 类型信息
public class com.gao.JVM.MethodInnerStructure extends java.lang.Object implements java.lang.Comparable<java.lang.String>, java.io.Serializable
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #18.#57        // java/lang/Object."<init>":()V
   #2 = Fieldref           #17.#58        // com/gao/JVM/MethodInnerStructure.num:I
   #3 = Class              #59            // java/lang/Exception
   #4 = Methodref          #3.#60         // java/lang/Exception.printStackTrace:()V
   #5 = Fieldref           #61.#62        // java/lang/System.out:Ljava/io/PrintStream;
   #6 = Class              #63            // java/lang/StringBuilder
   #7 = Methodref          #6.#57         // java/lang/StringBuilder."<init>":()V
   #8 = String             #64            // count =
   #9 = Methodref          #6.#65         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #10 = Methodref          #6.#66         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  #11 = Methodref          #6.#67         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #12 = Methodref          #68.#69        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #13 = Class              #70            // java/lang/String
  #14 = Methodref          #17.#71        // com/gao/JVM/MethodInnerStructure.compareTo:(Ljava/lang/String;)I
  #15 = String             #72            // 测试方法的内部结构
  #16 = Fieldref           #17.#73        // com/gao/JVM/MethodInnerStructure.str:Ljava/lang/String;
  #17 = Class              #74            // com/gao/JVM/MethodInnerStructure
  #18 = Class              #75            // java/lang/Object
  #19 = Class              #76            // java/lang/Comparable
  #20 = Class              #77            // java/io/Serializable
  #21 = Utf8               serialVersionUID
  #22 = Utf8               J
  #23 = Utf8               ConstantValue
  #24 = Long               2682528963354033461l
  #26 = Utf8               str
  #27 = Utf8               Ljava/lang/String;
  #28 = Utf8               num
  #29 = Utf8               I
  #30 = Utf8               <init>
  #31 = Utf8               ()V
  #32 = Utf8               Code
  #33 = Utf8               LineNumberTable
  #34 = Utf8               LocalVariableTable
  #35 = Utf8               this
  #36 = Utf8               Lcom/gao/JVM/MethodInnerStructure;
  #37 = Utf8               test2
  #38 = Utf8               (I)I
  #39 = Utf8               value
  #40 = Utf8               e
  #41 = Utf8               Ljava/lang/Exception;
  #42 = Utf8               cal
  #43 = Utf8               result
  #44 = Utf8               StackMapTable
  #45 = Class              #59            // java/lang/Exception
  #46 = Utf8               test1
  #47 = Utf8               count
  #48 = Utf8               compareTo
  #49 = Utf8               (Ljava/lang/String;)I
  #50 = Utf8               o
  #51 = Utf8               (Ljava/lang/Object;)I
  #52 = Utf8               <clinit>
  #53 = Utf8               Signature
  #54 = Utf8               Ljava/lang/Object;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/io/Serializable;
  #55 = Utf8               SourceFile
  #56 = Utf8               MethodInnerStructure.java
  #57 = NameAndType        #30:#31        // "<init>":()V
  #58 = NameAndType        #28:#29        // num:I
  #59 = Utf8               java/lang/Exception
  #60 = NameAndType        #78:#31        // printStackTrace:()V
  #61 = Class              #79            // java/lang/System
  #62 = NameAndType        #80:#81        // out:Ljava/io/PrintStream;
  #63 = Utf8               java/lang/StringBuilder
  #64 = Utf8               count =
  #65 = NameAndType        #82:#83        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #66 = NameAndType        #82:#84        // append:(I)Ljava/lang/StringBuilder;
  #67 = NameAndType        #85:#86        // toString:()Ljava/lang/String;
  #68 = Class              #87            // java/io/PrintStream
  #69 = NameAndType        #88:#89        // println:(Ljava/lang/String;)V
  #70 = Utf8               java/lang/String
  #71 = NameAndType        #48:#49        // compareTo:(Ljava/lang/String;)I
  #72 = Utf8               测试方法的内部结构
  #73 = NameAndType        #26:#27        // str:Ljava/lang/String;
  #74 = Utf8               com/gao/JVM/MethodInnerStructure
  #75 = Utf8               java/lang/Object
  #76 = Utf8               java/lang/Comparable
  #77 = Utf8               java/io/Serializable
  #78 = Utf8               printStackTrace
  #79 = Utf8               java/lang/System
  #80 = Utf8               out
  #81 = Utf8               Ljava/io/PrintStream;
  #82 = Utf8               append
  #83 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #84 = Utf8               (I)Ljava/lang/StringBuilder;
  #85 = Utf8               toString
  #86 = Utf8               ()Ljava/lang/String;
  #87 = Utf8               java/io/PrintStream
  #88 = Utf8               println
  #89 = Utf8               (Ljava/lang/String;)V
{
// 域信息
  private static final long serialVersionUID;
    descriptor: J
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
    ConstantValue: long 2682528963354033461l

  private static java.lang.String str;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC

  public int num;
    descriptor: I
    flags: ACC_PUBLIC

// 方法信息
  public com.gao.JVM.MethodInnerStructure();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: bipush        10
         7: putfield      #2                  // Field num:I
        10: return
      LineNumberTable:
        line 11: 0
        line 15: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lcom/gao/JVM/MethodInnerStructure;

  public static int test2(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: iconst_0
         1: istore_1
         2: bipush        30
         4: istore_2
         5: iload_2
         6: iload_0
         7: idiv
         8: istore_1
         9: goto          17
        12: astore_2
        13: aload_2
        14: invokevirtual #4                  // Method java/lang/Exception.printStackTrace:()V
        17: iload_1
        18: ireturn
    // 异常信息表
      Exception table:
         from    to  target type
             2     9    12   Class java/lang/Exception
      LineNumberTable:
        line 18: 0
        line 20: 2
        line 21: 5
        line 24: 9
        line 22: 12
        line 23: 13
        line 25: 17
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            5       4     2 value   I
           13       4     2     e   Ljava/lang/Exception;
            0      19     0   cal   I
            2      17     1 result   I
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 12
          locals = [ int, int ]
          stack = [ class java/lang/Exception ]
        frame_type = 4 /* same */

  public void test1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=1
         0: bipush        20
         2: istore_1
         3: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: new           #6                  // class java/lang/StringBuilder
         9: dup
        10: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
        13: ldc           #8                  // String count =
        15: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        18: iload_1
        19: invokevirtual #10                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        22: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        25: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        28: return
      LineNumberTable:
        line 31: 0
        line 32: 3
        line 33: 28
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      29     0  this   Lcom/gao/JVM/MethodInnerStructure;
            3      26     1 count   I

  public int compareTo(java.lang.String);
    descriptor: (Ljava/lang/String;)I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: iconst_0
         1: ireturn
      LineNumberTable:
        line 37: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       2     0  this   Lcom/gao/JVM/MethodInnerStructure;
            0       2     1     o   Ljava/lang/String;

  public int compareTo(java.lang.Object);
    descriptor: (Ljava/lang/Object;)I
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: checkcast     #13                 // class java/lang/String
         5: invokevirtual #14                 // Method compareTo:(Ljava/lang/String;)I
         8: ireturn
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/gao/JVM/MethodInnerStructure;

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: ldc           #15                 // String 测试方法的内部结构
         2: putstatic     #16                 // Field str:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 13: 0
}
Signature: #54                          // Ljava/lang/Object;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/io/Serializable;
SourceFile: "MethodInnerStructure.java"

4、运行时常量池

  • 运行时常量池和常量池表不是一个东西,常量池表即 jclasslib 编译字节码文件后的常量池数据,保存在class文件中。而运行时常量池是相对于整个系统而言的常量池表,属于方法区。

  • 运行时的常量池,包含了各种字面量、域和方法的引用。

  • 我们在 JVM虚拟机栈中说到虚拟机栈 --> 栈帧 --> 运行时常量池符号引用。

Java程序运行时需要JRE运行时环境,其包含了很多核心类库,当JVM执行引擎解析一个class字节码文件时,其中包含了很多的核心类库引用以及一些常量、方法、字段、类引用信息。我们不能为每个字节码文件都写入其引用信息。

方法区垃圾回收

方法区的垃圾回收,之前在堆中讲过,只有FGC才会触发方法区的垃圾收集,而触发FGC的条件有很多,包括system.gc、堆满、方法区满

有些人认为方法区(如Hotspot虚拟机中的元空间或者永久代)是没有垃圾收集行为的,其实不然。《Java虚拟机规范》对方法区的约束是非常宽松的,提到过可以不要求虚拟机在方法区中实现垃圾收集。事实上也确实有未实现或未能完整实现方法区类型卸载的收集器存在(如JDK11时期的zGC收集器就不支持类卸载)。

一般来说这个区域的回收效果比较难令人满意,尤其是类型的卸载,条件相当苛刻。但是这部分区域的回收有时又确实是必要的。以前sun公司的Bug列表中,曾出现过的若干个严重的Bug就是由于低版本的HotSpot虚拟机对此区域未完全回收而导致内存泄漏。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值