JVM-01-JVM基础-03-运行时常量池

一、常量池中都包含啥

1、常量池是一张表,其中包含多种常量-->从编译时已知的数字、文字;到必运行时解析的方法和字段引用。

2、JVM指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息。

举例:

(1)类

package com.fupng3.jvm;

public class Hello {
    private String name;
    final int AGE=30;

    public static void main(String[] args) {
        int i=10;
        //局部常量值不会在常量池中
        final int j=20;
        String str="abc";
        System.out.println(str);
    }
}

(2)类中常量池

Constant pool:
   #1 = Methodref          #7.#30         // java/lang/Object."<init>":()V
   #2 = Fieldref           #6.#31         // com/fupng3/jvm/Hello.AGE:I
   #3 = String             #32            // abc
   #4 = Fieldref           #33.#34        // java/lang/System.out:Ljava/io/PrintStream;
   #5 = Methodref          #35.#36        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Class              #37            // com/fupng3/jvm/Hello
   #7 = Class              #38            // java/lang/Object
   #8 = Utf8               name
   #9 = Utf8               Ljava/lang/String;
  #10 = Utf8               AGE
  #11 = Utf8               I
  #12 = Utf8               ConstantValue
  #13 = Integer            30
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               LocalVariableTable
  #19 = Utf8               this
  #20 = Utf8               Lcom/fupng3/jvm/Hello;
  #21 = Utf8               main
  #22 = Utf8               ([Ljava/lang/String;)V
  #23 = Utf8               args
  #24 = Utf8               [Ljava/lang/String;
  #25 = Utf8               i
  #26 = Utf8               j
  #27 = Utf8               str
  #28 = Utf8               SourceFile
  #29 = Utf8               Hello.java
  #30 = NameAndType        #14:#15        // "<init>":()V
  #31 = NameAndType        #10:#11        // AGE:I
  #32 = Utf8               abc
  #33 = Class              #39            // java/lang/System
  #34 = NameAndType        #40:#41        // out:Ljava/io/PrintStream;
  #35 = Class              #42            // java/io/PrintStream
  #36 = NameAndType        #43:#44        // println:(Ljava/lang/String;)V
  #37 = Utf8               com/fupng3/jvm/Hello
  #38 = Utf8               java/lang/Object
  #39 = Utf8               java/lang/System
  #40 = Utf8               out
  #41 = Utf8               Ljava/io/PrintStream;
  #42 = Utf8               java/io/PrintStream
  #43 = Utf8               println
  #44 = Utf8               (Ljava/lang/String;)V

(3)jvm指令根据常量池找到要执行的类、方法、参数信息

二、运行时常量池中数据的延时加载

1、当类被加载,它的常量池中信息就会被放入运行时常量池,并把里面的符号信息变为真实地址;
2、常量池中信息不是全部一次性加载,而是在执行代码时,如果发现常量池中内容不在运行时常量池中时,再加载(延迟加载);

3、运行时常量池在jdk1.8及之后,是放在堆中的,当常量不被引用时会被gc回收。

举例1:以字符串常量来演示运行时常量池中数据的延迟加载

(1)测试代码

public class Hello {

    public static void main(String[] args) {
        String str1="a"; //运行该行代码后,运行时常量池中有字符串a
        String str2="b"; //运行该行代码后,运行时常量池中有字符串a、b
        String str3="ab";//运行该行代码后,运行时常量池中有字符串a、b、ab
        String str4=str1+str2;  //str4的引用在堆中
        String str5="a"+"b";    //编译期优化

        System.out.println(str3==str4);
        System.out.println(str3==str5);

    }
}

(2)输出

false
true

更多案例参考:Java基础--03--String

三、intern方法

intern方法用于将字符串放在运行时常量池中。

1、如果字符串已经在运行时常量池中,则不放入

2、如果字符串不在运行时常量池中,则放入

如上2种情况,都会返回常量池中对象;

运行时常量池中存放的是类中常量池中的信息,动态生成的字符串不在运行时常量池中

举例1:

    public static void main(String[] args) {

        String str1=new String("a")+new String("b");
        String str3=str1.intern();

        String str2="ab";

        System.out.println(str1==str2);
        System.out.println(str3==str2);

    }

输出

true
true

举例2:

    public static void main(String[] args) {

        String str2="ab";
        String str1=new String("a")+new String("b");
        String str3=str1.intern();



        System.out.println(str1==str2);
        System.out.println(str3==str2);

    }

输出

false
true

举例3:jdk1.6中的intern

jdk1.6与jdk1.8的intern方法处理逻辑不同:

1、如果有则并不会放入,返回运行时常量池中的对象;

2、如果没有,则会复制一份str1放到运行时常量池中,返回运行时常量池中的对象;

//jdk1.6中运行结果
    public static void main(String[] args) {

        
        String str1=new String("a")+new String("b");
        String str3=str1.intern();
        
        String str2="ab";


        System.out.println(str1==str2);
        System.out.println(str3==str2);

    }

输出:
false
true




    public static void main(String[] args) {

        String str2="ab";
        String str1=new String("a")+new String("b");
        String str3=str1.intern();




        System.out.println(str1==str2);
        System.out.println(str3==str2);

    }

//输出结果

false
true

举例3:synchronized (String)的问题

(1)代码示例

public class TestSynString {
    public static void main(String[] args) throws InterruptedException {

        //多个用户执行多次任务,每个用户执行完一次才能执行下一次
        for (int i=1;i<=10;i++){
            for (int j=1;j<=10;j++){
                final String userId=String.valueOf(j);
                new Thread(()->{
                    try {
                        synchronized (userId) {
                            System.out.println("获得锁开始执行,uid="+userId);
                            Thread.sleep(20); //sleep模拟具体业务执行耗时
                        }
                        //执行结束释放锁
                        System.out.println("释放锁,uid="+userId);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }

        Thread.sleep(60000);
    }
}

(2)运行结果

获得锁开始执行,uid=1
获得锁开始执行,uid=2
获得锁开始执行,uid=3
获得锁开始执行,uid=4
获得锁开始执行,uid=5
获得锁开始执行,uid=6
获得锁开始执行,uid=7
获得锁开始执行,uid=8
获得锁开始执行,uid=9
获得锁开始执行,uid=10
获得锁开始执行,uid=1
获得锁开始执行,uid=2
。。。

从执行结果看synchronized (userId)给同一个uid加锁并没有生效,原因是同一个数字对应的userId对象不同,所以需要使用synchronized (userId.intern())

四、运行时常量池长度

运行时常量池是一个固定大小的Hashtable(数组+链表+红黑树【jdk8以后】),长度默认为60013,如果放进的常量非常多,就会造成Hash冲突严重影响性能,可以通过-XX:StringTablesize调整长度优化性能。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值