1. JVM的位置
运行在操作系统上
2. JVM的体系结构
简图
JVM调优就是在调堆跟方法区(99%就是调堆)
3. 类加载器
3.1 作用
3.2 等级
- 虚拟机自带的加载器
- 启动类(根)加载器
- 扩展类加载器
null
- 不存在
- java程序获取不到
- 应用程序(系统类)加载器
定义的String跟java.lang 包中的String 同包名同类名 所以调用的是哪个?
4. 双亲委派机制
5. 沙箱安全机制
安全模型
Java安全模型的核心就是Java杀向(sandbox),什么是沙箱?
沙箱是一个限制程序运行的环境,沙箱机制就是将Java代码限定在JVM特定的运行范围中,并且严格限制代码对本地资源访问,通过这样的措施来保证对代码的有效隔离,感知对本地系统造成破坏。
沙箱主要限制系统资源访问,那么系统资源包括什么?
CPU、内存、文件系统、网络。
不同级别的沙箱对这些资源访问限制也可以不一样。
所有的Java程序运行都可以指定沙箱,可以指定安全策略。
在Java中年执行程序分本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信任的。对于授信的本地代码,可以访问一切本地资源,而对于非授信的远程代码在早期的Java实现中,完全依赖于沙箱机制
JDK1.0 安全模型
但如此严格的安全机制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时,就无法实现。
因此在后续的Java1.1版本中,针对安全机制做了改进,增加了安全策略,允许用户指定代码对本地资源的访问权限。
在JDK 1.2中,再次改进了安全机制,增加了代码签名。不论是本地代码还是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。
JDK1.2安全模型
当前最新的安全机制实现,则引入了域(Domain)的概念,虚拟机会把所有的代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域(Protect Domain),对应不同的权限。存在于不同域中的类文件就具有了当前域的全部权限。
jdk1.6安全模型
沙箱组成
- 字节码校验器(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类
- 类装载器:其实类装载器在3个方面对Java沙箱起作用
- 防止恶意代码去干涉善意的代码 //双亲委派机制
- 守护了被信任的类库边界
- 将代码归入了保护域,确定了代码可以进程哪些操作
虚拟机为不同的类加载器载入的类提供了不同的命名空间,命名空间由一些列唯一的名称组成,每一个被装在的类都将有一个名字,这个命名空间是Java虚拟机为每一个类加载器维护的,他们互相之间甚至不可见
namespace 隔离?
字节码校验发生的时机
类从被加载到内存中,到被卸载出内存,一共分为以下几步:
- 加载
- 验证
- 准备
- 解析
- 初始化
- 使用
- 卸载
加载:
验证:
这一阶段是为了保证Class文件的字节流中包含的信息符合当前JVM的要求,并且不危害JVM自身的安全。大致分为以下四个阶段:
- 文件格式验证
6. Native
凡是带了native关键字的,说明不是java的作用范围,要调用底层方法
native
进入本地方法栈
调用本地方法接口(JNI)
JNI作用:扩展Java的使用,融合不同的语言供Java使用
它在内存区域中专门开辟了一块标记区域,Native 方法栈,登记本地方法
在最终执行时,通过JNI加载本地方法库中的方法
7. PC寄存器
8. 方法区
方法区是被所有线程共享的,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的的信息都保存在该区域,此区域属于共享空间;
静态变量、变量、类信息(构造方法、接口顶底)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关。
**
类信息与类常量池
方法区的class文件信息包括:
《深入理解JVM虚拟机》第六章 类文件结构
任何一个Class文件都对应着唯一的一个类或接口的定义信息,但是反过来说,类或接口并不一定都得定义在文件中(比如类或接口也可以动态生成,直接送入类加载器中)。
Class文件是一组以8个字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在文件之中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎全部是程序运行的必要数据,没有空隙存在。当遇到需要占用8个字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8个字节进行存储。
idea打开Class文件:
用十六进制编辑器(010 editor)打开 .class 文件
Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构中只有两种数据类型:“无符号数”和“表”.
无符号数属于基本的数据类型,以 u1,u2,u4,u8来分别标识1个字节,2个字节,4个字节和8个字节的无符号数。无符号数可以用来描述数字,索引引用,数量值或者按照UTF-8编码构成字符串值。
表时由多个无符号数或者其他表作为数据项构成的符合数据类型,为了便于区分,所有表的命名都习惯以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上也可以视作是一张表,这张表按照严格的顺序排列构成。
魔数:Magic Number
唯一作用:确定这个文件是否是一个能被虚拟机接受的Class文件
GIF和JPEG等文件在文件头中都存在魔数
版本号
常量池
常量池可以比喻为Class文件里的资源仓库,它是Class文件结构中与其他项目关联最多的数据,通常也是占用Class文件空间最大的数据项目之一。
常量池中常量的数量是不固定的。
所以在常量池的入口需要放置一项u2类型的数据。代表常量池容量计数值。
这个计数从1开始!!!!!!!!!!!
把第0项空出来的意义在于
如果后面某些指向常量池的索引值的数据在特定情况下(什么特定情况?)需要表达“不引用任何一个常量池项目”的含义,可以把索引值设置为0来表示。
确实计数从1开始
常量池中主要存放两大类常量:
- 字面量
- 文本字符串
- final常量
- 基本数据类型的值
- 其他
- 符号引用:编译原理方面的概念
- 被模块导出或者开放的包
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
- 方法句柄和方法类型
- 动态调用点和动态常量
**
Java代码在编译的时候,并没有C和C++那样有连接这一个步骤,而是在虚拟机加载Class文件时进行动态连接。
问题:
public class ConstantTest {
public static void main(String[] args) {
int a = 66;
int b = 32768;
}
}
为什么小于32768的数没有放在class文件的常量池种?
把 a,b改成Integer类型
说明short类型范围内的int,Integer 是直接放到栈中的,而不是class常量池。
截至JDK13,常量表中共有17种不同类型的常量
这17种常量类型各自有着完全独立的数据结构,两两之间并没有什么共性和联系。
**
JVM :常量池 运行时常量池 字符串常量池
运行时常量池:
把class文件的常量池中的 #后边的数字变为真正的地址。
所有class共享?
字符串常量池
字符串常量池 位于堆中,不是方法区中,创建字符串时首先去字符串常量池中查找是否存在此常量,存在的话直接拿来用,不存在的话在堆中创建一个字符串变量,然后放到常量池中。
Integer常量池和String常量池
访问标志
类索引、父类索引和接口索引集合
类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名。由于Java语言不允许多
重继承,所以父类索引只有一个,除了java.lang.Object之外,所有的Java类都有父类,因此除了
java.lang.Object外,所有Java类的父类索引都不为0。
字段表集合
用于描述接口或者类中声明的变量。Java语言中的“字段”(Field)包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。
方法表集合
与字段表集合相对应地,如果父类方法在子类中没有被重写(Override),方法表集合中就不会出现来自父类的方法信息。但同样地,有可能会出现由编译器自动添加的方法,最常见的便是类构造器“
在Java语言中,要重载(Overload)一个方法,除了要与原方法具有相同的简单名称之外,还要求
必须拥有一个与原方法不同的特征签名。
属性表集合
9. 栈
存储内容
- 八大基本类型
- 对象的引用
- 实例的方法
实例的方法是什么?
栈帧
Java虚拟机以方法作为最基本的执行单元,“栈帧”(Stack Frame)则是用于支持虚拟机进行方法调用和方法执行背后的数据结构,它也是虚拟机运行时数据区中的虚拟机栈(Virtual MachineStack)[1]的栈元素。
《深入理解JVM虚拟机》栈帧概念结构