深入理解jvm虚拟机读书笔记

运行时数据区域

image.png

程序计数器

当前线程执行字节码的行号指示器,通过改变程序计数器的值来选取下一调需要执行的字节码命令,分支,循环,跳转,异常处理,线程恢复等功能都依赖于程序计数器.每个线程都有独立的程序计数器,互不影响.
此内存区域在Java虚拟机规范中是唯一没有OOM情况的区域.

虚拟机栈

虚拟机栈也是线程独有的,每个方法在执行的同时都会建立一个栈帧,用于储存局部变量表,操作数栈,动态链接,方法入口.每一个方法调用完成都是都是栈帧入栈和出栈的过程.
局部变量表:存放了在编译期可预知的基本数据类型和对象引用类型.
这个区域规定了两种异常,如果线程栈请求深度大于虚拟机规定深度则会抛出StackOverflowError异常,如果虚拟机栈可以动态扩展扩展的时候请求的无法申请到足够的内存空间就会抛出OOM异常

本地方法栈

和虚拟机栈一样

Java堆

虚拟机管理内存最大的一块,存放所有的对象实例(数组也在堆上分配),所有线程共享的一块内存区域.可细分为老年代和新生代.更细一点可以分为Eden, From Survivor, To Survivor空间. 当堆中没有内存完成实例对象的内存分配则会抛出OOM.

方法区

线程共享区域,用于存储被虚拟加载的类信息,常量,静态变量即时编译的代码等数据.

对象的创建

当虚拟机遇到一个new指令的时候, 会到常量池中去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否被加载过,解析过初始过, 如果没被加载则会进行类的加载过程.类被加载后虚拟机将为新生对象分配内存空间,类的内存空间在类被加载后就会被确定,两种分配方式:

指针碰撞:假设java堆是决对规整的所有使用的内存放在一边, 未使用的内存放在另一边中间放着指针作为分界点指示器所分配的内存空间只是将指针像空闲的一方移动.

空闲列表:java堆的内存块不规整的时候,不能使用简单的指针碰撞的方式给新对象分配内存空间, 需要记录那些内存块是空闲的,在给对象分配内存空间的时候在列表中找到一块足够大的内存空间并更新列表.
采用那种分配方式是根据内存空间是否规整决定, 例如空闲列表一般使用CMS方式gc,内存分配是线程不安全的,有两种方式解决这种问题:一种是虚拟机采用CAS配上失败重试的方式来保证更新操作的原子性,另一种方式是把不同线程内存分配的动作划分到不同的空间中处理,每个线程在Java堆上会事先分配一小块内存空间成为本地线程分配缓冲(TLAB), 那个线程要分配内存就在那块TLAB上进行内存分配.只用当TLAB分配完成需要分配新的TLAB是才会进行同步锁定.

对象的内存布局

1.对象头:分为两部分信息, 一部分是存储对象自身运行时的数据,另一部分时类型指针,即对象指向它的类元素数据指针,虚拟机通过这个指针来确定这个对象是那个类的实例.
2.实例数据
3.对齐填充:对象大小必须时8字节的整数倍,可能需要对齐填充来不全.

对象的访问定位

Java虚拟机通过虚拟机栈中的reference数据来操作堆上的具体对象,这个引用去定位具体对象有两种方式, 通过句柄访问另一种直接访问:


image.png
image.png

通过句柄池定位对象实例的好处在于对象的实例在Java虚拟机内存中位置时经常发生变化的,不由频繁的去改变reference的具体指向, 通过指针直接访问对象则效率会高一点.

你可能感兴趣的:(深入理解jvm虚拟机读书笔记)