Java虚拟机(第二版) 学习笔记
Java技术体系
- sun官方所定义的Java技术体系包括:1、Java程序设计语言 2、各种硬件平台上的Java虚拟机 3、Class文件格式 4、Java API类库 5、来自商业机构和开源社区的第三方Java类库
- 我们可以把Java程序设计语言,Java虚拟机,Java API 类库这三个部分统称为JDK,JDK是用于支持Java程序开发的最小环境
- Java API类库中的Java SE API子集和Java虚拟机这两个部分统称为JRE(Java Runtime Environment)
Java内存区域与内存溢出异常
- 运行时数据区域:
程序计数器
- 程序计数器一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器的工作就是通过改变这个计数器的值来选取下一个要执行的指令,分支,循环,跳转,异常处理,线程恢复等基本功能。程序计数器是线程私有的。
Java虚拟机栈
- Java虚拟机栈也是线程私有的,它的生命周期和线程相同。每个方法执行的同时,都会创建一个虚拟机栈用来栈帧(Stack Frame)用来存储局部变量表,操作数栈,动态链接,方法出入口等信息。每一个方法的调用到执行完成,对应了一个栈帧在虚拟机栈中的入栈到出栈的过程。
- 局部变量表:存放了编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double),以及一些对象引用(非对象本身)
- 其中64位的long和double会占2个局部变量空间,其他的都占一个
- Java虚拟机的规范中,针对这个区域有规定了两个异常。1、如果线程请求的栈深度大于虚拟机栈允许的深度,将抛出StackOverflowError. 2、如果虚拟机栈可以动态扩展,但是又无法申请到足够的内存,就会抛出OutOfMemoryError。
Java堆
- Java堆是虚拟机管理的最大的一块内存,被所有线程共享。随虚拟机启动而创建,存在的唯一目的就是存放对象实例。
- Java堆是垃圾收集器管理的主要区域,因此很多时候被称为“GC堆”。
方法区
- 方法区与Java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。
运行时常量池
- 运行时常量池是方法区的一部分,Class文件中除了有类的版本,字段,方法和接口等描述信息外,还有一项信息就是常量池,用于存放编译期生成的各种字面量和符号引用。这部分将在类记载后进入方法区的运行时常量池中存放。运行时常量池还有另一个特性就是动态性,运行期间也可以将新的常量放入池中。
对象的创建
- 虚拟机遇到一条new指令,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且解析这个类的是否已经被加载,解析,初始化过
- 通过检查后,虚拟机将为新生对象分配内存。假设Java堆中的内存时规整的,一边是空闲的一边是已使用的,中间放着一个指针作为分界点的指示器,那么分配内存就是移动相应大小的指针区域,这种方式叫做指针碰撞。如果Java堆中的内存是散乱的,那么虚拟机必须要维护了列表,记录哪些内存是空闲的,然后再列表中找到相应大小的内存,并且更新列表。这种方式叫做 "空闲列表"。Java堆是否规整由所采用的垃圾收集器是否有压缩整理功能而决定。
- 问题:频繁的创建对象,在并发情况下并不是前程安全的,解决的方案有两种1、堆分配内存的操作进行同步处理 2、把分配内存的动作按照线程划分在不同的区域内,即每个线程在Java堆预先分配一小块内存,称为本地线程分配缓存,哪个线程需要分配缓存,就在线程的TLAB上分配
- 内存分配完,虚拟机将分配到的内存都初始化零值(不包括对象头)
- 对对象进行一系列必要的设置,之后再执行<init>方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象就算完全产出了
对象的访问
- 两种方式,句柄和直接指针
- 句柄:优点:reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而reference本身不需要改变
- 直接指针:优点:速度快,节省了一次指针定位的时间