1.java内存区域划分
- 程序计数器
字节码的行号指示器 - java虚拟机栈
描述java方法执行的线程内存模型:每个方法被执行的时候都会同步创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法被调用直至执行完毕的过程,就对应一个栈帧入栈和出栈的过程。局部变量表的内存空间在编译期间确定,在运行过程中,大小不变。 - 本地方法栈
和java栈类似,只不过为本地方法(非java方法)提供服务。 - 方法区
用于存储被虚拟机加载的类型信息、常亮、静态变量、及时编译器编译后的代码缓存等数据。
运行时常量池时方法区的一部分,用于存放编译期生成的各种字面量与符号引用。 - java堆
几乎所有对象的实力都在该区域分配内存。
补充说明:直接内存不是虚拟机运行时数据区的一部分,JDK1.4加入了NIO类,引入了一种基于通道与缓冲区的I/O方式,可以使用Native方法直接分配堆外内存,然后通过存储在堆里面的DirectByteBuffer对象作为这块内存区域的引用。通过这种方法避免在Java堆和Native堆来回复制数据。
2.对象的创建过程
(1)当Java堆遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到一个符号引用,并检查这个符号引用所代表的类是否被加载、验证、解析、初始化过,否则执行类加载过程(双亲委派机制)。
(2)加载完成后,分配内存。
两种内存分配方法:
方法1:“指针碰撞”,若java堆的内存是绝对规整的,使用过的内存放在一边,空闲的内存放在一边,中间放着一个指针作为分界点指示器,分配的过程就是讲指针想空闲区域挪动一段与对象大小相等的距离。
方法2:“空闲列表”,若java堆的内存不是规整的,维护一个列表,记录空闲内存块,然后分配并更新列表
对象的创建是非常频繁的行为,及时仅仅修改一个指针指向的位置,在并发情况下也并不是线程安全的,这里用两种同步策略。
策略1:使用CAS配置上失败重试的方式保证更新操作的原子性
策略2:使用threadlocal方法,把内存分配的动作划分在不同的空间内进行。
(3)内存分配完成后,虚拟机必须将对象所分配的内存(不包括对象投)都初始化为0。
(4)对对象头进行设置。
3.对象的内存布局
对象的内存布局可以分为三部分:对象头、实例数据和对齐填充。
对象头包含三部分数据:
4 对象的访问
- 使用句柄访问
- 使用直接指针访问