JVM 内存分配
内存分配
在 JVM 中内存分配是对象生命周期的起点,涉及内存区域划分、分配策略、线程交互等。
- 堆:存储对象实例和数组(对象主要分配在堆内存)
- 方法区:存储类信息、常量、静态变量(静态对象的引入,静态对象的实例在堆中)
- 虚拟机栈:局部变量表;(基本类型局部变量和对象局部变量的引用)
- 本地方法栈:不直接参与 Java 对象分配
- 程序计数器:记录当前线程执行位置,不参与内存分配
Java 对象的实例数据几乎都分配在堆中;局部变量(引用类型)在栈中存地址,指向堆中对象。
新生代:占堆内存的 1/3,分为 3 个区域;Eden 分配新对象;Survivor(幸存者区),分为 From 和 To 两个大小相等的区域(各占 10%),用于存放经历一次 GC 后存活的对象。新对象先进入 Eden 区;当 Eden 区满时,触发 Minor GC(新生代 GC),存活对象被复制到 Survivor 的 From 区。下次 Minor GC 时,From 区存活对象被复制到 To 区(年龄 + 1),同时清空 From 区;之后 From 和 To 角色互换。当对象年龄达到阈值(默认 15,可通过-XX:MaxTenuringThreshold 调整),会被晋升到老年代。
老年代:占堆内存的 2/3 左右,存放存活时间较长的对象;大对象直接分配到老年区,Survivor 区中相同年龄的对象总大小超过该区一半,年龄 ≥ 该年龄的对象直接进入老年代,Survivor 区中相同年龄的对象总大小超过该区一半,年龄 ≥ 该年龄的对象直接进入老年代
内存分配的核心策略
JVM 通过以下策略高效管理对象分配,减少内存碎片和 GC 开销
- 指针碰撞:适用于堆内存连续的情况。用一个指针指向堆中已分配内存的末尾,新对象分配时,指针直接向空闲区域移动对应大小的距离(类似数组扩容)。
- 空闲列表:适用于堆内存存在碎片,JVM 维护一个列表,记录所有空闲内存块的位置和大小,新对象分配时,从列表中找到足够大的块分配给对象,并更新列表。
- 线程本地分配缓冲:减少多线程竞争,提高分配效率;每个线程在 Eden 区中预先分配一小块私有内存(TLAB),线程创建对象时优先在自己的 TLAB 中分配,无需加锁。当 TLAB 不足时,才去公共 Eden 区分配
**内存分配有两个核心参数,初始堆大小和最大堆大小。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Myskill-blog!