《深入理解JVM》学习笔记(一)

书名:《深入理解Java虚拟机:JVM高级特性与最佳实践》

第一章 走进Java

JDK:Java程序设计语言+Java虚拟机+JavaAPI类库,支持Java开发的最小环境

JRE:JavaSE API子集+Java虚拟机,支持Java程序运行的标准环境

 

Java发展史

1991年4月,java语言的前身:Oak语言

1995年,Java

1996,JDK1.0

1998年,JDk1.2  分为J2SE(面向桌面),J2EE(面向企业),J2ME(面向移动终端)

1999年,HotSpot虚拟机(JDK1.3及以后版本的默认虚拟机)

2004年,JDK1.5  自动装箱、泛型、动态注解、枚举、可变长参数、foreach循环等

2006年,JDK1.6 更改为:Java SE、Java EE、Java ME      对虚拟机的改进:锁与同步、垃圾收集、类加载等

2009年,JDK1.7

2009年4月,Java商标归Oracle公司

 

Java虚拟机发展史

JDK1.0:Classic VM,需要外挂编译器

JDK3.0后:HotSpot VM

 

第二章 Java内存区域与内存溢出异常

运行时数据区域

程序计数器

一块较小的内存空间,当前线程所执行的字节码的行号指示器。(类似于CPU里的程序计数器)

由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间来实现的,在一个确定的时刻,一个处理器(或者多核处理器的一个内核)都只会执行一条线程中的指令。所以,每个线程都需要有一个独立的程序计数器,这类内存区域为“线程私有的”内存。

Java虚拟机栈

线程私有,生命周期与线程相同。描述Java方法执行的内存模型,每个方法在执行时都会创建一个栈帧(Stack Frame):存储局部变量表(包括基本数据类型、对象引用和returnAddress类型(指向了一条字节码指令的地址),在编译期间完成分配,运行时不会改变)、操作数栈、动态链接、方法出口等信息。方法从调用到执行完成,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

有人把Java内存区简单的分为堆内存和栈内存,这时的栈指的就是虚拟机栈。

线程的栈深度大于虚拟机允许的深度,抛出StackOverflowError异常

若虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,抛出OutOfMemoryError异常

本地方法栈

与虚拟机栈类似,只不过是为本地(Native)方法服务。

有的虚拟机(如Sun HotSpot)直接将本地方法栈和虚拟机栈合二为一。

Java堆

被所有线程共享,在虚拟机启动时创建。唯一目的是存放对象实例,几乎所有的对象实例都会在这里分配内存。

Java堆是垃圾收集器管理的主要区域,很多时候也被称作“GC堆”(Garbage Collected Heap)。

内存:逻辑上连续即可,可以实现成固定大小或者可扩展的(当前主流都是可扩展的)。

内存用尽并且无法扩展时,抛出OutOfMemoryError异常

方法区

线程共享,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

运行时常量池:方法区的一部分

 

HotSpot虚拟机对象探秘

1、对象的创建

一条new指令,检查指令参数,执行类加载

为新生对象分配内存(内存大小在类加载时就可确定):指针碰撞法(内存是规整的,空闲内存和已用内存之间有一个指针)或者空闲列表法(虚拟机维护一个内存列表)

分配内存时为了保证线程安全:采用两种方法:(1)虚拟机采用CAS配上失败重试的方式保证更新操作的原子性(2)每个线程在堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB),只有TLAB用完并分配新TLAB时,才需要同步锁定。

内存空间初始化,设置对象头,

2、对象的内存布局

对象的内存区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)

对象头:(1)自身运行时数据,包含哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,官方称之为“Mark Word”。(2)类型指针:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。另外,若对象是Java数组,对象头中必须包含数组长度(所以new数组要指明长度)

实例数据:程序代码中定义的各种类型的字段(包括从父类继承下来的)。

对齐填充:并不是必然存在的,仅仅起着占位符的作用。HotSpot VM自动内存管理系统要求对象的起始地址必须是8字节的整数倍。若实例数据部分没有对齐时,需要对齐填充补全。

3、对象的访问定位

Java程序需要通过栈上的reference数据来操作堆上的具体对象。但是,reference类型在虚拟机规范中规定了一个指向对象的引用,没有定义具体实现,其实现方式由具体的虚拟机决定。目前主流方式为使用句柄直接指针两种。

句柄访问:Java堆中会划分一块内存作为句柄池,reference中存储的就是对象的句柄地址。句柄中包括对象实例数据和类型数据各自的具体地址信息(即指针)。好处是存储的信息稳定,在对象被移动时只会改变实例数据指针,reference本身不变。

直接指针访问:reference中存储的直接就是对象地址。好处是速度快,因为节省了一次指针定位的时间。HotSpot使用这种方式。

你可能感兴趣的:(读书笔记)