HotSpot通过Method与ConstMethod来保存方法元信息。

1、Method

Method没有子类,定义在method.hpp文件中,其类继承关系如下图:

Method用于表示一个Java方法,因为一个应用有成千上万个方法,因此保证Method类在内存中短小非常有必要。为了本地GC方便,Method把所有的指针变量和方法大小放在Method内存布局的前面。

方法本身的不可变数据,如字节码用ConstMethod表示,可变数据如Profile统计的性能数据等用MethodData表示,都通过指针访问。

如果是本地方法,Method内存结构的最后是native_function和signature_handler属性,按照解释器的要求,这两个必须在固定的偏移处。

Method类中声明的属性如下:

源代码位置:/vm/oops/method.hpp

class Method : public Metadata {
friend class VMStructs;
private:
ConstMethod* _constMethod; // Method read-only data.
// MethodData结构基础是ProfileData,记录函数运行状态下的数据
// MethodData里面分为3个部分,一个是函数类型等运行相关统计数据,一个是参数类型运行相关统计数据,
// 还有一个是extra扩展区保存着deoptimization的相关信息
MethodData* _method_data;
MethodCounters* _method_counters;
AccessFlags _access_flags; // Access flags
// vtable index of this method (see VtableIndexFlag)
// note: can have vtables with >2**16 elements (because of inheritance)
int _vtable_index; u2 _method_size; // size of this object
u1 _intrinsic_id; // vmSymbols::intrinsic_id (0 == _none)
// Flags,在定义时指定各个变量占用的位
u1 _jfr_towrite : 1, // Flags
_caller_sensitive : 1,
_force_inline : 1,
_hidden : 1,
_dont_inline : 1,
: 3; // Entry point for calling both from and to the interpreter.
address _i2i_entry; // All-args-on-stack calling convention /*
_adapter 指向该Java方法的签名(signature)所对应的 i2c2i adapter stub。其实是一个 i2c stub
和一个 c2i stub 粘在一起这样的对象,可以看到用的时候都是从 _adapter 取 get_i2c_entry() 或
get_c2i_entry()。这些adapter stub用于在HotSpot VM里的解释模式与编译模式的代码之间适配其
calling convention。HotSpot VM里的解释模式calling convention用栈来传递参数,而编译模式的
calling convention更多采用寄存器来传递参数,两者不兼容,因而从解释模式的代码调用已经被编译的方法,
或者反之,都需要在调用时进行适配。
*/
// Adapter blob (i2c/c2i) for this Method*. Set once when method is linked.
AdapterHandlerEntry* _adapter; /*
_from_compiled_entry 初始值指向c2i adapter stub。原因上面已经说了,因为一开始该方法尚未被JIT编译,
需要在解释模式执行,那么从已经JIT编译好的Java方法调用过来的话就需要进行calling convention的转换,
把参数挪到正确的位置上。当该方法被JIT编译并“安装”完之后,_from_compiled_entry 就会指向编译出来的机
器码的入口,具体说时指向verified entry point。如果要抛弃之前编译好的机器码,那么 _from_compiled_entry
会恢复为指向 c2i stub。
*/
// Entry point for calling from compiled code, to compiled code if it exists
// or else the interpreter.
// Cache of: _code ? _code->entry_point() : _adapter->c2i_entry()
volatile address _from_compiled_entry; // The entry point for calling both from and to compiled code is
// "_code->entry_point()". Because of tiered compilation and de-opt, this
// field can come and go. It can transition from NULL to not-null at any
// time (whenever a compile completes). It can transition from not-null to
// NULL only at safepoints (because of a de-opt).
// nmethod全名native method,指向的是Java method编译的一个版本。
// 当一个方法被JIT编译后会生成一个nmethod,指向的是编译的代码
// _code的类型为nmethod
nmethod* volatile _code; // Points to the corresponding piece of native code /*
_from_interpreted_entry 初始的值与 _i2i_entry 一样。但后面当该Java方法被JIT编译并“安装”之后,
_from_interpreted_entry 就会被设置为指向 i2c adapter stub。而如果因为某些原因需要抛弃掉之前已
经编译并安装好的机器码,则 _from_interpreted_entry 会被恢复为 _i2i_entry。
*/
// Cache of _code ? _adapter->i2c_entry() : _i2i_entry
volatile address _from_interpreted_entry; // 如果有_code,则通过i2c_entry转向编译方法,否则通过_i2i_entry转向解释方法 ...
}

HotSpot虚拟机中的Method存储在元数据区(在JDK1.8之前是PermGen)。

Method中最后定义的几个属性非常重要。用以方法的解释执行和编译执行。一个方法可能有多个入口:

1、_i2i_entry :指向字节码解释执行的入口;

2、_code->entry_point() :指向JIT编译代码的入口。编译后的代码存储在CodeCache中,这是专门为动态生成的代码开辟的一块本地内存;

