深入理解JVM虚拟机--读书笔记(第二章)

来源:互联网 发布:手机恢复数据 编辑:程序博客网 时间:2024/06/05 11:30
java虚拟机运行时的数据区一共有五个:方法区,堆,虚拟机栈,本地方法栈,程序计数器。
(1)程序计数器
是一块较小的内存区域,可以看作是当前线程所执行的字节码的行号指示器,每个独立的线程都有一个程序计数器,互不影响,也称为线程的“私有内存”,且是虚拟机中不会发生OutOfMemoryError的区域
(2)java虚拟机栈
是线程私有的,生命周期和线程相同。
每个方法在执行时都会创建一个栈帧,用于存储局部变量表(八大数据类型,对象引用。其中double和long占据两个空间),操作数栈,动态链接,方法出口等。
若线程请求的栈深度大于虚拟机所允许的深度,则抛出StackOverflowError的错误。
局部变量表存放了编译器可知的各种基本数据类型、对象引用和returnAddress类型;其所需的内存空间在编辑期完成分配,不会再运行期改变;
可能存在两种异常:StackOverflowError和OutOfMemoryError;
(3)本地方法栈
本地方法栈则为虚拟机使用到的native方法服务。
(4)java 堆
是java虚拟机所管理内存中最大的一块,是被所有线程共享的一块内存,在虚拟机启动时创建。
此堆的唯一目的就是存放对象实例,几乎所有的对象实例都在这分配。
java堆是垃圾收集器管理的主要区域,也称为”GC堆“,堆可细分为新生代和老生代
(5)方法区
是各个线程共享的内存区域。
用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译后的代码等数据。也成为永久代,在这区上面很少发生垃圾回收。
(6)运行时常量池
是方法区的一部分,用于存放编译期生成的各种字面量和符号引用。
(7)直接内存
直接内存是被频繁使用的内存区域,可能导致outOfMemoryError等错误。

对象的创建
(1)首先检查这个指令参数是否在常量池中定位到一个符号引用,检查是否已被加载,解析及初始化
(2)接下来虚拟机为新生代对象分配内存,常用方法有:“指针碰撞”和“空闲列表”两种方法
(3)虚拟机对对象进行必要的设置,并存储在对象头中,自此初始化完成。


对象的内存布局可以分为三个区域:对象头,实例数据和对齐填充
(1)对象头:一部分用于存储对象自身运行时的数据,一部分是类型指针,即对象指向它的类元素数据的指针,用于确定这个对象是哪个类的实例。
(2)实例数据:表示对象真正存储的有效信息,在分配策略中,相同宽度的字段总是被分配到一起。


对象的访问定位需要依赖栈上的reference数据来操作堆上的具体对象。主流的访问方法:使用句柄和直接指针两种。
  • 栈上的reference类型在虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该通过何种方式去定位、访问堆栈对象的具体位置,目前主流的方式方式有句柄和直接直接两种。
  • 通过句柄:Java堆中划出一块内存作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。其最大好处就是reference存储的是稳定的句柄地址,在对象被移到(垃圾收集时移到)只改变实例数据指针,而reference不需要修改;
  • 通过直接指针:Java堆对象的布局中必须考虑如果放置访问类型数据的相关信息,而reference中存在的直接就是对象地址。其最大好处在于速度更快,节省了一次指针定位的时机开销。HotSpot采用该方式进行对象访问,但其他语言和框架采用句柄的也非常常见。



































































































原创粉丝点击