本文我们来介绍ClassFileParser 解析常量池的过程.解析常量池的过程是在ClassFileParser::parseClassFile 通过parse_constant_pool 来实现的.

在parse_constant_pool 中的步骤如下:

  1. 获得常量池的长度
  2. 创建constantPoolOop
  3. 调用parse_constant_pool_entries 解析常量池操作
  4. 验证交叉引用和修复类和字符串常量
  5. 检查string是否是正确的格式

获取长度

在解析常量池之前,已经处理了魔数,读取class文件主,次版本号及验证.在ClassFileParser::parse_constant_pool中获得常量池的长度的代码如下:

ClassFileStream* cfs = stream();
constantPoolHandle nullHandle;cfs->guarantee_more(3, CHECK_(nullHandle)); // 获得constant_pool_count 和第一个常量池表项的类型
// 1. 获得长度
u2 length = cfs->get_u2_fast();
guarantee_property(
length >= 1, "Illegal constant pool size %u in class file %s",
length, CHECK_(nullHandle)); // 常量池的表项个数必须是大于等于1的

此处回顾一下class文件的格式:

ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;  --> 在调用ClassFileParser::parse_constant_pool时ClassFileStream的指针就指向这里
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}

其中对于常量池相关项的含义描述如下:

  • constant_pool_count

    常量池计数器,constant_pool_count的值等于constant_pool表中的成员数加1。constant_pool表的索引值只有在大于0且小于constant_pool_count时才会被认为是有效的,对于long和double类型有例外情况。虽然值为0的constant_pool索引是无效的,但其他用到常量池的数据结构可以使用索引0来表示“不引用任何一个常量池项”的意思 该规范说明了constant_pool_count 必须是大于等于1

  • constant_pool[]

    常量池,constant_pool是一种表结构,它包含Class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量。常量池中的每一项都具备相同的格式特征——第一个字节作为类型标记用于识别该项是哪种类型的常量,称为“tag byte”。常量池的索引范围是1至constant_pool_count−1

    其中常量池项都具有如下通用格式:

    cp_info {
    u1 tag;
    u1 info[];
    }
    

    常量池中,每个cp_info项的格式必须相同,它们都以一个表示cp_info类型的单字节“tag”项开头。后面info[]项的内容tag由的类型所决定。tag有效的类型和对应的取值在表4.3列出。每个tag项必须跟随2个或更多的字节,这些字节用于给定这个常量的信息,附加字节的信息格式由tag的值决定。jvm中可识别的tag如下:

因此,此处获取常量池的长度就可以直接通过读取2个字节后得出(当前得通过字节次序变换).


创建constantPoolOop

创建constantPoolOop调用的方法为: oopFactory::new_constantPool.代码如下:


// 位于hotspot/src/share/vm/classfile/classFileParser.cppconstantPoolOop constant_pool =oopFactory::new_constantPool(length,methodOopDesc::IsSafeConc, // 默认为trueCHECK_(nullHandle));// 位于hotspot/src/share/vm/memory/oopFactory.cpp
constantPoolOop oopFactory::new_constantPool(int length,bool is_conc_safe,TRAPS) {constantPoolKlass* ck = constantPoolKlass::cast(Universe::constantPoolKlassObj());return ck->allocate(length, is_conc_safe, CHECK_NULL);
}
  1. 获取在Universe::genesis(TRAPS)方法中创建的_constantPoolKlassObj.

  2. 调用constantPoolKlass::allocate 进行分配.其步骤如下:

    1. 调用constantPoolOopDesc::object_size(length) 计算要分配内存的大小

    2. 调用CollectedHeap::permanent_obj_allocate进行分配.

    3. 初始化constantPoolOop 实例域变量.这里涉及的代码如下:

         c->set_length(length);c->set_tags(NULL);c->set_cache(NULL);c->set_operands(NULL);c->set_pool_holder(NULL);c->set_flags(0);// only set to non-zero if constant pool is merged by RedefineClassesc->set_orig_length(0);// if constant pool may change during RedefineClasses, it is created// unsafe for GC concurrent processing.c->set_is_conc_safe(is_conc_safe);
      
    4. 初始化tag 数组(常量池表项所使用的数组).

其中,constantPoolOopDesc::object_size(length) 的方法如下:

static int header_size()             { return sizeof(constantPoolOopDesc)/HeapWordSize; } // 10
static int object_size(int length)   { return align_object_size(header_size() + length); }inline intptr_t align_object_size(intptr_t size) {return align_size_up(size, MinObjAlignment); // 其中 MinObjAlignment 为2
}#define align_size_up_(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1))inline intptr_t align_size_up(intptr_t size, intptr_t alignment) {return align_size_up_(size, alignment);
}

