Java 内存模型 (JMM) 和对象内存分配机制
一、Java 内存模型 (JMM)
Java 内存模型(Java Memory Model, JMM)定义了线程和主内存之间的抽象关系,描述了 JVM 中变量的可见性和一致性问题。其核心目标是确保程序在多核处理器环境下的正确执行。以下是 JMM 的几个重要概念:
-
主内存与工作内存
主内存用于存储所有线程共享的数据结构,而每个线程都有自己的私有工作内存,其中保存了被该线程使用到的变量副本。当线程读写某个变量时,实际上是在线程的工作内存中进行操作,而非直接作用于主内存。 -
原子性
原子性意味着某些操作不可分割,要么全部完成,要么完全不发生。然而,并非所有的操作都是原子性的;例如,long
或double
类型的变量赋值可能涉及两个独立的操作,因此并非总是具有原子性。 -
可见性
可见性指的是一个线程修改的状态能够及时被其他线程观察到的能力。为了保证可见性,通常会借助同步关键字 (synchronized
) 或者挥发性字段 (volatile
) 来实现。 -
有序性
编译器优化可能会改变指令的实际执行顺序,从而破坏程序预期的行为。通过使用锁或特定的关键字(如volatile
),可以强制维持一定的执行顺序。
二、Java 对象内存分配机制
Java 对象的内存分配主要发生在堆区内,这是由垃圾回收器负责管理的一片连续区域。根据不同的场景和技术需求,存在两种常见的分配策略:指针碰撞(Bump the Pointer)和空闲列表(Free List)。下面详细介绍这两种方法及其适用条件:
-
指针碰撞 (Bump the Pointer)
当 Java 堆中的内存布局保持规整状态时——即已使用的内存位于一侧,未使用的内存位于另一侧——可以通过简单地调整边界指针位置来完成新对象的空间预留过程。这种方式效率极高,因为它只需增加少量开销即可快速定位新的可用地址范围。不过需要注意的是,只有那些不会引发内存碎片化的垃圾收集算法才能支持此模式,比如 Serial 收集器或 ParNew 收集器。 -
空闲列表 (Free List)
如果由于频繁的对象释放而导致堆内部出现了大量零散的小块空白区间,则无法再单纯依靠单一指针指示剩余容量的位置。此时就需要维护一份专门记录这些片段分布情况的信息表单,称为“空闲列表”。每当申请一个新的实例所需大小超过当前最大可得单元尺寸时,就必须遍历整个表格寻找合适的候选选项加以拆分重组后再交付出去供实际用途所用。这种方法虽然灵活性更强但也伴随着更高的计算成本,尤其是在面对复杂查询请求的情况下尤为明显。像 CMS 这样的基于 “标记-清除” 技术路线设计而成的产品往往会选择采用此类方案应对可能出现的各种极端状况。
此外还有一种特殊情况叫做 TLAB(Thread Local Allocation Buffer),它是针对高频次短生命周期小型对象创建场合特别定制的一种局部缓冲区技术。每一个活跃期间内的单独任务都会预先获得一小部分独占权限去自行决定如何支配这部分资源而不必每次都经历全局协调流程,这样既提高了吞吐量又减少了竞争冲突带来的延迟影响。
// 创建对象示例
Object obj