Hotspot CMS算法实现总结 (一)

  目录

一、数据结构

1、Java堆

2、CodeCache

3、MetaSpace

二、关键类/方法

1、oop/Klass

2、oop_iterate / adjust_pointers / follow_contents

3、ReferenceProcessor

4、markOop / promote / forward

5、GC_locker 和JNI关键区

6、VM_Operation / VMThread / ConcurrentMarkSweepThread

7、BarrierSet / CardTableRS

8、markBitMap / modUnionTable / CMSBitMap

 9、根节点oop

10、SafepointSynchronize / 安全点


 之前的多篇博客已经陆陆续续将CMS算法的实现所依赖的相关类都讲解了一遍,虽然还是有很多的细节需要进一步的补充,但是整体的实现思路和框架已经很清晰了,本篇博客就对其做一个整体的回顾与总结。

一、数据结构

     Java的内存整体上可以分为五大类,Java堆,CodeCache,Metaspace,栈内存和JVM自身,栈内存是指Java线程和JVM自身的后台服务线程执行过程中分配的调用栈对应的内存,包括所谓的虚拟机栈和本地方法栈,用于保存执行过程中的本地变量,方法入参,返回地址等方法执行过程中依赖的各种要素;JVM自身是指JVM实现各种功能所依赖的C/C++数据结构所占用的内存。后面两个的地址空间不是连续的,但是位于一段相对固定的地址区间范围内。栈内存分配与释放完全由底层操作系统控制,线程开始执行具体的方法前操作系统负责分配内存,当该方法执行完成对应的调用栈帧内存就会由操作系统自动回收;JVM自身的内存的分配不完全由操作系统控制,JVM对于一些经常被创建和销毁的数据结构(如用于临时保存oop的Handle)都实现了对应的内存管理工具(Handle对应的内存管理类是HandleArea),对象被销毁了其对应的内存并不会归还给操作系统,而是放到一个专门维护空闲内存块的数据结构中保存起来,下次再分配时优先从该数据结构中分配,如果空闲内存块不足再尝试向操作系统申请新的内存块,这样做的目的是为了减少操作系统分配内存的性能损耗。前面3个的地址空间是连续的,即对进程而言,这是一块连续的内存,下面来描述下这三个的数据结构。

1、Java堆

      CMS下Java堆的实现类就是GenCollectedHeap,该类实际不特指CMS下的Java堆内存,而是表示一个可能包含多个Generation的堆内存,现有的实现都是只有两个Generation。GenCollectedHeap的初始化在Universe::initialize_heap方法中,如下图:

GenCollectorPolicy的initialize_generations方法会指定包含的Generation的类型,如下:

 GenCollectedHeap会在初始化时调用GenerationSpec::init完成各Generation的初始化,其实现如下:

各Generation的类继承关系如下:

在默认配置下,年轻代的实现类就是DefNewGeneration,如果UseParNewGC为true,则变成ASParNewGeneration,注意UseParNewGC默认为false,如果所在系统是多核系统,在JVM启动时会将UseParNewGC的默认值由false改成true。ParNewGeneration相比DefNewGeneration只是增加了并行遍历根节点的逻辑,GC的流程一样,ASParNewGeneration相比ParNewGeneration增加了自动调整年轻代内存大小的支持;老年代的实现类默认是TenuredGeneration,如果UseConcMarkSweepGC为true,则变成ASConcurrentMarkSweepGeneration,相比ConcurrentMarkSweepGeneration只是增加了自动调整内存大小的支持。注意UseAdaptiveSizePolicy默认为true,表示根据堆内存的实际使用情况和GC耗时等动态调整Generation的内存容量。

Generation的实现是基于Space的,Space负责实际的内存管理,其类继承关系如下:

其中CompactibleSpace提供内存压缩的支持,即通过对象复制将存活的对象一个挨着一个的放到一起,减少内存碎片,另外因为对象复制的过程中存活的对象覆盖了需要了垃圾回收的对象,所以间接的实现的垃圾回收的效果;ContiguousSpace提供基于移动top指针的连续内存分配的支持,top指针前的内存区域表示已经分配出去了;EdenSpace和ConcEdenSpace只是添加了一个_soft_end属性,分配内存时不能超过_soft_end;CompactibleFreeListSpace的实现比较复杂,大于257字节的空闲内存块放到二叉树中管理,小于257字节的空闲内存块放到对应大小的List数组中,初始状态下List数组都是空的,二叉树中有一大块初始内存,在内存分配的过程中,会不断的从二叉树中的大块内存分割出指定大小的小块内存用来填充对应大小的List数组;实际分配时低于16字节的,从一个类似于ContiguousSpace连续分配内存的LinearAllocBlock中分配,16到257之间的从对应大小的List数组中分配,大于257的从二叉树中分配,所有分配的内存在释放时会按照大小归还到对应大小的List中或者二叉树中,在GC时这些空闲内存块会根据策略配置尽可能的合并成一个大的内存块。

CMS算法默认配置下,各Generation对应的Space如下图:

2、CodeCache

     CodeCache用于缓存不同类型的生成的汇编代码,如热点方法编译后的代码,各种运行时的调用入口Stub,每个字节码对应的执行代码,一些高频访问的数据函数和底层方法等。所有的汇编代码在CodeCache中都是以CodeBlob及其子类的形式存在的。通常CodeBlob会对应一个CodeBuffer,负责生成汇编代码的生成器会通过CodeBuffer将汇编代码写入到CodeBlob中,写入的起始地址就是该段汇编指令的调用地址。

    CodeCache只是对外的接口而已,具体的内存管理都是CodeHeap实现的。从CodeHeap中分配CodeBlob时,待分配的CodeBlob的大小会按照segment的大小向上对齐,一个segment可以理解为一个内存页,是操作系统分配内存的最小粒度,从而避免内存碎片。具体分配时会先在保存空闲内存块的List中查找,如果没有再按照类似top指针移动的方式来分配一块新的内存块,如果剩余内存不足则尝试扩容,扩容成功后再次尝试分配。如果一个CodeBlob被释放了,则对应的空闲内存块会被归还到List中管理。其内存结构如下图:

 

3、MetaSpace

     MetaSpace比较特殊,一个ClassLoader实例对应一个MetaSpace,由该ClassLoader加载的类元数据都会从绑定的MetaSpace中分配内存,当ClassLoader实例被销毁了,则对应的MetaSpace也会

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值