深入分析Java虚拟机

Java虚拟机是相对于物理机的概念,也就是对现有冯诺依曼体系结构的硬件系统的抽象,现有计算机硬件系统是由输入,运算器、控制器、存储器和输出构成的。Java虚拟机则是建立在硬件系统之上,提供自己的规范,使在虚拟机上运行的程序具有跨平台、自动回收内存等诸多特性。

虚拟机内存划分概述

Java虚拟机对内存划分为5种不同的区域,分别是栈内存、本地方法栈、堆内存、方法区、程序计数器。

栈内存

栈内存是线程私有的,它随着线程的创建而创建,随着线程的销毁而销毁,在执行方法时会创建栈帧,栈帧中存放的是局部变量表(方法的参数以及局部变量)、操作数栈(指令所操作的数据存放处)等信息。方法的调用就是栈帧入栈的过程。

本地方法栈

执行Native方法时所用到的存储单元

堆内存

堆内存是线程共享的,当虚拟机启动时便会创建。该内存区域是用来存放对象实例的,也是垃圾收集器管理的主要区域。

方法区

方法区是线程共享的内存区域,它存储的主要是在类加载时产生的数据,可以理解为是存储Class文件信息或者是类相关信息的。所以编译后的字节码、常量、静态变量等与对象无关只有类有关的数据都放在这里。

程序计数器

程序计数器作为线程执行时的行号指示器,分支、跳转、循环都是通过它实现的,在任一时刻,一个处理器核都只会执行一条线程中的指令,每一个核对应一个程序计数器。

Class文件详解

Java原码中的各种变量,关键字和运算符号的语义最终都是由多条字节码命令组合而成的,而ava原码通过编译器编译为虚拟机可加载的Class文件,Class文件中包含了Java字节码和符号表和其他辅助信息。

Class文件是一组以8位字节为原子单位的二进制流,各个数据严格按照规定的顺序紧凑的排列(顺序中包含着信息),而Class文件只包含两种数据类型:

无符号数 -- 表示数字、索引引用、数量值、或按照UTF-8表示字符串,通过u1、u2、u4表示1字节2字节4字节

表 -- 是由多个无符号数组成的复合结构,所有表都以"_info"结尾

类型 名称 描述 数量
u4 magic 魔数 1
u2 minor_version 副版本号 1
u2 major 主版本号 1
u2 constant_pool_count 常量池数量 1
cp_info constant_pool 常量池 常量池数量-1
u2 access_flags 访问标识 1
u2 this_class 类索引 2
u2 super_class 父类索引 2
u2 interfaces_count 接口数量 1
u2 interfaces 接口索引集合 接口数量
u2 fields_count 字段数量 1
field_info fields 字段表 字段数量
u2 methods_counts 方法数量 1
method_info methods 方法表 方法数量
u2 attributes_count 属性数量 1
attributes_info attributes 属性表 属性数量

魔数以及版本号

Class文件的前4个字节是魔数它的唯一作用就是确定这个文件是Class文件,接下来2个字节是副版本号,再接下来两个字节是主版本号,用来确定版本号。

常量池数量

由于每个Class文件的常量数量不是固定的,所以紧接着主版本号后面的是常量池数量,用来确定常量的数量

常量池

根据常量池的数量,后面是常量的具体内容,常量池中包含两大类常量:

  1. 字面量:字面量就是没有特殊意义的常量,就是用来表示常量,比如文本字符串(字段名等),或是被final修饰的常量值等

  2. 符号引用包括以下三类

类和接口的全限定名(用来确定类和接口)

字段名称和描述符(用来确定字段及字段的其他信息)

方法名称和描述符(用来确定字段及字段的其他信息)

类型 标识 描述
CONSTONT_utf8_info 1 utf8编码字符串
CONSTONT_Integer_info 3 整型字面量
CONSTONT_Float_info 4 浮点型字面量
CONSTONT_Long_info 5 长整型字面量
CONSTONT_Double_info 6 双精度浮点型字面量
CONSTONT_Class_info 7 类或者接口的符号引用
CONSTONT_String_info 8 字符串类型字面量
CONSTONT_Fieldref_info 9 字段的符号引用
CONSTONT_Methodref_info 10 方法的符号应用
CONSTONT_InterfaceMethodref_info 11 接口方法的符号应用
CONSTONT_NameAndType_info 12 字段或方法的部分引用
CONSTONT_MethodHandle_info 15 方法的句柄
CONSTONT_MethodType_info 16 方法的类型
CONSTONT_InvokeDynamic_info 18 动态方法调用点

