在对Jvm底层的认知中看了不少的博客,里面内容参差不齐,终于在读了《深入理解Java虚拟机》后有了系统的认识,也发现在学习中百度搜索或者博客内容很重要但是要深入了解还是需要从书中获取,毕竟每个人对知识的理解都不同,这里记录下对这本书的读书笔记,并持续更新。
第2章
2.2运行时数据分区
程序计数区:可以看作是当前线程所执行的字节码的行号指示器,如果线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址,如果正在执行的是Native方法,这个计数器值为空,也是虚拟机规范中唯一没有OutOfMemoryError的区域
Java虚拟机栈:虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行时会创建一个栈帧用于存储局部变量表,每个方法从调用知道执行完成对应栈帧在虚拟机栈中入栈道出栈的过程;
本地方法栈:与java虚拟机栈大致相同,主要执行Native的方法,在Hotspot中本地方法栈和Java虚拟机栈合二为一(线程共享)
堆:存放对象实例,gc机制的主要地区,在内存中可不连续存储,需逻辑连续
方法区:存储已经被虚拟机加载的类信息、常量、静态变量(线程共享)常量池位于方法区(类加载后进入方法区的常量池存放)
2.3对象的创建
1、检查加载:遇到New指令 ,首先检查这个指令的参数是否能在常量池定位到类的符号引用,并且检查这个符号引用的代表的类是否已经被加载、解析和初始化过;
2、划分内存:为对象分配空间等同于把一块确定大小的内存从Java堆中划分出来,而这时根据内存在堆中的存储方式分为“指针碰撞”和“空闲列表”,但这时在并发情况下并不是线程安全的,解决方法有两种,一种对分配内存空间动作进行同步处理,另一种吧内存分配的动作按照线程划分在不同的空间中进行,每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓存(TLAB);
3、初始化:内存分配后将分配的内存空间都初始化为0(不包括对象头);
4、对对象进行必要设置(类得元数据信息、对象的哈希码、GV分代)存储在对象头中
对象的内存分布
对象头、实例数据、对齐填充
对象头第一部分用于存储对象自身的运行时数据(Mark work),第二部门是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实力
『注』如果对象是一个数组,那在对象头中还必须有一块用于记录对象数组长度的数据,应为虚拟机可以通过普通java对象的元数据信息确定java对象的大小,但是从数组的元数据中无法确定数组的大小,这里也就说明for(int i= arr.length; i>0; i--)和for(int i= 0; i<arr.length;i++)并不会有本质区别。
实例数据:无论从父类继承的还是子类定义的都需要记录下来,受到虚拟机分配策略参数和字段在Java源码中定义顺序的影响;例如相同宽度的字段分派在一起(double,long)
对齐填充并不是必然存在的起着占位符的作用
对象的访问定位
需要通过栈上的reference护具来操作堆上的具体对象。主流的访问方式有两种,句柄和直接指针访问;
句柄访问:堆中会划分出一块内存作为句柄池,reference中存储就是对象的句柄地址,句柄中也包含了对象实例数据与类型数据各自的具体地址信息
直接指针访问,在regerence存储的就是对象的地址
优劣分析:句柄最大的好处就是regerence中存储的是稳定的句柄地址,在对象被移动是只会改变句柄中的实例数据指针,而reference本身不受改变,直接指针的好处就是速度更快,