JVM之线程资源标记ResourceMark

本文详细介绍了JVM中的ResourceMark类及其在内存资源管理中的作用。ResourceMark用于创建栈上对象,并通过ResourceMarkImpl管理线程的ResourceArea,保存和恢复内存状态。同时,文章讲解了Arena类,作为内存分配管理器,如何通过Chunk链表分配和回收内存。通过对ResourceMark和Arena的分析,理解JVM线程内存管理的细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在前面介绍JVM的类加载中比如Field和Method的解析之前,都时要县创建一个ResourceMark对象,那么我们今天介绍下ResourceMark这个类。

ResourceMark类的主要成员以及方法

  1. ResourceMark继承StackObj,表示它是分配在栈上的, 它内部持有 ResourceMarkImpl实现类的引用.在构造函数中,会初始化ResourceMarkImpl类的构造方法传入当前线程的ResouceArea对象。
class ResourceMark: public StackObj {
  const ResourceMarkImpl _impl;
  // 构造方法
  ResourceMark(ResourceArea* area, Thread* thread) :
    _impl(area),
    _thread(thread),
    _previous_resource_mark(nullptr)
  {
    if (_thread != nullptr) {
      assert(_thread == Thread::current(), "not the current thread");
      _previous_resource_mark = _thread->current_resource_mark();
      _thread->set_current_resource_mark(this);
    }
  }
public:
  ResourceMark() : ResourceMark(Thread::current()) {}
  explicit ResourceMark(Thread* thread)
    : ResourceMark(thread->resource_area(), thread) {}
  explicit ResourceMark(ResourceArea* area)
    : ResourceMark(area, DEBUG_ONLY(Thread::current_or_null()) NOT_DEBUG(nullptr)) {}

  void reset_to_mark() { _impl.reset_to_mark(); }
};

 

ResourceMark的实现类ResourceMarkImpl构造函数和方法

  1. 构造函数传入当前线程的ResourceArea对象,保存到_area变量。 并调用SavedState构造函数初始化_saved_state变量。 然后再函数体重调用ResourceArea的activate_state校验当前资源状态,并且将_nesting变量加1。
class ResourceMarkImpl {
  ResourceArea* _area;   
  ResourceArea::SavedState _saved_state; // ResourceArea的快照
  
public:
  explicit ResourceMarkImpl(ResourceArea* area) :
  _area(area),
  _saved_state(area)
  {
    _area->activate_state(_saved_state);
  }
  explicit ResourceMarkImpl(Thread* thread)
    : ResourceMarkImpl(thread->resource_area()) {}
};

 SavedState是保存ResourceArea当前使用状态的快照。

class SavedState {
    friend class ResourceArea;
    Chunk* _chunk;
    char* _hwm;
    char* _max;
    size_t _size_in_bytes;

  public:
    SavedState(ResourceArea* area) :
      _chunk(area->_chunk),
      _hwm(area->_hwm),
      _max(area->_max),
      _size_in_bytes(area->_size_in_bytes)
    {}
  };

ResourceMarkImpl的析构函数

在ResouceMark对象是在栈上分配,所以它在生命周期结束后,也会调用 ResourceMarkImpl的析构函数,从而调用reset_to_mark执行重置ResouceArea到分配内存空间之前的快照状态,然后调用deactivate_state将_nesting将1.

~ResourceMarkImpl() {
    reset_to_mark();
    _area->deactivate_state将(_saved_state);
  }
    void reset_to_mark() const {
    _area->rollback_to(_saved_state);
  }

 reset_to_mark调用reset_to_mark函数,最终通过调用ResourceArea的 rollback_to函数并传入之前保存的内存资源快照_saved_state。

  • 首先判断UseMallocOnly是否只是用Malloc分配,如果是,则调用free_malloced_objects释放上一次分配的资源,这个默认是false,所以直接跳过。
  • 如果快照状态中当前的Chunk的_next指针不为空指针,执行set_size_in_bytes重置到Area中总的Chunk的字节数,然后调用当前的next_chop函数
void rollback_to(const SavedState& state) {
    assert(_nesting > state._nesting, "rollback to inactive mark");
    assert((_nesting - state._nesting) == 1, "rollback across another mark");
    if (UseMallocOnly) {
      free_malloced_objects(state._chunk, state._hwm, state._max, _hwm);
    }
    if (state._chunk->next() != nullptr) { 
      assert(size_in_bytes() > state._size_in_bytes,
             "size: " SIZE_FORMAT ", saved size: " SIZE_FORMAT, size_in_bytes(), state._size_in_bytes);
      set_size_in_bytes(state._size_in_bytes);
      state._chunk->next_chop();
    } else {
      assert(size_in_bytes() == state._size_in_bytes, "Sanity check");
    }
    _chunk = state._chunk;      // Roll back to saved chunk.
    _hwm = state._hwm;
    _max = state._max;

    // Clear out this chunk (to detect allocation bugs)
    if (ZapResourceArea) {
      memset(state._hwm, badResourceValue, state._max - state._hwm);
    }
  }

释放当前Chunk后面的Chunk资源

next_chop调用chop释放当前Chunk的后面连接的Chunk的内存空间,可以是通过delete去释放空间.

void Chunk::next_chop() {
  _next->chop();
  _next = NULL;
}

void Chunk::chop() {
  Chunk *k = this;
  while( k ) {
    Chunk *tmp = k->next();
    // 清除当前的Chunk的内存
    if (ZapResourceArea) memset(k->bottom(), badResourceValue, k->length());
    delete k;                   // Free chunk (was malloc'd)
    k = tmp;
  }
}