总之,其最终返回的大小为: sizeof(constantPoolOopDesc)/HeapWordSize + length 后对齐的结果.

sizeof(constantPoolOopDesc) 的结果为 40,原因如下:

constantPoolOopDesc 继承自oopDesc,其具有如下字段:

volatile markOop  _mark;union _metadata {wideKlassOop    _klass;narrowOop       _compressed_klass;} _metadata;// Fast access to barrier set.  Must be initialized.static BarrierSet* _bs; // 该字段不参与计算

因此,在32位的情况下, oopDesc的大小为8.
而在64位的情况下,其大小为如果未开启压缩,则为16,否则为12(_metadata 的大小为4).

而在constantPoolOopDesc 中如下字段:

typeArrayOop         _tags; // the tag array describing the constant pool's contents
constantPoolCacheOop _cache;         // the cache holding interpreter runtime information
klassOop             _pool_holder;   // the corresponding class
typeArrayOop         _operands;      // for variable-sized (InvokeDynamic) nodes, usually empty
int                  _flags;         // a few header bits to describe contents for GC
int                  _length; // number of elements in the array
volatile bool        _is_conc_safe; // if true, safe for concurrent// GC processing
// only set to non-zero if constant pool is merged by RedefineClasses
int                  _orig_length;

共8个,其大小为32.

因此,在32位上,sizeof(constantPoolOopDesc)/HeapWordSize + length = 40/4+ length = 10+ length.


其中,调用CollectedHeap::permanent_obj_allocat这一步骤,在类加载流程-002 中有介绍,这里就不在介绍了.不过这里需要补充一点的是,这里为什么分配大小时传入的size为10+ length? 会不会不够用呢?

答案在MutableSpace::allocate中,代码如下:

