深入理解Java虚拟机(一)
主要是阅读《深入理解Java虚拟机》 之后所做的一些笔记。
更多内容可以访问我的个人博客。
Java的优点
摆脱了硬件平台的束缚,“Write once, compile anywhere”.
提供了一个相对安全的内存管理和访问机制。
热点代码检测和运行时编译及优化.
有一套完整的应用程序接口,以及无数的第三方库。
JDK(Java Development Kit):支持Java程序开发的最小环境,包括Java程序设计语言、Java虚拟机、Java API类库
JRE(Java Runtime Environment):支持Java程序运行的标准环境,包括Java API 类库中的JavaSE API子集、Java虚拟机
几款重要的JVM
1. Sun Classic(世界上第一款商用Java虚拟机)
只能使用纯解释器方式来执行Java代码(解释器和编译器不能配合工作)
也就是说若要使用编译器执行,则编译器要对每一个方法,每一行代码都进行编译,而无论它们执行的频率是否值得编译。导致无法使用编译优化技术,造成了Java语言很慢的最初印象
2. Exact VM
因使用准确式内存管理(Exact Memory Management)而得名(即VM中可以知道内存中某个位置的数据具体是什么类型)
已经具备现代高性能处理器的雏形,如两级即时编译器、编译器与解释器混合工作模式等。
3. Sun HotSpot VM(目前使用范围最广的Java虚拟机)
准确式内存管理
HotSpot—热点代码检测技术(通过执行计数器找出具有编译价值的代码)
编译器和解释器恰当地协同工作
运行时数据区域
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间。有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。
1. 程序计数器:当前线程所执行的字节码的行号指示器
线程私有(生命周期与线程相同)
字节码解释器工作时就是通过改变这个计数器的值来选取下一条需执行的字节码指令
此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域
2. Java虚拟机栈
线程私有(生命周期与线程相同)
是Java方法执行的内存模型
每个方法执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程
局部变量表
存放基本数据类型(byte、boolean、char、short、int、float、long、double),对象引用(reference类型),returnAddress类型(指向一条字节码指令的地址)
在编译期间完成分配,方法运行期间不会改变大小
当线程请求栈深大于VM允许栈深,报*Error错误;当VM栈扩展时无法申请到足够内存,报outOfMemoryError.
3. 本地方法栈.
与虚拟机栈非常相似,区别在于VM栈为VM执行Java方法(也就是字节码)服务,而本地方法栈则为VM使用到的Native方法服务
4. Java堆:对大多数应用来说,Java堆是Java VM所管理的内存中最大的一块。
所有线程共享,在虚拟机启动时创建
唯一的目的就是存放对象实例
是垃圾搜集器管理的主要区域
Java堆可以处于物理上不连续的内存空间中,逻辑是连续的即可
5. 方法区
线程共享
用于存储已被虚拟机加载的类信息(存放Class相关信息:类名、访问修饰符、常量池……)、常量、静态变量、即时编译器编译后的代码等。
不需连续内存,可固定大小也可扩展
对方法区的内存回收目标主要是针对常量池的回收和对类型的卸载
运行时常量池:
是方法区的一部分,Class文件中的常量池(Class文件中除了类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。)将在类加载后进入方法区的运行时常量池中存放
相对于Class常量池的两个特征:①Class中的每一部分的格式都有严格规定,每一个字节用于存储哪种数据都必须符合规范上的要求,而对于运行时常量池,Java虚拟机规范没有做任何细节上的要求。②具备动态性,运行期间也可能将新的常量放入池中。(**String.intern()**将新的String字符串加入字符串常量池)