3、i2c和c2i适配器:用来在解释执行和编译执行之间进行转换,由于解释执行和编译执行的调用约定不同,所以专门做了适配器来适配。

可以这样总结一下,如果转换的目标是解析执行,那么:

  • from_compiled_code_entry_point 的值为 c2i adapter
  • from_interpreter_entry_point 的值为 interpreter entry point

如果转换的目标是编译执行:

  • from_compiled_code_entry_point 的值为 nmethod entry point
  • from_interpreter_entry_point 的值为 i2c adapter

Method类中定义的各属性的介绍如下表所示。

字段名 描述
_constMethod

ConstMethod指针,该类定义在constMethod.hpp文件中,用于表示方法的不可变的部分,如方法ID,

方法的字节码大小,方法名在常量池中的索引等

_method_data

MethodData指针,该类在methodData.hpp中定义,用于表示一个方法在执行期间收集的相关信息,

如方法的调用次数,在C1编译期间代码循环和阻塞的次数,Profile收集的方法性能相关的数据等

_method_counters

MethodCounters指针,该类在methodCounters.hpp中定义,用于大量编译优化相关的计数,

  • 解释器调用次数
  • 解释执行时由于异常而终止的次数
  • 方法调用次数(method里面有多少方法调用)
  • 回边个数
  • 该方法曾经过的分层编译的最高层级
  • 热点方法计数

主要用于基于调用频率的热点方法的跟踪统计

_access_flags AccessFlags类,表示方法的访问控制标识
_vtable_index 该方法在vtable表中的索引
_method_size 这个Method对象的大小,以字宽为单位
_intrinsic_id

固有方法(intrinsic method)在虚拟机中表示一些众所周知的方法,针对它们可以做特别处理,生成独特的代码例程。

虚拟机发现一个方法是固有方法就不会走逐行解释字节码这条路径而是跳到独特的代码例程上面,

所有的固有方法都定义在hotspot\share\classfile\vmSymbols.hpp文件中

_compiled_invocation_count 编译后的方法叫nmethod,这个就是用来计数编译后的nmethod调用了多少次,如果该方法是解释执行就为0
_i2i_entry 解释器的入口地址
_adapter 此方法在解释器和编译器执行的适配器
_from_compiled_entry 执行编译后的代码的入口地址
_code nmethod类指针,表示该方法编译后的本地代码
_from_interpreted_entry code ? _adapter->i2c_entry() : _i2i_entry的缓存

基中_access_flags的取值如下:

_vtable_index的取值如下:

2、ConstMethod类介绍

ConstMethod对象代表方法中不可变的部分,例如字节码。类的定义如下:

源代码位置:/hotspot/src/share/vm/oop

class ConstMethod : public MetaspaceObj {
...
private:
... // 其中_constants,_method_idnum这两个是连接Method的参数,因为Method有ConstMethod指针,
// 但ConstMethod没有Method的指针,需要通过如下步骤来查找:
// ConstantPool -> InstanceKlass -> Method数组->通过_method_idnum获取对应的Method指针
// 原文链接:https://blog.csdn.net/raintungli/article/details/83857136
ConstantPool* _constants; // Constant pool int _constMethod_size;
u2 _flags; // Size of Java bytecodes allocated immediately after Method*.
u2 _code_size;
u2 _name_index; // Method name (index in constant pool)
u2 _signature_index; // Method signature (index in constant pool)
u2 _method_idnum; // unique identification number for the method within the class
// initially corresponds to the index into the methods array.
// but this may change with redefinition
u2 _max_stack; // Maximum number of entries on the expression stack
u2 _max_locals; // Number of local variables used by this method
u2 _size_of_parameters;// size of the parameter block (receiver + arguments) in words
...
}

该类用于表示方法的不可变部分,如方法的id(_method_idnum),方法的字节码大小,方法名在常量池中的索引等,其中_constMethod_size的大小以字宽为单位,_code_size的大小以字节为单位,其内存结构如下图,因为异常检查表平均长度小于2,本地变量表大多数情况下没有,所以这两个没有被压缩保存。访问这些内嵌表都很快,不是性能瓶颈。ConstMethod提供了获取内嵌部分,如字节码的起始地址,然后可以据此偏移地址获取的方法字节码。

相关文章的链接如下:

1、在Ubuntu 16.04上编译OpenJDK8的源代码

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)

7、HotSpot的类模型(3)

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)

10、HotSpot的对象模型(6)

11、操作句柄Handle(7)

12、句柄Handle的释放(8)

13、类加载器

14、类的双亲委派机制

15、核心类的预装载

16、Java主类的装载

17、触发类的装载

18、类文件介绍

19、文件流

20、解析Class文件

21、常量池解析(1)

22、常量池解析(2)

23、字段解析(1)

24、字段解析之伪共享(2)

25、字段解析(3)

