简书 占小狼

转载请注明原创出处,谢谢!

趁着年轻,多学习

背景

最近对Java细节的底层实现比较感兴趣,比如Java类文件是如何加载到虚拟机的,类对象和方法是以什么数据结构存在于虚拟机中?虚方法、实例方法和静态方法是如何调用的?本文基于openjdk-7的OpenJDK实现Java类在HotSpot的内部实现进行分析。

HotSpot内存划分

在HotSpot实现中,内存被划分成Java堆、方法区、Java栈、本地方法栈和PC寄存器几个部分:

1、Java栈和本地方法栈用于方法之间的调用,进栈出栈的过程;

2、Java堆用于存放对象,在Java中,所有对象的创建都在堆上申请内存,并被GC管理;

3、方法区分成PermGen和CodeCache:PermGen存放Java类的相关信息,如静态变量、成员方法和抽象方法等;CodeCache存放JIT编译之后的本地代码;

更详细的相关内容可以阅读《JVM内存的那些事》

HotSpot对象模型

HotSpot JVM并没有根据Java对象直接通过虚拟机映射到新建的C++对象,而是设计了一个oop/klass model,其中oop为Ordinary Object Pointer,用来表示对象的实例信息;klass用来保存描述元数据。

Klass

关于为何要设计oop/klass这种二分模型的实现,一个原因是不想让每个对象都包含vtbl(虚方法表),其中oop中不含有任何虚函数,虚函数表保存于klass中,可以进行method dispatch。

oop

oopDesc对象包含两部分数据:_mark 和 _metadata;

1、_mark是markOop类型对象,用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,占用内存大小与虚拟机位长一致,更具体的实现可以阅读 java对象头的HotSpot实现分析。

2、_metadata是一个结构体,wideKlassOop和narrowOop都指向InstanceKlass对象,其中narrowOop指向的是经过压缩的对象;

3、_klass字段建立了oop对象与klass对象之间的联系;

HotSpot如何加载并解析class文件

class文件在虚拟机的整个生命周期包括加载、验证、准备、解析、初始化、使用和卸载7个阶段,通过ClassLoader.loadClass方法可以手动加载一个Java类到虚拟机中,并返回Class类型的引用。

这里并没有自定义类加载器,而是利用ClassLoaderCase的类加载器进行加载类AAA。

loadClass方法实现

1、loadClass方法实现了双亲委派的类加载机制,如果需要自定义类加载器,建议重写内部的findClass方法,而非loadClass方法;

2、通过debug,可以发现loadClass方法最终会执行native方法defineClass1进行类的加载,即读取对应class文件的二进制数据到虚拟机中进行解析;

class文件的解析

Java中的defineClass1方法是个native方法,说明依赖于底层的实现,在HotSpot中,其实现位于ClassLoader.c文件中,最终调用jvm.cpp中的jvm_define_class_common方法实现,核心的实现逻辑如下:

1、验证全限定类名的长度,最大为(1 << 16) -1,如果长度超过 65535,就会抛出java/lang/NoClassDefFoundError异常,主要原因是constant pool不支持这么长的字符串;

2、SystemDictionary::resolve_from_stream处理stream数据流,并生成Klass对象。内部通过ClassFileParser.cpp的parseClassFile方法对class文件的数据流进行解析,代码实在实在实在实在太长,有兴趣的同学可以阅读完整的实现,大概的过程如下:

1、验证当前magic为0xCAFEBABE;

2、获取class文件的minor_version、major_version,并判断当前虚拟机是否支持该版本;

3、通过parse_constant_pool方法解析当前class的常量池;

4、解析当前class的access_flags;

5、解析当前class的父类;

6、解析当前class的接口;

7、....

好吧,我得承认这块逻辑很复杂...

class数据流解析完成后,通过oopFactory::new_instanceKlass创建一个与之对应的instanceKlass对象,new_instanceKlass实现如下:

1、其中instanceKlassKlass::allocate_instance_klass方法会初始化一个空instanceKlass对象,并由后续逻辑进行数据的填充;

2、但是发现该方法的返回类型并非是instanceKlass,而是klassOop类型;

3、allocate_instance_klass方法的实现如下:

1、base_create_klass方法最终通过Klass::base_create_klass_oop方法创建Klass对象,这里是instanceKlass对象,并返回对应的klassOop;

2、k()->klass_part()获取对应的Klass对象,并强制转换成instanceKlass类型的对象;

3、设置instanceKlass对象的默认值;

Klass对象如何创建?

上述的instanceKlass对象由Klass::base_create_klass_oop方法进行创建,实现如下:

1、allocate_permanent方法默认在PermGen分配内存,instanceKlass对象保存在永久代区域;

2、Klass的as_klassOop方法可以获取对应的klassOop,那klassOop到底是什么?

klassOop相当于Java中的class,一个klassOop对象包含header、klass_field和Klass。

instanceKlass

可以发现,每个instanceKlass对象都有一个ClassState状态,用来标识当前class的加载进度,另外instanceKlass对象中包含了如下字段,描述class文件的信息。

instanceKlassKlass

instanceKlassKlass在实现上继承了klassKlass类

全局只存在一个instanceKlassKlass对象,虚拟机启动时,会在Universe::genesis方法中初始化。

虚拟机中所有instanceKlass对象的_klass字段都指向该instanceKlassKlass对象,其初始化过程如下:

