当前位置: 首页 > news >正文

JVM常用概念之线程本地分配缓冲区(ThreadLocal Allocation Buffer,TLAB)

当实例化一个Java类时,运行时环境必须为相关实例分配存储空间,在JRE中此存储空间分配操作是由内存管理器实现的(其实是JVM的垃圾回收器),由于内存管理器通常使用与运行时目标语言不同的语言编写(例如,Java 以 JVM 为目标,而 HotSpot JVM 是用 C++ 编写的),因此接口会变得更加模糊。而这种操作成本是相当高的,并且内存管理器也必须应对多线程场景下进行内存请求多压力。为了使Java程序的运行效率尽可能接近C++等语言的运行效率,针对JVM的内存管理器的执行效率需要进行优化。

1.优化方法

优化方法如允许线程分配整个内存块以满足其需求,并且只传输到 VM 以获取新块。在 Hotspot 中,这些块称为线程本地分配缓冲区 (TLAB),并且有一个复杂的机制来支持它们。请注意,TLAB 在时间意义上是线程本地的,这意味着它们像缓冲区一样接受当前分配。它们仍然是 Java 堆的一部分,线程仍然可以将对新分配对象的引用写入 TLAB 之外的字段等等。

所有已知的 OpenJDK GC 都支持 TLAB 分配。VM 代码的这一部分在它们之间基本是共享的。所有 Hotspot 编译器都支持 TLAB 分配,因此您通常会看到如下所示的对象分配生成代码:

0x00007f3e6bb617cc: mov    0x60(%r15),%rax        ; TLAB "current"
0x00007f3e6bb617d0: mov    %rax,%r10              ; tmp = current
0x00007f3e6bb617d3: add    $0x10,%r10             ; tmp += 16 (object size)
0x00007f3e6bb617d7: cmp    0x70(%r15),%r10        ; tmp > tlab_size?
0x00007f3e6bb617db: jae    0x00007f3e6bb61807     ; TLAB is done, jump and request another one
0x00007f3e6bb617dd: mov    %r10,0x60(%r15)        ; current = tmp (TLAB is fine, alloc!)
0x00007f3e6bb617e1: prefetchnta 0xc0(%r10)        ; ...
0x00007f3e6bb617e9: movq   $0x1,(%rax)            ; store header to (obj+0)
0x00007f3e6bb617f0: movl   $0xf80001dd,0x8(%rax)  ; store klass to (obj+8)
0x00007f3e6bb617f7: mov    %r12d,0xc(%rax)        ; zero out the rest of the object

2.指针碰撞分配

分配路径内联在生成的代码中,因此不需要调用 GC 来分配对象。如果我们请求分配耗尽了 TLAB 的对象,或者对象足够大而无法放入 TLAB,那么我们将采取“慢速路径”,要么在那里满足分配,要么返回新的 TLAB。请注意,最常见的“正常”路径只是将对象大小添加到 TLAB 当前光标,然后继续。

这就是为什么这种分配机制有时被称为“指针碰撞分配”。指针碰撞需要分配一块连续的内存,但这又带来了堆压缩的需要。请注意 CMS 如何在“老”代中进行空闲列表分配,从而实现并发清除,但它压缩了STW情况下堆中的“年轻代”集合,这受益于指针碰撞分配!年轻代集合中幸存下来的对象数量要少得多,这就是空闲列表分配的代价。

为了进行实验,我们可以使用 -XX:-UseTLAB 关闭 TLAB 功能。然后,所有分配都将进入本机方法,通常不建议这么做,如下所示:

-   17.12%     0.00%  org.openjdk.All  perf-31615.map- 0x7faaa3b2d125- 16.59% OptoRuntime::new_instance_C- 11.49% InstanceKlass::allocate_instance2.33% BlahBlahBlahCollectedHeap::mem_allocate  <---- entry point to GC0.35% AllocTracer::send_allocation_outside_tlab_event

3.总结

TLAB 是内存分配机制的主力:它们消除了分配器的并发瓶颈,提供了廉价的分配路径,并全面提高了性能。有趣的是,使用 TLAB 会导致更频繁的 GC ,只是因为内存分配非常便宜!相反,在任何内存管理器实现中没有快速分配路径肯定会隐藏内存回收性能问题,从而严重的影响JVM的性能。

相关文章:

  • vivado HW_SERVER
  • C++ STL 中的 priority_queue::push() 和 priority_queue::pop()
  • stm32MP135裸机编程:启动流程分析
  • 假设Python脚本包含引用了大量的第三方库,如何打包成.exe文件,并且可以在没有环境的服务器下正常运行
  • vue-2 组件传值
  • Django学习(2)项目实战
  • React 18
  • ISO 19115-2:2019 第6章 获取和处理元数据
  • 【C++】STL中list的使用
  • powerdesigner各种字体设置
  • 深度解析:全流量分析与IP会话回溯在IT运维中的应用
  • matlab演示银河系转动动画
  • 进程概念(二)
  • pytest配置文件配置并通过allure生成报告
  • 使用difflib实现文件差异比较用html显示
  • CAP 一致性协议及应用解析
  • ComponentOne 2017 V2版本正式发布
  • Java新版本的开发已正式进入轨道,版本号18.3
  • Mysql5.6主从复制
  • python3 使用 asyncio 代替线程
  • Sublime Text 2/3 绑定Eclipse快捷键
  • Vue.js源码(2):初探List Rendering
  • Wamp集成环境 添加PHP的新版本
  • 爱情 北京女病人
  • 不上全站https的网站你们就等着被恶心死吧
  • 翻译--Thinking in React
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 模型微调
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 浅谈Golang中select的用法
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 通过获取异步加载JS文件进度实现一个canvas环形loading图
  • 消息队列系列二(IOT中消息队列的应用)
  • 怎样选择前端框架
  • elasticsearch-head插件安装
  • ​2021半年盘点,不想你错过的重磅新书
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • # Redis 入门到精通(九)-- 主从复制(1)
  • $nextTick的使用场景介绍
  • $redis-setphp_redis Set命令,php操作Redis Set函数介绍
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (4)事件处理——(7)简单事件(Simple events)
  • (Git) gitignore基础使用
  • (补)B+树一些思想
  • (二)丶RabbitMQ的六大核心
  • (蓝桥杯每日一题)love
  • (十八)Flink CEP 详解
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (四)事件系统
  • (一)、软硬件全开源智能手表,与手机互联,标配多表盘,功能丰富(ZSWatch-Zephyr)
  • (源码分析)springsecurity认证授权
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • ***利用Ms05002溢出找“肉鸡
  • ... 是什么 ?... 有什么用处?