26、字段解析之OopMapBlock(4)

作者持续维护的个人博客classloading.com

关注公众号,有HotSpot源码剖析系列文章!

 

参考文章:

1、JVM内部运行之Class的Method

 

方法解析之Method与ConstMethod介绍的更多相关文章

  1. Method Resolution Order – Python类的方法解析顺序

    在支持多重继承的编程语言中,查找方法具体来自那个类时的基类搜索顺序通常被称为方法解析顺序(Method Resolution Order),简称MRO.(Python中查找其它属性也遵循同一规则.)对 ...

  2. jvm源码解读--06 Method 方法解析

    进入 // Methods bool has_final_method = false; AccessFlags promoted_flags; promoted_flags.set_flags(0) ...

  3. Python的方法解析顺序(MRO)[转]

    本文转载自: http://hanjianwei.com/2013/07/25/python-mro/ 对于支持继承的编程语言来说,其方法(属性)可能定义在当前类,也可能来自于基类,所以在方法调用时就 ...

  4. sqlalchemy mark-deleted 和 python 多继承下的方法解析顺序 MRO

    sqlalchemy mark-deleted 和 python 多继承下的方法解析顺序 MRO 今天在弄一个 sqlalchemy 的数据库基类的时候,遇到了跟多继承相关的一个小问题,因此顺便看了一 ...

  5. 浅谈C++设计模式之工厂方法(Factory Method)

    为什么要用设计模式?根本原因是为了代码复用,增加可维护性. 面向对象设计坚持的原则:开闭原则(Open Closed Principle,OCP).里氏代换原则(Liskov Substitution ...

  6. Objective-C运行时编程 - 方法混写 Method Swizzling

    摘要: 本文描述方法混写对实例.类.父类.不存在的方法等情况处理,属于Objective-C(oc)运行时(runtime)编程范围. 编程环境:Xcode 6.1.1, Yosemite,iOS 8 ...

  7. iOS 详解NSXMLParser方法解析XML数据方法

    前一篇文章已经介绍了如何通过URL从网络上获取xml数据.下面介绍如何将获取到的数据进行解析. 下面先看看xml的数据格式吧! <?xml version="1.0" enc ...

  8. 【Android 多媒体开发】 MediaPlayer 状态机 接口 方法 解析

    作者 : 韩曙亮 转载请著名出处 :  http://blog.csdn.net/shulianghan/article/details/38487967 一. MediaPlayer 状态机 介绍 ...

  9. 乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pattern)

    原文:乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 工厂方法模式(Factory Method Pa ...

随机推荐

  1. cnn卷积理解

    首先输入图像是28*28处理好的图. 第一层卷积:用5*5的卷积核进行卷积,输入为1通道,输出为32通道.即第一层的输入为:28*28图,第一层有32个不同的滤波器,对同一张图进行卷积,然后输出为32 ...

  2. LeetCode第4题:寻找两个有序数组的中位数

    double Solution::findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) ...

  3. Ripple 20:Treck TCP/IP协议漏洞技术分析

    本文由“合天智汇”公众号首发,作者:b1ngo Ripple 20:Treck TCP/IP协议漏洞技术分析 Ripple20是一系列影响数亿台设备的0day(19个),是JSOF研究实验室在Trec ...

  4. project facet java 1.8 is not supported解决办法

    Right click on project -> Properties -> Search for Project Facets -> Java (Version)

  5. [Qt2D绘图]-04绘制文字&&绘制路径

    注:学习自<Qt Creator 快速入门>第三版.   文档中的示例参考 Qt Example推荐:Painter Paths Example和Vector Deformation   ...

  6. ajax根据坐标查询WMS地图服务属性信息

    <html lang="en"> <head> <meta charset="UTF-8"> <meta name=& ...

  7. try-catch- finally块中, finally块唯一不执行的情况是什么?

    除非在try块或者catch块中调用了退出虚拟机的方法(即System.exit(1);),否则不管在try块.catch块中执行怎样的代码,出现怎样的情况,异常处理的finally块总是会被执行的 ...

  8. JVM系列之:String,数组和集合类的内存占用大小

    目录 简介 数组 String ArrayList HashMap HashSet LinkedList treeMap 总结 简介 之前的文章中,我们使用JOL工具简单的分析过String,数组和集 ...

  9. CSS和JS实现文本溢出显示省略号

    本文记录实现文本溢出显示省略号的几种方式. 单行文本 三行CSS代码实现: overflow: hidden; // 文本溢出隐藏 text-overflow: ellipsis; // 显示省略号 ...

  10. Spring的SchedulingConfigurer实现定时任务

    前提:在做业务平台的时候我们经常会遇到,某些跟时间打交道的需要修改状态,比如说在时间区间之前,属于未生效状态,区间之内属于有效期,区间之后,属于过期,或者需要每天 每周 每月,甚至是年为单位的做一些固 ...