JVM运行时数据区第四篇方法区

JVM运行时数据区第四篇方法区,第1张

JVM运行时数据区第四篇方法

在前面几章中已经学完了运行时数据区(Runtime Data Areas)的大部分内容了,接下来还有最后一点内容,就是方法区,学完了就差不多了。

上一篇文章地址:https://blog.csdn.net/weixin_46635575/article/details/122730272

一、栈,堆,方法区的交互 1、从线程与否的角度分

2、三者之间的关系

二、方法区的基本理解 1、方法区的所在位置


2、Hotspot虚拟机的方法演进



3、设置方法区大小与OOM





(1)来举栗子


假设不设置上面,虚拟机会自动的扩充内存,然后是不会报错什么的,但是如果下面进行设置虚拟机参数的话,那就会报错。


4、方法区的内部结构

先来回忆一下执行的一个过程
方法区里面的存储的一些数据(大概有这些,但是在不同的版本里面,有些内容是变化的)
(1)类型信息

(2)域(Field)信息(成员变量,也是属性)

(3)方法(Method)信息


方法区里面不仅仅是记录了这些内容,而且它存储了它被谁加载的,之前写的文章中写到了类加载器中,这里就是记录了被谁加载的。

对下面这个文件进行反编译

(4)静态变量和全局常量

静态变量
全局变量即用static修饰的final的常量


上面这张图就可以看出来,在编译的时候是直接赋值的(上面这个number属性),而另外一个count则没有赋值

到这里的有一个静态变量块的复制了一样,这是为什么呢?
其实这个在之前文章中就有写到了内容,对类加载的过程中,就是对静态变量进行处理的时候是不会先赋值的,首先赋值为0,然后再到初始化的过程才会赋值。(因为我们类加载过程分为三个阶段,就Loading加载 ,linking链接 ,初始化三个阶段,在前面两个时期是不会赋值的,只有到最后一个阶段初始化才会对静态变量进行赋值,会自动生成一个clinit的方法,对静态变量进行赋值,【这个clinit是有静态的变量或方法才会生成,不然是不会生成的】) 5、运行时常量池(Runtime Constant pool)重点

要学习运行常量池,那必须得先学习运行时间常量池。

(1)常量池(Constant pool)

字节码对应的文件,也是上面这张图里面的内容。
javap -v -p test1.class
Classfile /D:/JVMTest/out/production/SecondTest/cn/mldn/Zg/p5/test1.class
  Last modified 2022-1-22; size 792 bytes
  MD5 checksum 47e4a1309e9e100140f18f662a2269a1
  Compiled from "test1.java"
