内存分配方式主要分为两种 分别是 指针碰撞 和 空闲列表。
指针碰撞指的是线性空间,通过移动指针分配内存。
空闲列表指的是不同的内存块,通过指针链接到一块完整从而继续分配。
解决内存分配存在线程不安全的方法是:
1、采用CAS机制+失败重试。
2、为每个线程预先分配一小块内存,称为本地线程分配缓冲内存,虚拟机在这块内存上赋初值。
对象在内存中的存储布局可分为 对象头(header)和实例数据(Instance Data)和对齐填充(Padding)
对象头分为两个部分,一部分用于存储对象自身的运行时数据如hashcode,GC分代年龄,锁状态,线程持有的锁、偏向线程ID、偏向时间戳等等,称为“MarkWord”
另一部分为类型指针,指向它类元数据的指针、从而确定对象是属于哪一个类。(若是Java数组还需要一块空间存储数据大小,因为虚拟机只能知道对象大小)
对齐填充只在需要的时候存在,目的是填充对象大小至8字节的整数倍。
对象的访问定位
在栈中的Ref可通过句柄或者直接指针指向对象
句柄方式:Ref->句柄池中的句柄->对象实例数据(Java堆)
->类型数据(方法区)
直接指针方式:Ref->对象实例数据(Java堆)->对象类型数据(方法区)
句柄采用二次定位 在对象移动时,只需要改动句柄而不需要改动Ref
直接指针直接访问对象,效率更高,不用二次寻址。
给每个线程的内存有限(Window限制每个线程最多2G)若因线程过多导致的SOF异常,可采取减少最大堆和分配给线程的栈容量来获得更多线程。
方法区和运行时常量溢出只能通过缩减其他内存区域或者优化GC对方法区的回收来保证。