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. Spring中异步注解@Async的使用、原理及使用时可能导致的问题

    前言 其实最近都在研究事务相关的内容,之所以写这么一篇文章是因为前面写了一篇关于循环依赖的文章: <面试必杀技,讲一讲Spring中的循环依赖> 然后,很多同学碰到了下面这个问题,添加了S ...

  2. Android 性能优化---布局优化

    Android 性能优化---布局优化 Android 布局绘制原理 布局加载过程 setContentView() --> inflate() -- > getLayout()(I/O操 ...

  3. GitHub和码云gitee及远程仓库管理

    目录 备注: 知识点 GitHub 码云(gitee.com) gitee的使用 本地版本库关联多个远程库 备注: 本文参考于廖雪峰老师的博客Git教程.依照其博客进行学习和记录,感谢其无私分享,也欢 ...

  4. 自学 Python 到什么程度能找到工作,1300+ 条招聘信息告诉你答案

    随着移动互联网的发展以及机器学习等热门领域带给人们的冲击,让越来越多的人接触并开始学习 Python.无论你是是科班出身还是非科班转行,Python 无疑都是非常适合你入门计算机世界的第一门语言,其语 ...

  5. unittest学习笔记

    File "C:\Program Files\Python36\lib\site-packages\selenium\webdriver\remote\errorhandler.py&quo ...

  6. C++11 STL Regex正则表达式与字符串字段解析

    简单的日期正则表达式 一个简单的日期解析程序,从yyyy-mm-dd格式的日期字符串中,分别获取年月日. 先设置一个简单的正则表达式,4位数字的"年",1-2位数字的"月 ...

  7. JavaFX布局神器-SceneBuilder

    JavaFX允许开发使用FXML来设计和布局界面,跟Qt和Android的布局有点类似,JavaFX用SceneBuilder来设计和布局界面. SceneBuilder最新的下载地址:https:/ ...

  8. Bug:No mapping for GET /onepill//swagger-ui.html

    SpringBoot使用Swagger2本来可以使用的,后来出现的异常No mapping for GET /swagger-ui.html,这个异常其实不用怎么解释,说白了就是找不到了. 遇到这种情 ...

  9. Spring+hibernate+JSP实现Piano的数据库操作---1.目录结构+展示

    目录结构 界面

  10. Windows下使用图形化mount挂载磁盘到文件夹

    Windows上也有类似于Linux上的mount命令,至于mount是什么: mount是Linux下的一个命令,它可以将分区挂接到Linux的一个文件夹下,从而将分区和该目录联系起来,因此我们只要 ...