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

不止新生代与老年代:深入Java虚拟机堆内存布局与TLAB、卡表等优化机制

Java虚拟机运行数据区域

在JDK 8及以上版本中,Java虚拟机运行时数据区域主要包括以下部分:

1)堆(Heap):这是Java虚拟机中最大的内存区域,所有线程共享,主要用于存放对象实例和数组。这也是垃圾回收的主要区域,因此也被称作GC堆(Garbage Collection Heap)。

2)方法区(Method Area):在JDK 8之前,这被称为永久代(PermGen)。但从JDK 8开始,被替换为元空间(Metaspace)。主要用于存储已加载的类信息、常量、静态变量、编译后的代码等。

3)栈(Stack):每个线程创建时都会有一个对应的Java栈,用于存储局部变量、操作数栈、动态链接、方法出口等信息。

4)程序计数器(Program Counter Register):这是一块小内存区域,作为当前线程执行的字节码的行号指示器。每个线程都有一个独立的程序计数器。

5)本地方法栈(Native Method Stack):类似于Java栈,用于支持本地方法的执行。

6)直接内存(Direct Memory):虽然不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但这部分内存被频繁使用,主要被NIO用于提高IO效率。

image

Java虚拟机的堆划分

当前,主流的Java虚拟机主要采用分代回收(Generational Garbage Collection)。分代回收,更准确地说,它是一种理念。这种理念将系统中的所有对象划分为不同的代(Generation),并根据对象的生命周期长度将其分类到相应的代中,每个代则采用适合其特性的垃圾回收算法。这种理念主要基于两个分代假设。

1)弱分代假说(Weak Generational Hypothesis):大部分对象都会在创建后不久就变得不可达。也就是说,许多对象的生命周期都很短;

2)强分代假说(Strong Generational Hypothesis):存活时间较长的对象,很可能会引用存活时间较短的对象,但反之则不然。也就是说,老的对象很少引用新的对象。

Java虚拟机将堆划分为新生代(Young Generation)和老年代(Old Generation)。其中,新生代又被划分为Eden区,以及两个大小相同的Survivor区。默认情况下,Java虚拟机采取一种动态分配的策略,根据生成对象的速率,以及Survivor区的使用情况动态调整Eden区和Survivor区的比例。

image

TLAB

通常,调用new指令时,会在Eden区中划出一块作为存储对象的内存。由于堆空间是线程共享的,在多线程环境下,为了避免多个线程同时向堆内存申请空间而产生的竞争,Java虚拟机为每个线程分配了一个私有的缓冲区,即TLAB(Thread-Local Allocation Buffer)。由于TLAB是线程私有的,因此在分配对象时不需要进行线程同步,大大提高了对象分配的效率。如果TLAB空间不足,那么线程可能需要申请新的TLAB。需要注意的是,TLAB只适用于小对象的分配。对于大对象,通常直接在堆内存中进行分配。

image

Minor GC、Minor GC、Full GC

当Eden区空间不足时,会触发Minor GC,对年轻代进行垃圾回收。存活下来对象,会被迁移到Survivor区。新生代包含两个Survivor区,分别为from和to区,其中to区始终保持空闲。

Minor GC期间,Eden区和from区的存活对象被复制至to区,随后交换from和to,确保下次Minor GC时,to区为空。

Java虚拟机会跟踪Survivor区对象的复制次数,当达到15次或单个Survivor区占用率超过50%时,那么较高复制次数的对象,将被晋升(Promote)至老年代。

Minor GC主要采用复制算法,Survivor区中的老存活对象晋升至老年代,然后将剩余存活对象与Eden区的存活对象复制至另一Survivor区。在理想情况下,Eden区的对象大多已死亡,因此需要复制的数据量较小,因此采用复制算法效率较高。

当老年代空间不足时,触发Major GC,对老年代进行垃圾回收。Major GC通常采用清除或整理算法,由于需要扫描整个老年代并可能进行对象移动以整理内存,因此会导致较长的停顿时间。

当Java堆内存空间不足时,触发Full GC,对整个堆内存(包括新生代和老年代)进行垃圾回收。由于需要扫描整个堆内存并可能进行对象移动以整理内存,Full GC会导致比Major GC更长的停顿时间。

