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. Reface.AppStarter 类型扫描 —— 获得系统中所有的实体类型

    类型扫描 是 Reface.AppStarter 提供的最基本.最核心的功能. AutoConfig , ComponentScan 等功能都是基于该功能完成的. 每一个使用 Reface.AppSt ...

  2. Spring Boot 2.3.0正式发布:优雅停机、配置文件位置通配符新特性一览

    当大潮退去,才知道谁在裸泳..关注公众号[BAT的乌托邦]开启专栏式学习,拒绝浅尝辄止.本文 https://www.yourbatman.cn 已收录,里面一并有Spring技术栈.MyBatis. ...

  3. JSON基础使用详解

    JSON:JavaScript对象表示法 1.1 JSON说明 曾经一段时间,XML是互联网传递数据的统一标准,但是业界一直不乏质疑XML的人士,他们都认为XML过于繁琐,冗长:提取数据也过于麻烦 2 ...

  4. 【Nginx】如何为已安装的Nginx动态添加模块?看完我懂了!!

    写在前面 很多时候,我们根据当时的项目情况和业务需求安装完Nginx后,后续随着业务的发展,往往会给安装好的Nginx添加其他的功能模块.在为Nginx添加功能模块时,要求Nginx不停机.这就涉及到 ...

  5. Redis 6.0 新特性 ACL 介绍

    Redis 6.0 新特性 ACL 介绍 Intro 在 Redis 6.0 中引入了 ACL(Access Control List) 的支持,在此前的版本中 Redis 中是没有用户的概念的,其实 ...

  6. canvas : 几个入门需要的基本概念

    这段时间做项目需要用canvas. 而我在看文档的时候,发现canvas是一个很独立的API:和DOM BOM基本上没什么关系. 在学习canvas的时候需要了解很多概念,否则看某些文档的讲解可能会看 ...

  7. javascript : splice(0) 的妙用

    今天看前同事的代码,有个写法很有意思: splice(0). 有意思的是,这并不是一个“合法”的写法. w3school是这样写的: arrayObject.splice(index,howmany, ...

  8. 浅谈6种JS数组遍历方法的区别

    本篇文章给大家介绍一下6种JS数组遍历方法:for.foreach.for in.for of.. each. ().each的区别.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. ...

  9. 请解释ASP.NET中的web页面与其隐藏类之间的关系

    其实页面与其隐藏类之间就是一个部分类的关系,你在页面上放一个一个的控件就是在这个类中定义一个一个的属性, 因为是同一个类的部分类的关系,所以隐藏类可以访问到页面上控件,这样做是为了把展现与处理逻辑分开 ...

  10. PHP入门之类型与运算符

    前言 PHP对于大部分人来说,是比较容易入门的.笔者也是刚学习不久,所以就把自己学习的基础知识进行总结和整理.第一部分是类型与运算符.如果你想学习PHP,可以参考PHP学习手册学习,任何一本教学资料也 ...