Class常量池

     Class常量池可以理解为是Class文件中的资源仓库。 Class文件中除了包含类的版本、字段、方法、接口等描述信息外, 还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。

一个class文件的16进制大体结构如下图:
在这里插入图片描述
对应的含义如下,细节可以查下oracle官方文档
在这里插入图片描述
当然我们一般不会去人工解析这种16进制的字节码文件,我们一般可以通过javap命令生成更可读的JVM字节码指令文 件:
javap -v Math.class

Classfile /E:/open/demo/target/classes/com/example/demo/test/opencv/Math.class
  Last modified 2020-9-20; size 619 bytes
  MD5 checksum f70c67abc97af9f0978a807380fcb072
  Compiled from "Math.java"
public class com.example.demo.test.opencv.Math
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#26         // java/lang/Object."<init>":()V
   #2 = Class              #27            // com/example/demo/test/opencv/Math
   #3 = Methodref          #2.#26         // com/example/demo/test/opencv/Math."<init>":()V
   #4 = Methodref          #2.#28         // com/example/demo/test/opencv/Math.compute:()I
   #5 = Class              #29            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               Lcom/example/demo/test/opencv/Math;
  #13 = Utf8               compute
  #14 = Utf8               ()I
  #15 = Utf8               a
  #16 = Utf8               I
  #17 = Utf8               b
  #18 = Utf8               c
  #19 = Utf8               main
  #20 = Utf8               ([Ljava/lang/String;)V
  #21 = Utf8               args
  #22 = Utf8               [Ljava/lang/String;
  #23 = Utf8               math
  #24 = Utf8               SourceFile
  #25 = Utf8               Math.java
  #26 = NameAndType        #6:#7          // "<init>":()V
  #27 = Utf8               com/example/demo/test/opencv/Math
  #28 = NameAndType        #13:#14        // compute:()I
  #29 = Utf8               java/lang/Object
{
    
  public com.example.demo.test.opencv.Math();
    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 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/example/demo/test/opencv/Math;

  public int compute();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: bipush        10
         9: imul
        10: istore_3
        11: iload_3
        12: ireturn
      LineNumberTable:
        line 6: 0
        line 7: 2
        line 8: 4
        line 9: 11
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0  this   Lcom/example/demo/test/opencv/Math;
            2      11     1     a   I
            4       9     2     b   I
           11       2     3     c   I

  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: new           #2                  // class com/example/demo/test/opencv/Math
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #4                  // Method compute:()I
        12: istore_2
        13: return
      LineNumberTable:
        line 14: 0
        line 15: 8
        line 16: 13
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      14     0  args   [Ljava/lang/String;
            8       6     1  math   Lcom/example/demo/test/opencv/Math;
           13       1     2 compute   I
}
SourceFile: "Math.java"

常量池中主要存放两大类常量:字面量符号引用

字面量
字面量就是指由字母、数字等构成的字符串或者数值常量
字面量只可以右值出现,所谓右值是指等号右边的值,如:int a=1 这里的a为左值1为右值。在这个例子中1就是字面量。

int a = 1; 
int b = 2; 
int c = "abcdefg";

符号引用
符号引用是编译原理中的概念,是相对于直接引用来说的。主要包括了以下三类常量:

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符
         上面的a,b就是字段名称,就是一种符号引用,还有Math类常量池里的 Lcom/example/demo/test/opencv/Math 是类的全限定名, main和compute是方法名称,()是一种UTF8格式的描述符,这些都是符号引用。
          这些常量池现在是静态信息,只有到运行时被加载到内存后,这些符号才有对应的内存地址信息,这些常量池一旦被装 入内存就变成运行时常量池了,对应的符号引用在程序加载或运行时会被转变为被加载到内存区域的代码的直接引用, 也就是我们说的动态链接了。例如,compute()这个符号引用在运行时就会被转变为compute()方法具体代码在内存中 的地址,主要通过对象头里的类型指针去转换直接引用。

Jdk1.6及之前: 有永久代, 常量池在方法区
Jdk1.7:有永久代,但已经逐步“去永久代”,常量池在堆
Jdk1.8及之后: 无永久代,常量池在元空间

字符串常量池

字符串常量池的设计思想

  1. 字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建 字符串,极大程度地影响程序的性能
  2. JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化

为字符串开辟一个字符串常量池,类似于缓存区
创建字符串常量时,首先坚持字符串常量池是否存在该字符串
存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中

代码示例,一些字符串局部变量操作

String str1 = "abc"; 
String str2 = "abc"; 
String str3 = "abc"; 
String str4 = new String("abc");
String str5 = new String("abc");
String str5 = new String("abc");

在这里插入图片描述

安全点与安全区域

安全点

安全点就是指代码中一些特定的位置,当线程运行到这些位置时它的状态是确定的,这样JVM就可以安全的进行一些操作,比 如GC等,所以GC不是想什么时候做就立即触发的,是需要等待所有线程运行到安全点后才能触发。 这些特定的安全点位置主要有以下几种:

  1. 方法返回之前
  2. 调用某个方法之后
  3. 抛出异常的位置
  4. 循环的末尾

安全区域

Safe Point 是对正在执行的线程设定的。
如果一个线程处于 Sleep 或中断状态,它就不能响应 JVM 的中断请求,再运行到 Safe Point 上。 因此 JVM 引入了 Safe Region。
Safe Region 是指在一段代码片段中,引用关系不会发生变化。在这个区域内的任意地方开始 GC 都是安全的。 线程在进入 Safe Region 的时候先标记自己已进入了 Safe Region,等到被唤醒时准备离开 Safe Region 时,先检查能 否离开,如果 GC 完成了,那么线程可以离开,否则它必须等待直到收到安全离开的信号为止。

猜你喜欢

转载自blog.csdn.net/mlplds/article/details/108700450