卡表

Minor GC期间,在标记存活对象的时候,可能会碰到跨代引用对象的问题。由于年轻代大都是存活时间较短的对象,跨代引用通常是指老年代对象存在对新生代对象的引用。为了保证对年轻代存活对象标记准确性,就不得不把老年代也纳入到扫描范围。

为了解决跨代引用的问题,可以在新生代可以引入记忆集(RememberSet)。记忆集位于新生代中,是一种用于记录从非回收区域指向回收区域的指针集合的抽象数据结构。

image

记忆集是一种概念,在Java虚拟机中,记忆集通常通过卡表(Card Table)实现,它也是目前最常用的一种方式。卡表是一个字节数组,其中每个元素对应一块特定大小(默认512字节)的内存区域,称为卡页(Card Page)。

当对象的引用发生修改时,写屏障会被触发,将对应的卡页标记为"脏",表示该内存区域的对象已被修改。为了降低写屏障的开销并保持其生成指令的简洁性,写屏障并不判断更新后的引用是否指向新生代对象,而是统一视为可能指向新生代对象的引用。

if (CARD_TABLE [this address >> 9] != DIRTY)

CARD_TABLE [this address >> 9] = DIRTY;

在进行Minor GC的时,就无需扫描整个老年代,而是在卡表中寻找标记为脏卡的区域,并将这些脏卡区域的对象加入到Minor GC的GC Roots中。完成所有脏卡的扫描后,Java虚拟机会清除所有脏卡的标记。

image

未完待续

很高兴与你相遇!如果你喜欢本文内容,记得关注哦

http://www.cnnetsun.cn/news/40792.html

相关文章:

  • 随机图床 _
  • PETools 逆向工程工具完整使用指南
  • 改善深层神经网络 第一周:深度学习的实践(四)其他缓解过拟合的方法
  • 品牌营销 深度心智方法论
  • STM32之使用DWT外设编写延时函数
  • 选择中国优化路线的美国独立服务器的8大好处
  • Abaqus三维纤维复合材料Vumat子程序:弹性层压板+Hashin损伤(纤维)+Puck损...
  • 移动应用无障碍测试完全指南:如何用Maestro实现WCAG标准自动化验证
  • Effective C++ 中文版第3版:C++进阶编程的终极指南
  • Slang光线追踪加速结构深度解析:从理论到性能优化实践
  • DeepSeek-R1-Distill-Qwen-7B集群部署终极指南:轻松搞定AI推理服务
  • 风储调频在Matlab/Simulink中的探索:基于四机两区系统的实践
  • 基于Java Swing的猜数字小游戏(2)
  • 提升 Web 端 JavaScript 的可信度:WAICT 体系详解
  • Solon (可替换 SpringBoot)集成 Docker 实战:30分钟搞定轻量级应用容器化部署
  • 一次 React 项目 lock 文件冲突修复:从 Hook 报错到 Vite 配置优化
  • 记录一次USB虚拟网络问题排查
  • 学Simulink——基于高比例可再生能源渗透的复杂电网建模场景实例:大规模光伏并网对区域电网频率稳定影响研究
  • CANN Samples(十九):特色场景:机器人 AI 绘画 手写识别等
  • 毕设开源 深度学习YOLO交通路面缺陷检测系统(源码+论文)
  • 【URP】Unity[后处理]色彩调整ColorAdjustments
  • Flutter混合开发与WebView集成实战
  • 压缩空气储能和释能阶段模型,附相关文档文献。 建立了压缩空气储能系统中的压缩机、换热器、储气罐...
  • Profinet转Modbus TCP工业数据采集网关:实现1200PLC 与打标卡数据实时传输
  • Flutter 2025 国际化与本地化实战:从多语言支持到文化适配,打造真正全球化的用户体验
  • P1178 到天宫做客 题解
  • 2025年GEO优化机会与争议以及规范发展的必要性
  • 2026老年春晚怀化区域节目征集启动仪式在怀化学院举行
  • 【笔记篇】【硬件基础篇】电力电子元器件应用手册 阅读笔记(1)电阻器及其应用
  • 柠檬 软件测试之python全栈自动化测试工程师第25期