public class cn.mldn.Zg.p5.test1
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #8.#27         // java/lang/Object."":()V
   #2 = Fieldref           #28.#29        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #30.#31        // java/io/PrintStream.println:(I)V
   #4 = String             #23            // hello
   #5 = Methodref          #30.#32        // java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
   #6 = Methodref          #7.#33         // cn/mldn/Zg/p5/test1.hello2:()V
   #7 = Class              #34            // cn/mldn/Zg/p5/test1
   #8 = Class              #35            // java/lang/Object
   #9 = Utf8               
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               LocalVariableTable
  #14 = Utf8               this
  #15 = Utf8               Lcn/mldn/Zg/p5/test1;
  #16 = Utf8               main
  #17 = Utf8               ([Ljava/lang/String;)V
  #18 = Utf8               args
  #19 = Utf8               [Ljava/lang/String;
  #20 = Utf8               i1
  #21 = Utf8               I
  #22 = Utf8               i2
  #23 = Utf8               hello
  #24 = Utf8               hello2
  #25 = Utf8               SourceFile
  #26 = Utf8               test1.java
  #27 = NameAndType        #9:#10         // "":()V
  #28 = Class              #36            // java/lang/System
  #29 = NameAndType        #37:#38        // out:Ljava/io/PrintStream;
  #30 = Class              #39            // java/io/PrintStream
  #31 = NameAndType        #40:#41        // println:(I)V
  #32 = NameAndType        #40:#42        // println:(Ljava/lang/String;)V
  #33 = NameAndType        #24:#10        // hello2:()V
  #34 = Utf8               cn/mldn/Zg/p5/test1
  #35 = Utf8               java/lang/Object
  #36 = Utf8               java/lang/System
  #37 = Utf8               out
  #38 = Utf8               Ljava/io/PrintStream;
  #39 = Utf8               java/io/PrintStream
  #40 = Utf8               println
  #41 = Utf8               (I)V
  #42 = Utf8               (Ljava/lang/String;)V
{
  public cn.mldn.Zg.p5.test1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcn/mldn/Zg/p5/test1;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: iconst_1
         1: istore_1
         2: iinc          1, 1
         5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/Pri
ntStream;
         8: iload_1
         9: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V

        12: iconst_2
        13: istore_2
        14: iinc          2, 1
        17: getstatic     #2                  // Field java/lang/System.out:Ljava/io/Pri
ntStream;
        20: iload_2
        21: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V

        24: return
      LineNumberTable:
        line 5: 0
        line 6: 2
        line 7: 5
        line 10: 12
        line 11: 14
        line 12: 17
        line 13: 24
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      25     0  args   [Ljava/lang/String;
            2      23     1    i1   I
           14      11     2    i2   I

  public void hello();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/Pri
ntStream;
         3: ldc           #4                  // String hello
         5: invokevirtual #5                  // Method java/io/PrintStream.println:(Lja
va/lang/String;)V
         8: aload_0
         9: invokevirtual #6                  // Method hello2:()V
        12: return
      LineNumberTable:
        line 16: 0
        line 17: 8
        line 18: 12
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0  this   Lcn/mldn/Zg/p5/test1;

  public void hello2();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/Pri
ntStream;
         3: ldc           #4                  // String hello
         5: invokevirtual #5                  // Method java/io/PrintStream.println:(Lja
va/lang/String;)V
         8: return
      LineNumberTable:
        line 21: 0
        line 22: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcn/mldn/Zg/p5/test1;
}
SourceFile: "test1.java"

为什么需要常量池呢?

因为仅仅是写了一个比较小的类,就输出了一句话而已,但是内部的却包括了很多内容,比如我们的String,System等类,如果都放到字节码文件里面,那么就很多了,所以我们就用一些符号把我们需要的类进行符号引用,看清楚这里是引用,那我们就能在运行的时候进行加载解析,来使用,那样字节码文件就比较小了。都是一步步的调用的

我们的每一个方法都是这样直接写引用,然后间接的去引用类本身。【这样也达到了多用的目的,比如我们的String类信息,hello1方法引用,然后hello2引用等】
(2)运行时常量池(Runtime Constant pool)

其实很好理解的,就是运行时候,在内存层面对我们刚才的常量池表的一个维护,它是动态变化的。
到这里来梳理一下过程

首先我们写的程序会在你启动的时候,java虚拟机就会启动了,你写的一个程序就相当于一个进程在执行了。对我们的XXX.java 文件进行编译得到了(XXX.class)的文件。上面一个过程中进行了很多内容的处理,包括了对主类的加载,主类牵扯到所有的类进行加载。对于所有加载的类,java虚拟机会为它维护一张常量池表,以表示当前类的信息,包括方法的符号引用,当前类对其他类的一些引用等。你的程序在执行,你的主类里面,主类里面就有你主类对其他类的一些引用,你的主方法(main方法)在执行,它会去调用其他的方法,就是通过对当前去找到其他类,从其他类里面找到对应的方法,然后进行一系列的处理。 6、方法区使用例子

public class pcTest {
    public static void main(String[] args) {
        int x = 100;
        int y = 10;
        int k = x / y;

        int  b = 12;

        System.out.println(k+b);
    }
}

以此类为例,进行讲解









这个lstatic是对其他的引用,因为我们要输出嘛,所有它就是去常量池里面的#System的类(真正在执行的时候,它都会把这些符号引用转化为真正的类)






最后我们的main方法的栈帧也就没用了,然后就d出栈了,结束。

7、方法区的演进


这里有点历史的,就在java6的时候就有点想干掉永久代,但是都是在慢慢的演进,后来甲骨文公司直接收购了JRockit,然后就在JDK8合并的,把这个干掉了。


jdk8就方法区的大小就只是受到本地内存的限制了。

为什么要替换呢?

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5716357.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存