zoukankan      html  css  js  c++  java
  • 句柄Handle的释放(8)

    本篇首先介绍几个与句柄分配与释放密切相关的类,然后重点介绍句柄的释放。

    1、HandleArea、Area与Chunk

    句柄都是在HandleArea中分配并存储的,类的定义如下:

    // Thread local handle area
    class HandleArea: public Arena {
      friend class HandleMark;
      ...
      HandleArea* _prev;  // HandleArea通过_prev连接成单链表
     public:
      // Constructor
      HandleArea(HandleArea* prev) : Arena(Chunk::tiny_size) {
        _prev = prev;
      }
     
      // Handle allocation
     private:
      oop* real_allocate_handle(oop obj) { // 分配内存并存储obj对象
        oop* handle = (oop*) Amalloc_4(oopSize);
        *handle = obj;
        return handle;
      }
    
      // ... 
    };
    

    real_allocate_handle()用来在HandleArea中分配内存并存储obj对象,方法会调用父类Arena中定义的Amalloc_4()函数。HandleArea的父类Arena的定义如下:

    // Fast allocation of memory
    class Arena: public CHeapObj {
    protected:
      ...
      Chunk *_first;                // First chunk
      Chunk *_chunk;                // current chunk
      char  *_hwm, *_max;            // High water mark and max in current chunk
      void* grow(size_t x);         // Get a new Chunk of at least size x
      size_t _size_in_bytes;        // Size of arena (used for memory usage tracing)
    public:
      Arena();
      Arena(size_t init_size);
      Arena(Arena *old);
      ~Arena()                      { _first->chop(); }
      char* hwm() const             { return _hwm; }
     
      // Fast allocate in the arena.  Common case is: pointer test + increment.
      // Further assume size is padded out to words
      // Warning:  in LP64, Amalloc_4 is really Amalloc_8
      void *Amalloc_4(size_t x) {
        //  保证在64位上,x是一个字的整倍数
        assert( (x&(sizeof(char*)-1)) == 0, "misaligned size" ); 
        if (_hwm + x > _max) {
          return grow(x);
        } else {
          char *old = _hwm;
          _hwm += x;
          return old;
        }
      }
     
      ... 
    };  
    

    Amalloc_4()函数会在当前的Chunk块中分配内存,如果当前块的内存不够,则会调用grow()方法分配新的Chunk块,然后在新的Chunk块中分配内存。

    这个类通过_first、_chunk等管理着一个连接成单链表的Chunk,其中 _first指向单链表的第一个Chunk,而_chunk指向的是当前可提供内存分配的Chunk,通常为单链表的最后一个块Chunk。_hwm与_max指示当前可分配内存的Chunk的一些分配信息。

    Chunk类的定义如下:

    // Linked list of raw memory chunks
    class Chunk: public CHeapObj {
     public:
      ...
      Chunk*       _next;           // Next Chunk in list
      size_t       _len;            // Size of this Chunk
     
       
      // Boundaries of data area (possibly unused)
      char* bottom() const { return ((char*) this) + sizeof(Chunk);  }
      char* top()    const { return bottom() + _len; }
    };
    

     HandleArea与Chunk类之间的关系如下图所示。

     

    2、HandleMark

    每一个Java线程都有一个私有的句柄区_handle_area来存储其运行过程中句柄信息,这个句柄区是随着Java线程的栈帧变化的。Java线程每调用一个Java方法就会创建一个对应HandleMark来保存已经的对象句柄,然后等调用返回后恢复。

    HandleMark主要用于记录当前线程的HandleArea的内存地址top,当相关的作用域执行完成后,当前作用域之内的HandleMark实例自动销毁,在HandleMark的析构函数中会将HandleArea的当前内存地址到方法调用前的内存地址top之间的所有分配的地址中存储的内容都销毁掉,然后恢复当前线程的HandleArea的内存地址top到方法调用前的状态。

    C++的析构函数专门用来释放内存,这绝对是一个需要好好学习的知识点。

    HandleMark一般情况下直接在线程栈内存上分配,应该继承自StackObj,但是部分情况下HandleMark也需要在堆内存上分配,所以没有继承自StackObj,并且为了支持在堆内存上分配,重载了new和delete方法。

    类的定义如下:

    class HandleMark {
     private:
      Thread     *_thread;          // thread that owns this mark
      HandleArea *_area;            // saved handle area
      Chunk      *_chunk;           // saved arena chunk,Chunk和Area配合,获得准确的内存地址
      char       *_hwm, *_max;      // saved arena info
      size_t     _size_in_bytes;    // size of handle area
      // Link to previous active HandleMark in thread
      HandleMark* _previous_handle_mark;
     
      void initialize(Thread* thread);                // common code for constructors
      void set_previous_handle_mark(HandleMark* mark) { _previous_handle_mark = mark; }
      HandleMark* previous_handle_mark() const        { return _previous_handle_mark; }
     
      size_t size_in_bytes() const { return _size_in_bytes; }
     public:
      HandleMark();       // see handles_inline.hpp
      HandleMark(Thread* thread) {
          initialize(thread);
      }
      ~HandleMark();
     
      ...
    };
    

    handleMark也会通过_previous_handle_mark属性形成一条单链表。 

    在HandleMark的构造方法中会调用initialize()方法,方法的实现如下:

    void HandleMark::initialize(Thread* thread) {
      _thread = thread;
      // Save area
      _area  = thread->handle_area();
      // Save current top
      _chunk = _area->_chunk;
      _hwm   = _area->_hwm;
      _max   = _area->_max;
      _size_in_bytes = _area->_size_in_bytes;
     
      // Link this in the thread
      // 将当前HandleMark实例同线程关联起来
      HandleMark* hm = thread->last_handle_mark();
      set_previous_handle_mark(hm);
      thread->set_last_handle_mark(this); // 注意,线程中的_last_handle_mark属性来保存HandleMark对象
    }
    

    方法主要初始化一些属性。Thread中定义的_last_handle_mark属性的定义如下:

    // Point to the last handle mark
    HandleMark* _last_handle_mark;
    

    handleMark的析构函数如下: 

    HandleMark::~HandleMark() {
      HandleArea* area = _area;   // help compilers with poor alias analysis
     
      // Delete later chunks
      if( _chunk->next() ) {
        // reset arena size before delete chunks. Otherwise, the total
        // arena size could exceed total chunk size
        assert(area->size_in_bytes() > size_in_bytes(), "Sanity check");
        area->set_size_in_bytes(size_in_bytes());
        // 删除当前Chunk以后的所有Chunk,即在方法调用期间新创建的Chunk
        _chunk->next_chop();
      } else {
        // 如果没有下一个Chunk,说明未分配新的Chunk,则area的大小应该保持不变
        assert(area->size_in_bytes() == size_in_bytes(), "Sanity check");
      }
      // Roll back arena to saved top markers
      // 恢复area的属性到HandleMark构造时的状态
      area->_chunk = _chunk;
      area->_hwm = _hwm;
      area->_max = _max;
     
      // Unlink this from the thread
      // 解除当前HandleMark跟线程的关联
      _thread->set_last_handle_mark(previous_handle_mark());
    }
    

    创建一个新的HandleMark以后,新的HandleMark保存当前线程的area的当前chunk,_hwm ,_max等属性,代码执行期间新创建的Handle实例是在当前线程的area中分配内存,这会导致当前线程的area的当前chunk,_hwm ,_max等属性发生变更,因此代码执行完成后需要将这些属性恢复成之前的状态,并把代码执行过程中新创建的Handle实例的内存给释放掉。 

    相关文章的链接如下:

    1、在Ubuntu 16.04上编译OpenJDK8的源代码 

    2、调试HotSpot源代码

    3、HotSpot项目结构 

    4、HotSpot的启动过程 

    5、HotSpot二分模型(1)

    6、HotSpot的类模型(2)  

    7、HotSpot的类模型(3) 

    8、HotSpot的类模型(4)

    9、HotSpot的对象模型(5)  

    10、HotSpot的对象模型(6) 

    11、操作句柄Handle(7)

    关注公众号,有HotSpot源码剖析系列文章!

     

      

  • 相关阅读:
    LIS(最长的序列)和LCS(最长公共子)总结
    SVN最有效的方法打基线
    SRM 638 Div2
    poj 2038 Team Rankings 枚举排列
    BZOJ 2809 APIO2012 dispatching Treap+启示式合并 / 可并堆
    Java中的工具类和新特性
    微信公众平台PHP开发
    全国各大 oj 分类题集...
    语言处理程序
    Android开发
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/13303505.html
Copyright © 2011-2022 走看看