常量池中的数据是以_info结尾,前面讲到Class文件只有两种类型,无符号数和表,而常量池的数据全部都是表,也就是由多个无符号数组成的复合结构。

常量池是提供其他表进行引用的,一个class文件中的所有出现的字符都该在常量池中显示

访问标识

在常量池结束之后,接着的是2个字节的访问标识,用于识别该文件是类还是接口、是否为public、是否为abstract等信息

类索引、父类索引、接口索引集合

在访问标识后的为类索引,是这个类的的全限定名,指向常量池中的CONSTONT_Class_info

字段表和方法表

字段表用于表述接口或者类中声明的变量。

类型 名称 描述
u2 access_flags 描述字段、方法作用域等信息
u2 name_index 字段、方法简单名称常
u2 descriptor_index 字段、方法描述符
u2 attributes_count 属性数量
attributes_info attributes 属性表

属性表

属性表的作用是对其他表额外进行描述,下面是一些常用的属性。

名称 使用位置 描述
code 方法表 编译后的字节码指令
Exceptions 方法表 方法抛出的异常

其中code属性非常重要,如果把Java程序中的信息分为代码和元数据两部分,那么Code属性就是用来描述代码的,其他所有数据项都是用来描述元数据。

虚拟机类加载过程

虚拟机类加载过程就是将原来磁盘中的代码加载到内存中。

类加载过程生命周期

类加载的生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。

其中加载、验证、准备、初始化、卸载这五个过程顺序是确定的,而解析阶段有可能在初始化前也有可能在初始化后,在初始化前的解析是静态绑定(前期绑定),也就是可以在编译时确定,而初始化后的解析是动态绑定(后期绑定)。

加载
  1. 通过类的全限定名来获取类的二进制流
  2. 将类加载到方法区中
  3. 生成这个类的Class对象,获取方法区数据的入口(反射时会使用的)
验证

验证合法性

准备

分配类变量内存,并且赋初值

解析

将运行时常量池(类文件中的常量池加载到方法区)的符号引用替换为直接引用(内存地址)。

初始化

执行<clinit>方法,为准备阶段类变量的初值赋值。<clinit>方法是编译器自动生成的,相当于为静态字段赋值的方法,并且会合并static{}代码块的代码。(加载阶段还为涉及到具体对象)

类加载器

上面的生命周期是由类加载器完成的,对于任何一个类,都需要通过类加载器和这个类来确定在JVM中的唯一性。(同一个类不同类加载器加载后的Class对象的equals()方法返回false)

双亲委派机制

如果有一个类加载器收到加载请求,它首先会请求父加载器去加载,所以会导致所有加载请求都会到顶层的启动类加载器去;当父加载器无法完成加载请求(搜索范围没有这个类),就传递给子加载器加载。

字节码执行过程

字节码源代码进过编译又经过类加载器加载到方法区中的,字节码的执行代表着程序运行时一个方法的执行。

运行时栈帧

栈帧是方法执行时的数据结构,主要包括:局部变量表、操作数栈、动态连接、方法返回地址等。

局部变量表

用来存放方法参数和方法的变量的存储空间。它的大小在编译期间就已经确定,存放到Code属性中。

操作数栈

当方法开始执行时,操作数栈是空的,方法执行过程根据字节码会往操作数栈中写入和提取内容。

动态连接

每个栈帧都包含一个指向运行时常量池中所属方法的引用,这个引用是为了完成方法调用过程中的动态连接(确定方法),其中静态方法和私有方法是编译器可知,运行期不可变的。所以在编译期就可以确定。如重载就是需要动态连接来确认的

方法返回地址

就是方法的返回地址。。。。。

垃圾回收机制

垃圾回收是回收堆内存中不使用的对象。

判断对象是否存活

  1. 引用计数算法
    每当有一个地方引用对象就将计数加1,当引用失效就减1,当计数器为零就说明对象不再被使用了。(无法解决对象循环引用)
  2. 可达性算法
    通过一系列GC Root 对象作为起点,当一个对象到GC Root 没有任何引用链相连就证明对象不可使用

垃圾收集算法

  1. 标记清除算法
    标记需要回收的对象,之后统一回收。(会产生内存碎片)
  2. 复制算法
    将内存二等分,每次使用一半,当一半内存使用完之后触发复制算法,将存活的对象复制到另外一半中。(内存缩小到原来的一半)。
    适用于新生代,大量对象死去,少量存活,移动很少的对象。
  3. 标记整理
    标记需要回收的对象,将所有存活对象移动到一端,清除边界意外的内存。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容