分配内存资源管理器Arena

  1. ResourceArea是继承Arena的资源管理器,ResourceMark是使用它去做内存资源分配。
    class ResourceArea: public Arena {
      friend class VMStructs;
    
    public:
      ResourceArea(MEMFLAGS flags = mtThread) :
        Arena(flags) DEBUG_ONLY(COMMA _nesting(0)) {}
    
      ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) :
        Arena(flags, init_size) DEBUG_ONLY(COMMA _nesting(0)) {}

    Arena类的主要Field

class Arena : public CHeapObj<mtNone> {
protected:
  MEMFLAGS    _flags;         // 追踪内存的标识
  Chunk *_first;             // 第一个分配的chunk
  Chunk *_chunk;             // 当前 chunk
  char *_hwm, *_max;        // 高水位 and 当前chunk的最大水位

 MEMFLAGS是内存种类的枚举,主要是以下几个内存存储的类别

define MEMORY_TYPES_DO(f)                                    
  f(mtJavaHeap,       "Java Heap")   // Java 堆            
  f(mtClass,          "Class")  // java的class对象             
  f(mtThread,         "Thread")   // 线程对象
  f(mtThreadStack,    "Thread Stack")
  f(mtCode,           "Code")      // 生成的字节码
  f(mtGC,             "GC")                                   
  f(mtGCCardSet,      "GCCardSet")   // G1使用的卡表 
  f(mtCompiler,       "Compiler")                             
  f(mtJVMCI,          "JVMCI")                                 
  f(mtInternal,       "Internal")          

Chunk是分配内存资源管理者

Chunk是通过_next指针链接形成单向链表, _len记录当前Chunk的大小

class Chunk: CHeapObj<mtChunk> {
 private:
  Chunk*       _next;     // next指针指向下一个,形成链表
  const size_t _len;      //当前Chunk的大小
}

Arena构造函数初始化

  • Chunk继承了CHeapObj,它是重载new关键字,所以new Chunk就会再堆区分配对象并设置初始化大小为Chunk::init_size(从下面枚举可知初始大小为: 1k减去Chunk自身占用内存大小)。
  • 刚开始_first和_chunk变量是指向同一个chunk对象,_hwn初始化保存没有分配资源的位置. _max初始化为_hwn加当前chunk的大小len。
  • set_size_in_bytes设置可分配的字节数大小。
Arena::Arena(MEMFLAGS flag) : _flags(flag), _size_in_bytes(0) {
  _first = _chunk = new (AllocFailStrategy::EXIT_OOM, Chunk::init_size) Chunk(Chunk::init_size);
  _hwm = _chunk->bottom();   
  _max = _chunk->top();
  MemTracker::record_new_arena(flag);
  set_size_in_bytes(Chunk::init_size);
}

// 内存大小枚举
enum {
#ifdef _LP64
    slack      = 40,   // [RGV] Not sure if this is right, but make it a multiple of 8.
#else
    slack      = 24,  // suspected sizeof(Chunk) + internal malloc headers
#endif
    tiny_size  =  256  - slack, // Size of first chunk (tiny)
    init_size  =  1*K  - slack, // Size of first chunk (normal aka small)
    medium_size= 10*K  - slack, // Size of medium-sized chunk
    size       = 32*K  - slack, // Default size of an Arena chunk (following the first)
    non_pool_size = init_size + 32 // An initial size which is not one of above
  };

 Arena分配内存资源

  • 首先是64位内存对齐
  • 然后调用internal_amalloc进行分配内存
void* Amalloc(size_t x, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) {
    x = ARENA_ALIGN(x); // 64位内初对齐
    // 校验内存对齐
    assert(is_aligned(_max, ARENA_AMALLOC_ALIGNMENT), "chunk end unaligned?");
    return internal_amalloc(x, alloc_failmode);
  }
  • 调用pointer_delta计算当前Chunk是有剩余的空间分配申请的x字节,则直接 _hwm 加上x,返回这次分配的_hwm指针。
  • 如果当前Chunk没有足够的剩余空间,则调用grow进行创建新的Chunk的进行分配内存空间。
void* internal_amalloc(size_t x, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM)  {
    assert(is_aligned(x, BytesPerWord), "misaligned size");
    if (pointer_delta(_max, _hwm, 1) >= x) {
      char *old = _hwm;
      _hwm += x;
      return old;
    } else {
      return grow(x, alloc_failmode);
    }
  }

 

  • 分配新的Chunk分配内存空间,空间大小取申请的x的64位对齐和Chunk::size(实际大小是32k-Chunk对象自身占用大小)
    void* Arena::grow(size_t x, AllocFailType alloc_failmode) {
      size_t len = MAX2(ARENA_ALIGN(x), (size_t) Chunk::size);
    
      Chunk *k = _chunk;        //记录之前的Chunk
      _chunk = new (alloc_failmode, len) Chunk(len);
    
      if (_chunk == NULL) {
        _chunk = k;  // 创建 Chunk失败,则恢复之前的Chunk指针
        return NULL;
      }
      if (k) k->set_next(_chunk);   // 将新的chunk设置_next指针
      else _first = _chunk;
      _hwm  = _chunk->bottom(); //保存chunk的最小地址
      _max =  _chunk->top();  // 保存chunk的最大地址
      set_size_in_bytes(size_in_bytes() + len);//设置Arena的所有Chunk的总的字节大小
      void* result = _hwm; // 返回Chunk开始分配的内存的void*指针
      _hwm += x; // 将_hwm加上x,那么就分配了[_hwn,_hwn+x]内存空间,返回result指针指向_hwn加x之前的地址指针
      return result;
    }

    Arena的分配内存是Chunk的链表组成

    总结

    本文主要分析栈上分配的ResouceMark,利用线程的ResourceArea进行分配前的快照保存以及内存分配,并利息ResourceMarkImpl的析构函数,释放当前Chunk后面分配的内存空间,并恢复分配前的内存快照的状态,

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值