1、方法Universe::klassKlassObj()获取klassKlass对象;

2、方法base_create_klass负责创建instanceKlassKlass对象,并返回对应的klassOop;

3、方法java_lang_Class::create_mirror分配mirror,类似于一个镜像,在java层面可以访问到;

klassKlass

klassKlass在实现上继承了Klass类

和instanceKlassKlass一样,klassKlass对象也是全局唯一的,虚拟机启动时,会在Universe::genesis方法中初始化,其初始化过程如下:

1、通过base_create_klass创建klassKlass对象,并返回对应的klassOop;

2、set_klass方法把自身设置成_klass;

我是占小狼

坐标魔都,白天上班族,晚上是知识的分享者

如果读完觉得有收获的话,欢迎点赞加关注

描述一下JAVA的加载过程_JVM源码分析之Java类的加载过程相关推荐

  1. java中Mark接口_JVM源码分析之Java对象头实现

    原标题:JVM源码分析之Java对象头实现 原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 "365篇原创计划"第十一篇. 今天呢!灯塔君跟大家讲: JVM源码分析之Ja ...

  2. mysql insert执行过程_MySQL · 源码分析 · 一条insert语句的执行过程

    本文只分析了insert语句执行的主路径,和路径上部分关键函数,很多细节没有深入,留给读者继续分析 create table t1(id int); insert into t1 values(1) ...

  3. Mybatis 源码分析(一)配置文件加载流程

    Mybatis 源码分析(一)配置文件加载流程 1.项目构建 引入依赖 <dependency><groupId>org.mybatis</groupId>< ...

  4. 【SA8295P 源码分析】08 - XBL Loader 加载 SMSS、XBL Config、SHRM、CDT 、DDR、APDP、RamDump、OEM_MISC、AOP、QSEE过程分析

    [SA8295P 源码分析]08 - XBL Loader 加载 SMSS.XBL Config.SHRM.CDT .DDR.APDP.RamDump.OEM_MISC.AOP.QSEE Dev Co ...

  5. java多线程系列:ThreadPoolExecutor源码分析,java基础面试笔试题

    我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码或搜索下图红色VX号,加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起 ...

  6. docker push 过程 distribution源码 分析

    docker push 过程 distribution源码分析 承接上一篇"distribution structure and start up 分析"本文分析一下distrib ...

  7. WebRTC[1]-WebRTC中h264解码过程的源码分析

    目录 前言 正文 <WebRTC工作原理精讲>系列-总览_liuzhen007的专栏-CSDN博客_webrtc 原理前言欢迎大家订阅Data-Mining 的<WebRTC工作原理 ...

  8. 这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析

    前言 package com.jvm.classloader;class Father2{public static String strFather="HelloJVM_Father&qu ...

  9. Tomcat7.0源码分析——server.xml文件的加载与解析

    前言 作为Java程序员,对于Tomcat的server.xml想必都不陌生.本文基于Tomcat7.0的Java源码,对server.xml文件是如何加载和解析进行分析. 加载过程分析 Bootst ...

最新文章

  1. 禁用java rmi_java-如何安全关闭rmi客户端?
  2. oninput,onpropertychange,onchange的用法和区别
  3. hyper服务器虚拟网卡和实际网卡,Hyper-V 3 虚拟网卡带宽应用限制
  4. MySQL笔记-解决...mysql.sock (13)(两种情况会产生此问题)
  5. 抖音封杀小猪佩奇,一年赚100亿的“社会人”得罪了谁?
  6. fianl属性 java_java基础-类的高级属性(包、final、内部类)
  7. GOTURN——基于深度学习的物体追踪 (OpenCV contrib)
  8. oracle之sqlloader
  9. 联想如何在桌面显示计算机和网络图标,联想电脑桌面图标字体变小怎么办
  10. 模拟电路和数字电路的学习笔记(精华总结55条)
  11. gtx 750 linux驱动下载,Ubuntu 12.04安装NVIDIA GTX750显卡驱动
  12. 保护水资源公益网站html,保护水资源公益广告词
  13. 静态条件下三轴加速度求角度的算法
  14. Aliyun 阿里云 机器翻译调用 详解
  15. 【读书笔记】计算机是怎样跑起来的 - 读书笔记(2)
  16. vue 项目中分别使用 vue-pdf 插件和内嵌 iframe 实现 PDF 文件预览,缩放,旋转,下载,保存等功能 ?
  17. NFT艺术品交易平台有哪些?
  18. 神经网络与BP算法(代码实现)
  19. 没有密码如何解除工作表保护
  20. 单精度浮点数(float)与双精度浮点数(double)的区别如下:

热门文章

  1. c语言存储类_C编程语言的存储类
  2. Java ObjectInputStream registerValidation()方法与示例
  3. 最常见的10种Java异常问题!
  4. 45 张图深度解析 Netty 架构与原理
  5. Oracle 练习P297 131026 PL/SQL块程序
  6. JupyterHub on Kubernetes-Helm安装
  7. html里面怎么ul加高度,div里面嵌套了ul,为什么div的高度小于ul高度
  8. 如何用java对接口发送请求_7. 用Java做接口测试1-发送HTTP请求和接收HTTP响应
  9. 路由虚拟端口配置dhcp服务器,交换机和路由器DHCP设置中的端口配置
  10. python内置函数open的解释_在python的内置open()函数中缓冲的用途是什么?