小言_互联网的博客

深入理解JAVA虚拟机(第三版)第二章阅读笔记

314人阅读  评论(0)

深入理解Java虚拟机第二章

1.Java内存区域与内存溢出异常

1.1运行时数据区域

包含:线程共享数据区

​ 线程隔离数据区

线程共享数据区包括:方法区,Java堆

线程隔离数据区包括:虚拟机栈,本地方法栈,程序计数器


1.1.1程序计数器

可以看作当前线程执行的字节码的行号指示器

每个线程都有独立的程序计数器

如果执行的是Java方法,计数器记录的是当前正在执行的虚拟机字节码指令的地址。

如果执行的是本地方法,记录值为空

没有规定OutOfMemory异常


1.1.2Java虚拟机栈

线程私有,生命周期与线程相同

栈帧内设有局部变量表,局部变量表存放

​ (1)Java虚拟机基本数据类型(int,boolean…)

​ (2)对象引用(reference类型,不是对象本身)

​ (3)returnAddress类型(指向字节码指令地址)

方法运行期,局部变量表大小不变

请求栈深度溢出,抛出StackOverflow溢出,若栈可动态扩展,且无法申请到足够内存,抛出OutOfMemory异常


1.1.3本地方法区

与虚拟机栈类似,为Native方法服务


1.1.4Java堆

是Java虚拟机管理的内存中最大的一块

是垃圾收集器管理的内存区域

存放对象实例,原则上所有对象实例和数组都在堆上分配(随着即时编译,逃逸分析等技术的发展不再绝对)

当堆无法完成对象实例内存的分配,且无法拓展时抛出OutOfMemory异常。


1.1.5方法区

各线程共享的内存区域

存放常量,静态变量,即时编译器编译后的代码缓存,已被虚拟机加载的类信息(版本,字段,方法,接口)

无法满足内存分配时,抛出OutOfMemory异常


1.1.6运行时常量池

为方法区的一部分

class文件内含有一张常量池表(这张表可以看作一张HashSet),用于存放编译期产生的字面量和符号引用,类加载后,这部分内容存入方法区的运行时常量池

通过new创建的对象都在堆中,不在运行时常量池内

String s="123";//常量池内
String s1=new String();//堆内

运行期间也可以将新的常量放入池中,常用String的intern()方法(把堆中内容放入常量池)

常量池无法申请到足够内存时,抛出OutOfMemory异常


1.1.7直接内存

NIO类,避免了在Java堆和Native堆间来回复制数据,一些场景下显著提高性能

直接内存分配不受java堆内存的限制,但受到本机总内存的限制,也会抛出OutOfMemory异常


1.2对象

1.2.1对象的创建

第一步,接收到new指令后,先检查指令参数是否能在常量池中定位到一个类符号的引用,并检查这个符号引用代表的类是否已被加载解析和初始化过,若否,执行相应过程

第二步,为新生对象分配内存。

主要方法有“指针碰撞”(通过移动指向空闲内存区域与使用过的区域交界处的指针分配内存,要求内存规整,垃圾回收器有压缩整理能力)和“空闲列表”(维护一张记录着内存块使用情况的表)

内存分配存在线程安全问题,解决方案有同步处理(加锁),TLAB(本地线程分配缓冲,把内存分配的动作按线程分配在不同的缓冲区中,只有缓冲区需要扩展时才执行同步处理的过程)

第三步,将分配到的内存空间(不包括对象头)初始化为零值

第四步,对对象头进行设置,例如这是哪个对象的实例,如何找到类的元信息,对象的哈希值,GC分代年龄

第五步,执行init方法


1.2.2对象内存布局

HotSpot中,对象在堆内存中的布局可以划分为:对象头、实例数据、对齐填充

对象头分为MarkWord和类型指针两部分

MarkWord长度在32位和64位虚拟机中分别为32bit和64bit

存储内容 标志位 状态
对象哈希码、GC分代年龄 01 未锁定
指向锁记录的指针 00 轻量级锁
指向重量级锁的指针 10 重量级锁
11 GC标记
偏向线程ID、偏向时间戳、对象分代年龄 01 可偏向

类型指针指向它的类型元数据的指针,虚拟机通过该指针确定该对象是哪个类型的实例

此外,如果对象为数组,对象头中还记录了数组的长度

实例数据部分存储在程序代码中定义的各种字段内容,包括从父类继承的,HotSpot将相同宽度的字段分配到一起存储

对齐填充部分是为了保证任何对象的大小都是8字节的整数倍,没有实际意义


1.2.3对象的访问定位

不同虚拟机有不同的实现,主要分为直接指针访问和句柄访问

句柄访问

java堆中划分出一块句柄池,句柄中包含对象实例数据和类型数据的指针,优点是对象移动时只需要修改句柄,reference本身不要修改

直接指针访问

reference中存储的就是对象的地址,可以减少一次访问开销,速度更快


转载:https://blog.csdn.net/malaoshidediao/article/details/108322127
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场