操作句柄Handle(7)


可以將Handle理解成訪問對象的一個“句柄”。垃圾回收時對象可能被移動(對象地址發生改變),通過Handle訪問對象可以對使用者屏蔽垃圾回收細節。

Handle涉及到的相關類的繼承關系如下圖所示。

  

HotSpot會通過Handle對Oop和某些Klass進行操作。下圖左邊顯示了直接訪問的情況,下圖右邊顯示了間接訪問的情況。

  

可以看到,當對Oop直接引用時,如果Oop的地址發生變化,那么所有的引用都要更新,如圖有3處引用,所以都需要更新;當通過Handle對Oop間接引用時,如果Oop的地址發生變化,那么只需要更新Handle中保存的對Oop的引用即可。

每個Oop都有一個對應的Handle,這樣通過對應的Handle可直接獲取對應的Oop,不需要進行類型轉換。為了讀者方便閱讀,這里再次給出了Oop繼承體系,如下圖所示。

 

可以看到Handle繼承體系與Oop繼承體系類似,實際上也有相應的對應關系,例如通過instanceHandle操作instanceOopDesc,通過objArrayHandle操作objArrayOopDesc。

與Oop類似,Klass也需要通過Handle來間接引用。如下幾個Klass有對應的Handle: 

Klass -klassHandle
    InstanceKlass - instanceKlassHandle
ConstantPool - constantPoolHandle
Method - methodHandle

現在假設有個Person類,還有這個類的一個Person對象,那么可以像下圖這樣理解Handle、Oop與Klass之間的關系: 

下面具體看一下Handle的定義,如下:

// Base class for all handles. Provides overloading of frequently
// used operators for ease of use.

class Handle VALUE_OBJ_CLASS_SPEC {
 private:
  oop* _handle; // 可以看到是對oop的封裝

 protected:
  oop obj() const {
	  return _handle == NULL ? (oop)NULL : *_handle;
  }
  oop non_null_obj() const {
	  assert(_handle != NULL, "resolving NULL handle");
	  return *_handle;
  }
  ...
}

調用obj()或non_null_obj()方法獲取被封裝的oop對象,不過並不會直接調用Handle對象的obj()或non_null_obj()對象,而是通過C++的運算符重載來獲取。Handle類重載了()和->運算符,如下:

// General access
oop operator () () const {
   return obj();
}
oop operator -> () const { 
   return non_null_obj(); 
}

可以這樣使用:

oop     obj = ...;
Handle  h1(obj); // allocate new handle

oop obj1 = h1(); // get handle value
h1->print(); // invoking operation on oop  

由於重載了運算符(),所以h1()會調用()運算符的重載方法,重載方法中調用obj()獲取到被封裝的oop對象。重載了運算符->,所以h1->print()同樣會調用oop對象的print()方法。

另外還需要知道,Handle分配在本地線程的HandleArea中,這樣在進行垃圾回收時,只需要掃描每個線程的HandleArea即可找出句柄引用的活躍對象。

每次創建句柄對象時,都會調用到Handle類的構造函數,其中一個構造函數如下:

inline Handle::Handle(oop obj) {
  if (obj == NULL) {
     _handle = NULL;
  } else {
     HandleArea* ha = Thread::current()->handle_area();
     _handle = ha->allocate_handle(obj);
  }
}

參數obj就是要通過句柄操作的對象。通過調用當前線程的handle_area()函數獲取HandleArea,然后調用allocate_handle()在HandleArea中分配存儲obj的空間並將obj保存起來。

每個線程都會有一個_handle_area屬性,定義如下:

// Thread local handle area for allocation of handles within the VM
HandleArea*   _handle_area; // 定義在Thread類中

在創建線程時初始化_handle_area屬性,然后通過handle_area()函數獲取這個屬性的值。 

allocate_handle()函數為對象obj分配一個新的句柄,實現如下:

oop* real_allocate_handle(oop obj) {
    oop* handle = (oop*) Amalloc_4(oopSize);
    *handle = obj;
    return handle;
}

分配空間並完成obj的存儲操作。 

句柄的釋放要通過HandleMark來完成,不過在介紹HandleMark之前需要介紹一下FHandleArea、Area及Chunk等類的實現,下一篇會詳細分析。

 

相關文章的鏈接如下:

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) 

關注公眾號,有HotSpot源碼剖析系列文章!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM