疑难杂症_Java常量池

前言

本文章来自网上各方大佬笔记,如有侵权,敬请告知

JVM常量池主要分为class文件常量池运行时常量池全局字符串常量池基本类型包装类对象常量池


class文件常量池

class文件是一组以字节为单位的二进制数据流,在.java文件编译过程中,.class文件会以二进制数据的形式存放在磁盘中,这些二进制数据中就包括class文件常量池
所以说,class文件常量池是非运行时常量池,其在编译阶段便已确定

既然是常量池,其中自然是用来存放常量的,class文件常量池主要存放两大常量:

- 字面量(主要包括)
	- 文本字符串
		也即:public String s = "123"; 中的 "123"
	-final修饰的成员变量
		包括静态变量、实例变量和局部变量
	而对于基本数据类型、方法中的局部变量,class文件常量池中只会保留它的字段描述符(即数据类型描述符,int的是I)、字段的名称value,而它们的字面量不会存在于常量池
- 符号引用
	- 类和接口的全限定名
		如:java/lang/String;
		类和接口的全限定名会将类名/接口名中的 . 替换为 / ,以便在运行时解析得到类的直接引用
	- 字段的名称和描述符
		字段也就是类/接口中声明的变量,分为类级别变量和实例级变量
		方法中的局部变量名在class文件常量池中仅保存字段名
	- 方法中的名称和描述符
		即参数类型+返回值

运行时常量池

运行时常量池是方法区的一部分,所以也是全局贡献的
JVM在执行某个类时,会依次进行:加载链接(验证、准备、解析)初始化
加载步骤中,JVM需要完成:

- 通过一个类的全限定名来获取此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个类对象,代表加载的这个类,这个对象是java.lang.Class,它作为方法区这个类的各种数据访问的入口

类对象和普通对象是不同的。类对象是在类加载的时候完成的,是JVM创建的并且是单例的,类对象会作为这个类和外界交互的入口;普通对象则是在调用new之后创建
加载的第二步,即为将class字节流所代表的静态存储结构转化为方法区的运行时数据结构,其中就包括class文件常量池进入运行时常量池的过程
当不同类共用一个运行时常量池时,各类的class文件常量池会在进入运行时常量池的过程中,各个class文件常量池中的相同字符串只会在在运行时常量池存下一份,实现进一步优化
运行时常量池的作用是存储class文件常量池中的符号信息,运行时常量池中保存着一些class文件中描述的符号引用,同时在类的解析阶段还会将这些符号引用翻译出直接引用(直接指向实例对象的指针,内存地址),翻译出来的直接引用也是存储在运行时常量池
运行时常量池相对于class文件常量池的一大特征就是具有动态性,java规范并不要求常量只能在运行时产生,也即运行时常量池中的内容并不完全来自class文件常量池,在运行的过程中也可以通过代码生成常量并将其放入运行时常量池中,这种特性被使用最多的就是String.intern()


全局字符串常量池

Java中创建字符串对象的方式一般有如下两种:

- String s_1 = "Hellow";
- String s_2 = new String("Hellow");

第一种方式声明的字面量 Hellow是在编译器就已经确定的,它会直接进入class文件常量池中,当运行期间在全局字符串常量池中保存它的一个引用,实际上最终还是要在上创建一个“Hellow”对象
第二种方式使用了new String(),也就是调用了String类的构造函数,new指令的功能是创建一个类的实例对象并完成加载初始化,因此这个字符串对象是在运行期才能确定的,创建的字符串的对象是在
因此,System.out.println(s_1 == s_2);的结果必定为false,虽然它们都在中,但所处位置地址肯定不同


JVM运行时数据区的结构图

在这里插入图片描述

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值