Java常量池解析与字符串intern简介
在Java应用程序运行时,Java虚拟机会保存一份内部的运行时常量池,它区别于class文件的常量池,是class文件常量池映射到虚拟机中的数据结构。 关于class文件常量池的部分可以参考之前的博文实例探索Class文件。
1.CONSTANT_Class入口解析
数组类的符号解析较为特殊。若是基本类型数组,那么虚拟机将创建该基本类型的新数组类,并创建一个Class实例来代表该类型,数组类的定义类加载器为启动类加载器。若是引用类型的数组,那么在此之前还会进行引用类型的解析,数组类的定义类加载器为引用类型的定义类加载器。
非数组类和接口的的解析将经历以下步骤:
(1).加载该类型和其所有的超类型
如果该类型在此之前已经装载到了虚拟机的当前命名空间,那么直接使用已经被装载的类型即可,否则由引用的发起类的初始类加载器进行加载。对目标类型的超类的加载必然是在对当前类型加载完的基础上进行的,因为只有加载完当前类型,才能从class文件的super_class域找到其直接超类的符号引用,再递归进行解析和加载,直至java.lang.Object类。而在递归返回的过程中,会检查interfaces域以查看实现或扩展了哪些接口,并再次递归遍历对接口的符号引用。
(2).检查访问权限
随后是对目标类型的连接和初始化,这样才可以正常使用该类型。前面提到,对目标类型的初始化需要其所有超类都必须进行初始化(超接口不是必须的),并且,由于已经对其超类进行了加载,所以不必再依赖于自该类向Object类的解析顺序,而是从Object类向该类进行初始化。类型的连接和初始化步骤如下:
(3).类型校验
(4).类型准备
(5).类型解析(可推迟)
注意该过程是对被引用类型及其超类的符号引用的解析,因为对于被引用类型的某些符号引用不会立刻用到,故该步骤之前是严格意义上属于发起引用的类型的符号解析的过程。只有在主动使用被引用类型的这些符号引用所指向的类型时,才会对这些符号引用进行解析,对其所指向的类型进行装载、连接和初始化。
(6).类型初始化
2.CONSTANT_Fieldref入口解析
由于一个类型不会含有其超类型所定义的字段,所以对目标字段的搜索将会从字段所指向的类型开始,从该类型开始搜索,再递归搜索其所实现或扩展的接口,再递归搜索其超类,直至找到目标字段,并会将运行时常量池的该字段入口标记为已解析,并在该常量池的数据上改为对这个字段的直接引用。
3.CONSTANT_Methodref入口解析
与字段的搜索类似但有所不同,其搜索顺序将从该类型开始,再递归搜索其超类,在递归搜索其所实现或扩展的接口。
4.CONSTANT_InterfaceMethodRef入口解析
对接口方法的搜索就是从被解析的接口开始,向其超接口递归搜索。
5.CONSTANT_String入口解析
Java虚拟机会将字符串处理为一个字符串对象加以维护,而虚拟机所维护的就是一张字符串池,它包含所有被”拘留”的字符串对象的引用。对CONSTANT_String常量池的解析首先就要查看字符串池中该字符串对象的引用是否存在,如果存在则直接把常量池数据解析为该字符串对象的引用,若不存在,那么就需要根据这个字符串序列创建一个字符串对象,并将其引用加入到字符串池中,并将常量池数据解析为该引用。
也可以使用String对象的intern对象来拘留一个字符串(注意并非字符串对象),若该字符串池中存在对该字符串序列的对象的引用,那么直接返回该引用即可,否则,将会拘留该字符串,但注意拘留返回的字符串对象引用将不会指向原String对象,因为原String对象位于Java堆,而字符串池的对象是虚拟机所创建的,由虚拟机所维护。
package com.ice.intern; public class InternTest { public static void main(String args[]){
String a = new String("123");
String b = a;
String c = new String("123");; System.out.println("before intern:");
System.out.println("a = b ? :" + (a == b));
System.out.println("a = c ? :" + (a == c)); a = a.intern();
c = c.intern(); System.out.println("after intern:");
System.out.println("a = b ? :" + (a == b));
System.out.println("a = c ? :" + (a == c));
} }
结果如下:
(6).其他类型(数据基本类型)入口解析
直接使用常量池所包含的常量值即可
6.直接引用
常量池解析最终将符号引用替换成为直接引用。指向类型、类变量和类方法的直接引用可能为在方法区的指针。而指向实例变量和实例方法的直接引用是从对象映像的开始到该实例变量或方法表的偏移。
实例变量的组织方式为:从Object类开始到该实例的类型,将类中声明的实例变量按在class文件中出现的顺序依次放在对象映像中。
实例方法的组织方式较为类似:从Object类开始到该实例的类型,将类中声明的实例方法指针按在class文件中出现的顺序依次放在对象映像中。但对于重写的方法将出现在超类对应的位置(该方法第一次出现的位置)。
但是访问接口方法就不能简单地通过方法表的偏移量来进行访问,而必须搜索对象的类的方法表来找到该方法。
比如Factory接口分别由A和B来实现其produce()方法,但由于A和B不能保证由同一个实现了Factory接口的超类派生,即有着同样的produce()方法偏移,那么就无法通过方法表的偏移来访问Factory的produce()方法。
7.装载约束
对于一个类型指向另一个类型的符号引用,如果引用的类型和被引用类型并非由同一个初始加载器加载(可能通过用户自定义ClassLoader来实现),那么虚拟机就必须确保被引用类型在不同的命名空间中保持一致。这样就通过自定义ClassLoader来加载不受信类型后,就不会发生解析对被引用类型的符号引用时,把受信的类型当做已经被解析过的不受信类型(因为对方法的符号引用只有权限定名和描述符,并不会也无法得知其初始类加载器),从而调用了不受信类型的方法访问受信类型的受保护成员。
Java常量池解析与字符串intern简介的更多相关文章
- 从Java的字符串池、常量池理解String的intern()
前言 逛知乎遇到一个刚学Java就会接触的字符串比较问题: 通常,根据"==比较的是地址,equals比较的是值"介个定理就能得到结果.但是String有些特殊,通过new Str ...
- 基本数据类型的常量池与String类型常量池解析
抛出样例: Integer a1 = new Integer(123); Integer a2 = new Integer(123); System.out.print ...
- 触摸java常量池
java常量池是一个经久不衰的话题,也是面试官的最爱,题目花样百出,小菜早就对常量池有所耳闻,这次好好总结一下. 理论 小菜先拙劣的表达一下jvm虚拟内存分布: 程序计数器是jvm执行程序的 ...
- 【转载】Java常量池
本篇随笔为转载,原贴地址:Java常量池理解与总结. (其实Java的常量池有点像C++中的存储字符串常量的常量存储区). 一.相关概念 什么是常量用final修饰的成员变量表示常量,值一旦给定就无法 ...
- java 常量池
在jvm规范中,每个类型都有自己的常量池.常量池是某类型所用常量的一个有序集合,包括直接常量(基本类型,String)和对其他类型.字段.方法的符号引用.之所以是符号引用而不是像c语言那样,编译时直接 ...
- java常量池理解
String类两种不同的创建方式 String s1 = "zheng"; //第一种创建方式 String s2 = new String("junxiang" ...
- 深入浅出java常量池
理论 jvm虚拟内存分布: 程序计数器是jvm执行程序的流水线,存放一些跳转指令. 本地方法栈是jvm调用操作系统方法所使用的栈. 虚拟机栈是jvm执行java代码所使用 ...
- java虚拟机学习-触摸java常量池(13-1)
java虚拟机学习-深入理解JVM(1) java虚拟机学习-慢慢琢磨JVM(2) java虚拟机学习-慢慢琢磨JVM(2-1)ClassLoader的工作机制 java虚拟机学习-JVM内存管理:深 ...
- Java常量池的理解
1.常量池的好处常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享.例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中.(1)节省内存空间:常量池中所有相同的字符串常 ...
随机推荐
- 6)Linux程序设计入门--消息管理
)Linux程序设计入门--消息管理 前言:Linux下的进程通信(IPC) Linux下的进程通信(IPC) POSIX无名信号量 System V信号量 System V消息队列 System V ...
- Python操作dict时避免出现KeyError的几种方法
见原文:https://www.polarxiong.com/archives/Python-%E6%93%8D%E4%BD%9Cdict%E6%97%B6%E9%81%BF%E5%85%8D%E5% ...
- Could not connect to Redis at 192.168.0.129:6379: Connection refused
在虚拟机上(CentOS 6.7)本机连接自己的redis [root@localhost bin]# ./redis-cli -h Could not connect to Redis at : C ...
- UVA 10790 (13.08.06)
How Many Points of Intersection? We have two rows. There are a dots on the toprow andb dots on the ...
- LruCache DiskLruCache 缓存 简介 案例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- java中遍历实体类属性和类型
public static void testReflect(Object model) throws NoSuchMethodException, IllegalAccessException, I ...
- windows下安装rabbitmq的php扩展amqp(原创)
从php官方下载相应的版本http://pecl.php.net/package/amqp,我这里使用的是1.4.0版本(http://pecl.php.net/package/amqp/1.4.0/ ...
- js 判断页面元素是否存在
转自:http://www.0fish.com/js/416 原生js写法案例: obj = document.getElementById("someID"); if (obj) ...
- Oracle 之 分析函数
一.分析函数 1.分析函数 分析函数是Oracle专门用于解决复杂报表统计需求的功能强大的函数,它可以在数据中进行分组然后计算基于组的某种统计值,并且每一组的每一行都可以返回一个统计值. 2.分析函数 ...
- 安卓手机GPU OpenCL总结(转)
前段时间,把市面上手机GPU OpenCL支持情况做了一个总结.总结如下: 目前,手机 GPU 市面有四个公司产品:Qualcomm, Imagination Technologies,ARM, Vi ...