JVM堆分配中TLAB分配方案

2024-05-26 16:04
文章标签 java jvm 分配 方案 tlab

本文主要是介绍JVM堆分配中TLAB分配方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

个人博客

JVM堆分配中TLAB分配方案 | iwts’s blog

Java对象的内存分配过程如何保证线程安全

对象的内存分配过程中,主要流程是将对象的引用指向一个具体的内存区域,然后进行初始化操作。

但是,因为堆是全局共享的,因此在同一时间,可能有多个线程在堆上申请空间,那么,在并发场景中,可能出现两个线程先后把对象引用指向了同一个内存区域这样的情况。

为了解决这个并发问题,对象的内存分配过程就必须进行同步控制。但是加锁/CAS,本质上都会影响内存的分配效率。

那么在HotSpot虚拟机中就实现了不涉及同步的解决方案,即TLAB。

TLAB分配方案

Thread Local Allocation Buffer。其主要概念是:

  1. 每个线程在Java堆中预先分配一小块内存。

  2. 给对象分配内存的时候,直接在自己这块“私有”内存中分配。

    a> 当这部分区域用完之后,再分配新的“私有”内存。

而预先分配的这一小块内存,即TLAB,是从堆的Eden区中划分出来的,但是是某个线程独享的。

在虚拟机的TLAB功能启动的情况下,在线程初始化时,虚拟机会为每个线程分配一块TLAB空间,只给当前线程使用,这样每个线程都单独拥有一个空间,如果需要分配内存,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率。

TLAB非分配时公有

这里值得注意的是,我们说TLAB是线程独享的,但是只是在“分配”这个动作上是线程独享的,至于在读取、GC等动作上都是线程共享的。而且在使用上也没有什么区别。

也就是说,虽然每个线程在初始化时都会去堆内存中申请一块TLAB,并不是说这个TLAB区域的内存其他线程就完全无法访问了,其他线程的读取还是可以的,只不过无法在这个区域中分配内存而已。

TLAB的GC

在TLAB分配之后,并不影响对象的移动和回收,也就是说,虽然对象刚开始可能通过TLAB分配内存,存放在Eden区,但是还是会被GC掉或者被移到Survivor Space、Old Gen等分区中。

大对象在TLAB下的分配

TLAB是在Eden区分配的,但是因为Eden区域本身就不太大,而且TLAB空间的内存也非常小,TLAB在默认情况下仅占有整个Eden空间的1%。

所以,如果是大对象,那么本身是无法在TLAB区直接分配的。

那么这个时候还是可能只在Eden区或者老年代进行分配的,但是这种分配就需要进行同步控制了。

这也是大对象分配比较慢的原因,大对象不能利用TLAB隔离线程,所以只能通过锁等机制保证并发下的同步,所以速度相对小对象的创建要慢不少。

TLAB最大浪费空间

上面也聊过,本来Eden区就小,TLAB更小,那么分配的时候可能经常会碰到扩容的问题。例如:

  1. 线程的TLAB空间100kb,已经使用了80kb。
  2. 此时需要分配一个30kb的对象。
  3. TLAB空间不足。

那么这个时候有两个选择:

  1. 放弃TLAB,跟大对象一样直接堆内分配。

    a> 如果只剩下1kb,那么后续的对象几乎都是在堆内分配,失去了TLAB的意义。

  2. 放弃当前TLAB,申请一块新的TLAB。

    a> TLAB在划分时还是要同步的,如果频繁重新申请TLAB,那么同步操作次数也会多很多,本身性能也会下降。

JVM则提出了最大浪费空间(refill_waste)的概念,用以综合上面两种选择:

当请求分配的内存大于refill_waste的时候,会选择在堆内存中分配。若小于refill_waste值,则会废弃当前TLAB,重新创建TLAB进行对象内存分配。

依旧是上面的例子:

  1. TLAB总共100kb,使用了80kb,剩余20kb

  2. 设置refill_waste=25kb

  3. 那么有这样的场景:

    a> 新对象20kb,恰好分配。

    b> 新对象23kb,<25kb,那么放弃TLAB,直接申请一块新TLAB空间并分配

    c> 新对象30kb,>25kb,直接走大对象堆分配。

这篇关于JVM堆分配中TLAB分配方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1004934

相关文章

Spring boot整合dubbo+zookeeper的详细过程

《Springboot整合dubbo+zookeeper的详细过程》本文讲解SpringBoot整合Dubbo与Zookeeper实现API、Provider、Consumer模式,包含依赖配置、... 目录Spring boot整合dubbo+zookeeper1.创建父工程2.父工程引入依赖3.创建ap

SpringBoot3.X 整合 MinIO 存储原生方案

《SpringBoot3.X整合MinIO存储原生方案》本文详细介绍了SpringBoot3.X整合MinIO的原生方案,从环境搭建到核心功能实现,涵盖了文件上传、下载、删除等常用操作,并补充了... 目录SpringBoot3.X整合MinIO存储原生方案:从环境搭建到实战开发一、前言:为什么选择MinI

SpringBoot结合Docker进行容器化处理指南

《SpringBoot结合Docker进行容器化处理指南》在当今快速发展的软件工程领域,SpringBoot和Docker已经成为现代Java开发者的必备工具,本文将深入讲解如何将一个SpringBo... 目录前言一、为什么选择 Spring Bootjavascript + docker1. 快速部署与

Spring Boot spring-boot-maven-plugin 参数配置详解(最新推荐)

《SpringBootspring-boot-maven-plugin参数配置详解(最新推荐)》文章介绍了SpringBootMaven插件的5个核心目标(repackage、run、start... 目录一 spring-boot-maven-plugin 插件的5个Goals二 应用场景1 重新打包应用

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Java中读取YAML文件配置信息常见问题及解决方法

《Java中读取YAML文件配置信息常见问题及解决方法》:本文主要介绍Java中读取YAML文件配置信息常见问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录1 使用Spring Boot的@ConfigurationProperties2. 使用@Valu

创建Java keystore文件的完整指南及详细步骤

《创建Javakeystore文件的完整指南及详细步骤》本文详解Java中keystore的创建与配置,涵盖私钥管理、自签名与CA证书生成、SSL/TLS应用,强调安全存储及验证机制,确保通信加密和... 目录1. 秘密键(私钥)的理解与管理私钥的定义与重要性私钥的管理策略私钥的生成与存储2. 证书的创建与

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr