当前线程所执行的字节码的行号指示器
对于多线程来讲,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。
Java方法执行的内存模型,存储局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈和出栈的过程。
为虚拟机使用到的Native
方法服务,而虚拟机栈为虚拟机执行Java
方法(也就是字节码)服务
内存中最大的一块。存放对象实例,而且是垃圾收集器管理的主要区域。
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
方法区的一部分,用于存放编译期生成的各种字面量和符号引用。
init
方法GC Roots
到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制会就会产生内存溢出异常cglib
,大量jsp
或动态产生jsp
文件等主流虚拟机不选用该方法来管理内存。
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
缺点:很难解决对象之间相互循环引用的问题。
定义:通过一系列的称为“GC Roots
”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots
没有任何引用链时,则证明此对象是不可用的。
GC Roots
的对象包括以下几种:
1. 虚拟机栈(栈帧中的本地变量表)中引用的对象。
2. 方法区中类静态属性引用的对象
3. 方法区中常量引用的对象
4. 本地方法栈中JNI
(即一般说的Native
方法)引用的对象
方法区的主要回收两部分内容:废弃常量和无用的类
Eden
和Survivor
比例为8:1Minor GC
和Major GC
的区别:前者发生在新生代,后者发生在老生代略
两种数据类型:
- 无符号数:用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值
- 表:由多个无符号数或者其他表作为数据项构成的复合数据类型,以”_info”结尾
Class文件
- 前4个字节:0xCAFEBABE
- 第5和第6个字节:次版本号
- 第7和第8个字节是主版本号
- 常量池
- 访问标志:用于识别一些类或者接口层次的访问信息
- 类索引、父类索引与接口索引集合
- 字段表集合:用于描述接口或者类中声明的变量
- 方法表集合
- 属性表集合(Class文件、字段表、方法表中都可以携带)
常量池主要存放两大类常量:
1. 字面量
- 文本字符串
- 声明为final
的常量值
2. 符号引用
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Oprands)而构成。
注意:大部分的指令没有支持整数类型byte
、char
和short
,和boolean
,编译期会在编译期或运行期将byte
和short
类型的数据带符号扩展为相应的int
类型数据,将boolean
和char
类型数据零位扩展为相应的int
类型数据。
类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型
注意:类型的加载、连接和初始化过程都是在程序运行期间完成的。
类的生命周期:总共7个阶段,如图
必须立即对类进行初始化的有且仅有的5种情况:
1. 使用new
实例化对象、读取或设置一个类的静态字段、调用一个类的静态方法
2. 对类进行反射调用
3. 当初始化一个类的时候,其父类还没被初始化的时候
4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()
方法的那个类)
5. 当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle
实例最后的解析结果REF_getStatic
、REF_putStatic
、REF_invokeStatic
的方法句柄,并且这个方法句柄所对应的类没有进行过初始化
java.lang.Class
对象,作为方法区这个类的各种数据的访问入口()
方法的过程(包括类变量的赋值和静态语句块)注意:比较两个类是否“相等”,只要加载它们的类加载器不同,那这两个类就必定不相等。
工作过程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
栈帧:是用于支持虚拟机进行方法调用和方法执行的数据结构。如下图所示
invokedynamic
字节码指令略
即时编译器:
当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”(Hot Spot Code),为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化。
两种特性:
1. 保证此变量对所有线程具有可见性
2. 禁止指令重排序优化
注意:volatile
保证此变量对所有线程具有可见性,但是变量的运算在并发下一样是不安全的
使用场景:
- 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值
- 变量不需要与其他的状态变量共同参与不变约束
原子性
long
和double
除外)lock
和unlock
满足更大范围的原子性,虚拟机提供了monitorenter
和moniterexit
字节码指令,反应到Java代码中就是同步块——synchronized
关键字可见性
volatile
、synchronized
和final
都能实现可见性有序性
指的是Java内存模型中定义的两项操作之间的偏序关系,如果说操作A先行发生于操作B,其实就是说在发生操作B之前,操作A产生的影响能被操作B观察到,“影响”包括修改了内存中共享变量的值、发送了消息、调用了方法等。
final
即可;将对象中带有状态的变量都声明为final
Vector
、HashTable
、Collections
的synchronizedCollection()
方法包装的集合。ArrayList
和HashMap
。Thread
类的suspend()
和resume()
方法。synchronized
关键字和ReentrantLock
ReentrantLock
增加的三个功能: compareAndSet()
和getAndIncrement()
等方法都是用了Unsafe
类的CAS操作java.lang.ThreadLocal
类来实现线程本地存储的功能。String
字符串的拼接操作。