HeapWord* MutableSpace::allocate(size_t size) {assert(Heap_lock->owned_by_self() ||(SafepointSynchronize::is_at_safepoint() &&Thread::current()->is_VM_thread()),"not locked");/** 注意,这里先执行HeapWord* obj = top();再执行 HeapWord* new_top = obj + size;,而最终返回的是obj指针,该指针指向的是原来的堆的最顶端,* 这样,调用方通过指针可以还原从原顶端到当前堆顶之间的内存空间,将其强制转换为常量池对象.*/HeapWord* obj = top();if (pointer_delta(end(), obj) >= size) {HeapWord* new_top = obj + size; // 将PermSpace内存区域的top指针往高地址移动了size大小的字节数,完成了当前被加载的类所对应的常量池的内存分配set_top(new_top);assert(is_object_aligned((intptr_t)obj) && is_object_aligned((intptr_t)new_top),"checking alignment");return obj;} else {return NULL;}
}

其中,方法的参数size 是一路传过来的,分配时是通过HeapWord* new_top = obj + size; 进行分配的.这里涉及到了指针运算,因此,其最终的大小为: (10+ length) * 4 . (HeapWord 的大小为4).

接下来,我们介绍一下创建tag 数组(常量池表项所使用的数组)的过程,其代码如下:

constantPoolHandle pool (THREAD, c);
typeArrayOop t_oop = oopFactory::new_permanent_byteArray(length, CHECK_NULL);
typeArrayHandle tags (THREAD, t_oop);
for (int index = 0; index < length; index++) {
tags()->byte_at_put(index, JVM_CONSTANT_Invalid);
}
pool->set_tags(tags());

步骤如下:

  1. 首先,调用oopFactory::new_permanent_byteArray 创建typeArrayOopDesc::object_size(layout_helper(), length)大小的数组.

  2. 然后将其进行初始化,其值为:JVM_CONSTANT_Invalid

  3. 让constantPoolOop 和tag 数组 进行关联.

此时, constantPoolOop的内存结构如下:

这里有个问题,为啥constantPoolOop 为额外分配length个大小的内存,另外又用过tags指向一个typeArrayOop呢? 这个我们在下篇文章中通过介绍ClassFileParser::parse_constant_pool_entries来进行介绍.

类文件解析003-解析常量池相关推荐

  1. 《深入理解Java虚拟机》——类文件结构之魔数常量池

    相对于Java虚拟机的其他部分,这部分的内容我们只需要搞清楚下面两个方面的内容: 1.无关性 2.Class文件的结构与组成 我们都知道Java有个特性是:一次编写,到处运行.这里体现的是平台无关性, ...

  2. 【深入理解JVM】Java类文件的基本结构

    Java类文件(.class文件)是一个为已编译Java程序仔细定义的格式.Java源代码被编译成能够被任何JVM加载和执行的类文件.在被JVM加载之前,类文件可能是由网络传输而来. 类文件是独立于底 ...

  3. Java常量池解析与字符串intern简介

    在Java应用程序运行时,Java虚拟机会保存一份内部的运行时常量池,它区别于class文件的常量池,是class文件常量池映射到虚拟机中的数据结构. 关于class文件常量池的部分可以参考之前的博文 ...

  4. java基础(八) 深入解析常量池与装拆箱机制

    ###引言 本文将介绍常量池 与 装箱拆箱机制,之所以将两者合在一起介绍,是因为网上不少文章在谈到常量池时,将包装类的缓存机制,java常量池,不加区别地混在一起讨论,更有甚者完全将这两者视为一个整体 ...

  5. java怎编写么解析一个类型_DAY3:你必须知道的java虚拟机之类篇——类文件的结构...

    马上过年啦,不知道大家今年有没有投资基金股票呢?是赚的盆满钵满还是拍断大腿,可以评论区一起交流交流,秀一秀哈哈,反正我是没来得及上车. 暴富西不可能暴富的啦,打工人嘛几能写写文章啦-记得点赞➕关注呀 ...

  6. 深入解析常量池与装拆箱机制

    2019独角兽企业重金招聘Python工程师标准>>> 常量池 常量 可分为 字面常量(也称为直接常量)和 符号常量. 字面常量: 是指在程序中无需预先定义就可使用的数字.字符.bo ...

  7. java 字符串池 原理_《Java虚拟机原理图解》1.2.2、Class文件中的常量池详解(上)...

    注意: 对于某个类而言,其class文件中至少要有两个CONSTANT_Class_info常量池项,用来表示自己的类信息和其父类信息.(除了java.lang.Object类除外,其他的任何类都会默 ...

  8. class字节码文件中的常量池结构详解

    文章目录 前言 方法区 常量池基本结构 JVM 所定义的11种常量 常量池元素的复合结构 常量池的结束位置 常量池元素总数量 第一个常量池元素 父类常量 变量型常量池元素 自己的学习笔记,部分节选自& ...

  9. 3.内存分配、逃逸分析与栈上分配、直接内存和运行时常量池、基本类型的包装类和常量池、TLAB、可达性分析算法(学习笔记)

    3.JVM内存分配 3.1.内存分配概述 3.2.内存分配–Eden区域 3.3.内存分配–大对象直接进老年代 3.3.1.背景 3.3.2.解析 3.4.内存分配–长期存活的对象进去老年代 3.5. ...

最新文章

  1. R语言使用magick包的image_write函数将已有图像以任何指定的格式导出保存到磁盘上(例如将原文将从png转化为jpeg)
  2. Citrix XenApp 下载及一年 developer license 获取
  3. linux网卡口闪烁,LINUX 下网卡口绑定整理
  4. php代码格式化工具 php-cs-fixer的使用
  5. [置顶] 动软软代码生成器使用(127.0.0.1)无法看到 SQLServer2008 新附加数据库的 原因 以及 解决方案...
  6. Java反射机制概念及应用场景
  7. 【BZOJ3670】【codevs3319】动物园,KMP+时间优化
  8. 《Photoshop Lightroom4 经典教程》—第1课复习题答案
  9. Eclipse-properties文件乱码问题
  10. 第五章 Windows基础控件
  11. springMVC接受对象集合,name数组
  12. H3C 命令行历史记录功能
  13. NodeJS stream 一:Buffer
  14. 34.在排序数组中查找元素的第一个和最后一个位置(力扣leetcode) 博主可答疑该问题
  15. kuka机器人齿轮箱油_库卡KUKA机器人保养润滑油00-144-898
  16. 【Verilog TestBench教程】
  17. PDF文件怎么制作,PDF文件制作方法
  18. 19年6月英语六级第一套听力单词
  19. 微信小程序底部导航栏
  20. 通过Oracle的回收站恢复被删除的表浅谈

热门文章

  1. hive常用的内置函数
  2. 数据结构c语言版谭浩强pdf,谭浩强C语言_数据结构.pdf
  3. 程序猿面试题:为什么曹操能号令诸侯?
  4. 如何提炼好的软文标题
  5. 国网对计算机二级科目要求,今起!计算机等级考试可以网报,二级部分科目获证条件调整...
  6. Oh my God, 连jQuery都放弃IE了!
  7. ES6 lterator迭代器是个什么东西?有什么用?
  8. 用阿里云托管服务器怎么托管_云托管就像圣诞老人的4种方式
  9. 5月6日—5月9日三年级课程新
  10. 50件事爸爸一定要与孩子做(转)