深入理解JAVA虚拟机(JAVA内存区域与内存溢出异常)-读书笔记

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

2.1 运行时数据区域

  • java虚拟机在执行java程序过程中会将它所管理的内存划分若干个不同的数据区域,有的区域随着虚拟机进程的启动而存在,有些区域依赖用户线程启动和结束而建立和消亡;

2.1.1 程序计数器

线程私有

  • 较小区域,当前线程执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条指令,,分支,循环,跳转,异常处理,线程恢复都依赖这个计数器;每个线程都会有一个程序计数器,各个线程之间相互隔离;

2.1.2 JAVA虚拟机栈

线程私有

  • 描述的是JAVA方法执行的内存模型,每个方法执行同时都会创建一个栈帧,用来存储局部变量表(存放编译期可知的各种基本数据类型和对象引用,局部变量表所需的内存空间在编译期间就完成,运行期间不会改变),操作数栈动态链接方法出口,每一个方法从调用到执行完成都对应着栈帧在虚拟机栈中的入栈和出栈;

异常

  • 线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常,虚拟机栈如果动态扩展无法申请到足够的内存,则抛出OutOfMemoryError异常;

2.1.3 本地方法栈

线程私有

  • 为虚拟机使用的native服务,也会抛出StackOverflowError和OutOfMemoryError异常;

2.1.4 Java堆

线程共享

  • 虚拟机启动时创建,几乎存放所有对象实例,数组,也不绝对;

  • 分为新生代和老年代,Eden,from Survivor, to Survivor;

  • Java虚拟机规定,堆可以处于物理上不连续的内存空间,逻辑连续即可,一般都是可扩展的,当堆中没有内存完成分配,并且无法扩展,抛出OutOfMemoryError异常;

2.1.5 方法区

线程共享

  • 存储虚拟机加载的类信息,常量,静态变量即时编译器编译后的代码等数据,也叫永久代(方便进行分代回收);对这块的回收主要针对于对常量池的回收和对类型的卸载;

2.1.6 运行时常量池

  • 方法区的一部分,用来存储编译器的各种字面量和符号引用,在类加载后进入方法区的运行时常量池,运行期间可能将新的常量放入池中,String类的intern()方法,没有内存时,抛出OutofMemoryError异常;

2.1.7 直接内存

  • 不是运行时数据区一部分,也不是虚拟机规范的内存区域,但是频繁使用会出现OutofMemoryError异常;

  • JDK1,4加入NIO类,引入基于通道和缓冲区的I/O方式,使用Native函数库直接使用堆外内存,通过存储在堆中DirectByteBuffter对象对这块内存进行引用;

  • 配置虚拟机参数时,会忽略直接内存,导致内存区域总和大于物理内存限制;

2.2 HotSpot虚拟机对象探秘

2.2.1 对象的创建

  • 虚拟机遇到new指令时,先去检查这个指令参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表类是否已经被加载,解析,初始化过,如果没有则先要进行类加载;

  • 类加载检查通过后,为新生对象分配内存,一个对象所需内存大小在类加载后就能确定;两种分配方式:指针碰撞:若java堆内存绝对规整,用过内存放一边,未使用内存放一边,分配内存仅仅是将指针向空闲内存挪动一段和对象大小一样的距离;空闲列表:若java堆中的内存并不是规整的,虚拟机需要维护一个表,记录哪些内存块可用,分配时候从列表找出一块足够大的内存分配给对象,并更新列表上的记录;

  • 创建对象要考虑并发问题,对象A正在分配内存,指针还没来得及修改,对象B使用原指针分配内存;两种方案:一种对分配内存空间动作进行同步处理(实际虚拟机采用CAS配上失败重试保证更新操作的原子性)另一种每个线程预先分配一小块内存称为本地线程分配缓冲(TLAB)哪个线程分配内存就在哪个TLAB上分配,只有TLAB用完再分配TLAB时,再同步锁定,- XX:+/-UseTLAB参数决定;

  • 虚拟机将分配到的内存空间都初始化为零值,保证对象不给初始值也能使用;

  • 对对象进行必要的设置,例如对象是哪个类的实例,如何找到类的元数据信息,对象的哈希值,对象的GC分代年龄信息存储在对象头中;

  • 执行init方法,进行初始化;

2.2.3 对象的内存布局

对象头

  • 又叫Mark word,包含两部分:第一部分存储对象自身的运行时数据:哈希码,GC分代年龄,锁状态标志,线程持有锁,偏向线程ID,偏向时间戳等;第二部分类型指针,对象指向它类元数据的指针,并不是所有虚拟机实现都在对象头保留类型指针; 若对象是java数组,则对象头还有一块记录数组长度的数据,因为虚拟机可以通过普通对象的元数据确定对象大小,数组却无法确定;

实例数据

  • 对象真正存储的有效信息,存储顺序会受虚拟机分配策略影响;

对齐填充

  • 不是必然存在的,没有特别含义,只是占位符,HotSpot VM的自动内存管理系统要求对象起始地址必须是8的整数倍;

2.2.4 对象的访问定位

  • 对象的访问方式主流通过句柄直接指针两种;

  • 句柄:java堆会划分出一块内存作为句柄池,栈引用存储的就是对象的句柄地址,而句柄中包含对象实例数据和类型数据的具体地址;

  • 直接指针:栈引用直接存储对象地址,堆中的对象存储着指向类型数据的地址;

  • 各有优势:句柄优势,稳定,若对象地址发生改变,栈引用中的句柄地址不变,只有句柄池中的实例对象地址发生变化;直接指针优势,快,节省一次指针定位的开销,虚拟机Sun HotSpot是用的直接指针;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值