JAVA:JVM 创建对象时,堆会发生抢占吗?

发布于:2025-06-27 ⋅ 阅读:(14) ⋅ 点赞:(0)

1、简述

在 Java 中,对象创建是非常频繁的操作。你可能会疑问:

当多个线程同时创建对象时,它们是否会在 JVM 堆中“抢占”内存?

本文将从 JVM 的内存模型、对象分配机制出发,揭开“堆抢占”的本质,探讨多线程下的并发内存分配策略,并通过实践样例进行验证。

在这里插入图片描述


2、对象创建的内存来源:Java 堆

Java 中大多数对象都分配在堆内存(Heap)中。堆是所有线程共享的内存区域,因此在多线程环境下,如果不加管理,就可能存在多个线程争用堆中内存的问题——也就是所谓的“堆抢占”

在这里插入图片描述

2.1 什么是“堆抢占”?

“堆抢占”本质上是:

多线程并发创建对象时,竞争堆内存的可用空间,导致同步开销或分配延迟。

如果 JVM 采用的是共享指针分配策略,每个线程都需要对同一个内存指针进行加锁或原子操作,这会产生性能瓶颈。

2.2 JVM 如何避免堆抢占?——TLAB(线程本地分配缓冲)

为了优化对象分配的并发性能,JVM 引入了 TLAB(Thread Local Allocation Buffer)

TLAB 机制:
  • JVM 会为每个线程分配一小块堆内存区域。
  • 线程创建对象时,优先从自己的 TLAB 中分配,无需加锁。
  • 只有当 TLAB 用尽时,才会申请新的 TLAB 或走全局堆分配(可能引起“堆抢占”)。

这意味着:大多数情况下,不会发生堆抢占。

2.3 TLAB 开启状态

TLAB 在大多数 JVM 实现中是默认开启的。可以使用以下参数查看状态:

java -XX:+PrintTLAB -XX:+UseTLAB -Xlog:gc -Xms128m -Xmx128m Demo

3、实践样例:观察是否存在堆抢占

下面通过一个多线程对象创建示例,对比 TLAB 开启与关闭下的分配效率。

public class AllocationTest {
    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 0; i < 1_000_000; i++) {
                byte[] buffer = new byte[1024]; // 每次分配 1KB
            }
        };

        Thread[] threads = new Thread[8];
        long start = System.currentTimeMillis();
        for (int i = 0; i < 8; i++) {
            threads[i] = new Thread(task);
            threads[i].start();
        }
        for (Thread t : threads) {
            t.join();
        }
        long end = System.currentTimeMillis();
        System.out.println("Total time: " + (end - start) + " ms");
    }
}

3.1 对比执行方式:

开启 TLAB(默认)

java -XX:+UseTLAB -Xms512m -Xmx512m AllocationTest

关闭 TLAB

java -XX:-UseTLAB -Xms512m -Xmx512m AllocationTest

观察指标:

  • 执行时间:关闭 TLAB 后通常变慢。
  • GC 日志:关闭 TLAB 会增加堆分配冲突,导致更多 GC 或停顿。
  • CPU 使用率:可能增加同步操作占用。

3.2 深入:TLAB 分配过程图

            +---------------------------+
Thread ---> | TLAB (Thread-local heap) |
            +---------------------------+
               ↓
           +-----------+
           | allocate()| ——> Fast path
           +-----------+

TLAB 不足时:
→ 请求新 TLAB
→ 或 走 Global Allocation (需同步)

3.3 JVM 其他避免堆抢占的机制

除了 TLAB,JVM 还通过以下方式优化并发对象分配:

机制 说明
Eden 区并发分配 年轻代中的 Eden 区为对象分配提供连续空间
指针碰撞算法 避免查找空闲块,提升分配效率
多线程 GC 支持 如 G1、ZGC 支持并发标记/复制,降低暂停

4、结语

JVM 设计了诸如 TLAB、指针碰撞、内存分代等机制,最大限度地避免多线程对象创建时的“堆抢占”。理解这些原理不仅能帮助我们写出更高效的代码,还能在面对 GC 性能瓶颈时,做出更精确的诊断与调优。

问题 是否发生? 说明
多线程下是否争抢堆空间? ✅ 有可能 当 TLAB 用尽或关闭时,发生堆全局分配争抢
TLAB 是否能避免堆抢占? ✅ 是 提供线程本地内存,提升并发分配效率
是否推荐关闭 TLAB? ❌ 否 除非做性能调试,否则不建议关闭

如果你想进一步实践,我可以提供:

  • 📦 自定义 TLAB 大小对性能影响的测试代码
  • 🧠 多线程 + Off-Heap(堆外)对象分配对比
  • 📈 使用 JMH 测试分配速度差异

是否需要我继续拓展?


网站公告

今日签到

点亮在社区的每一天
去签到