1. 程式人生 > >google protobuf 反射機制學習筆記

google protobuf 反射機制學習筆記

持續更新中。。。

何為反射機制

基本概念

指程式可以訪問、檢測和修改它本身狀態或行為的一種能力

程式集包含模組,而模組包含型別,型別又包含成員。反射則提供了封裝程式集、模組和型別的物件。您可以使用反射動態地建立型別的例項,將型別繫結到現有物件,或從現有物件中獲取型別。然後,可以呼叫型別的方法或訪問其欄位和屬性。

我(c++程式設計師)關注的問題

  • 如何在程式執行過程中通過型別名字(一個字串,合法但是內容在編譯期間未知,比如是在配置檔案中獲取的)創建出型別物件.
  • 如果在程式執行過程中通過物件和物件的屬性的名字(一個字串,合法但是內容在編譯期間未知,比如是通過通訊包獲取的)獲取,修改對應屬性.
  • 如果在程式執行過程中通過物件和物件方法的名字(一個字串,合法但是內容在編譯期間未知,比如是從使用者輸入獲取的)呼叫對應的方法.

protobuf 反射使用簡介

舉個例子 :
當你有一個test.proto 比如 :

package T;
message Test
{
    optional int32 id = 1;
}

通過型別名字創建出型別物件.

  • 預先編譯好proto模式
//! 利用型別名字構造物件.
/*!
 * @Param type_name 型別名字,比如 "Test.TestMessage".
 * @Return 物件指標,new 出來的,使用者負責釋放.
 */
#include <google/protobuf/descriptor.h> #include <google/protobuf/message.h> #include "cpp/test.pb.h" // 這是protoc給你生成的檔案 int main() { // 先獲得型別的Descriptor . auto descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("T.Test"); if (nullptr == descriptor) { return
0 ; } // 利用Descriptor拿到型別註冊的instance. 這個是不可修改的. auto prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor); if ( nullptr == descriptor) { return 0 ; } // 構造一個可用的訊息. auto message = prototype->New(); // 只有當我們預先編譯了test訊息並且正確連結才能這麼幹. auto test = dynamic_cast<T::Test*>(message); // 直接呼叫message的具體介面 // 其實這些介面是語法糖介面.所以並沒有對應的反射機制來對應呼叫. // 反射機制實現了的Set/Get XXX系列介面,是屬於Reflection的介面,接收Message作為引數. test->set_id(1); std::cout<<test->Utf8DebugString()<<std::endl; delete message ; return 0 ; }
  • 直接解析proto 檔案模式
#include <iostream>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>
int main()
{
    // 準備配置好檔案系統
    google::protobuf::compiler::DiskSourceTree sourceTree;
    // 將當前路徑對映為專案根目錄 , project_root 僅僅是個名字,你可以你想要的合法名字.
    sourceTree.MapPath("project_root","./");
    // 配置動態編譯器.
    google::protobuf::compiler::Importer importer(&sourceTree, NULL);
    // 動態編譯proto原始檔。 原始檔在./source/proto/test.proto .
    importer.Import("project_root/source_proto/test.proto");
    // 現在可以從編譯器中提取型別的描述資訊.
    auto descriptor1 = importer.pool()->FindMessageTypeByName("T.Test");
    // 建立一個動態的訊息工廠.
    google::protobuf::DynamicMessageFactory factory;
    // 從訊息工廠中創建出一個型別原型.
    auto proto1 = factory.GetPrototype(descriptor1);
    // 構造一個可用的訊息.
    auto message1= proto1->New();
    // 下面是通過反射介面給欄位賦值.
    auto reflection1 = message1->GetReflection();
    auto filed1 = descriptor1->FindFieldByName("id");
    reflection1->SetInt32(message1,filed1,1);
    // 列印看看
    std::cout << message1->DebugString();
    // 刪除訊息.
    delete message1 ;
    return 0 ;
}

通過物件和物件的屬性的名字獲取,修改對應屬性.

首先定義mesage :
對於上文提到的 test.proto

#include "cpp/test.pb.h"
#include <iostream>

int main()
{
    // 拿到一個物件,不在乎怎麼拿到,可以是通過反射拿到。
    // 這裡簡單直接的建立一個.
    T::Test p_test ;
    // 拿到物件的描述包.
    auto descriptor = p_test.GetDescriptor() ;
    // 拿到物件的反射配置.
    auto reflecter = p_test.GetReflection() ;
    // 拿到屬性的描述包.
    auto field = descriptor->FindFieldByName("id");
    // 設定屬性的值.
    reflecter->SetInt32(&p_test , field , 5 ) ;
    // 獲取屬性的值.
    std::cout<<reflecter->GetInt32(p_test , field)<< std::endl ;
    return 0 ;
}

通過物件和物件方法的名字呼叫對應的方法.

//TODO

protobuf 反射實現解析.

基本概念

Descriptor系列.

::google::protobuf::Descriptor 系列包括:

  • Descriptor – 用來描述 訊息
  • FieldDescriptor – 用來描述 欄位
  • OneofDescriptor – 用來描述 聯合體
  • EnumDescriptor – 用來描述 列舉
  • EnumValueDescriptor – 用來描述 列舉值
  • ServiceDescriptor – 用來描述 伺服器
  • MethodDescriptor – 用來描述 伺服器方法
  • FileDescriptor – 用來描述 檔案

DescriptorProto系列是一些用protobuf定義的,用來描述所有由protbuf產生的型別的型別資訊包.
對應的proto檔案在 : https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto
Descriptor 系列最大的message是 FileDescriptor . 每個檔案會生成一個包含本檔案所有資訊的FileDescriptor包.

舉個例子 :
對於上文提到的 test.proto , protoc 會給就會自動填裝一個描述包,類似於:

::google::protobuf::FileDescriptorProto file;
file.set_name("test.proto");
file.set_packet("T")
auto desc = file.add_message_type() ;
desc->set_name("T.Test");
auto id_desc  = desc->mutable_field();
id_desc->set_name("id");
id_desc->set_type(::google::protobuf::FieldDescriptorProto::TYPE_INT32);
id_desc->set_number(1);
//...

然後儲存起來.
如果你讀protoc生成的 test.pb.cc檔案 你會看到這樣的程式碼 :

::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
    "\n\013test.proto\022\001T\"\022\n\004Test\022\n\n\002id\030\001 \001(\005", 36);

其實就是在protoc生成程式碼中hard code 了對應proto檔案的FileDescriptor包序列化之後的資料. 作為引數直接使用.

offset

任何一個物件最終都對應一段記憶體,有記憶體起始(start_addr)和結束地址,
而物件的每一個屬性,都位於 start_addr+$offset ,所以當物件和對應屬性的offset已知的時候,
屬性的記憶體地址也就是可以獲取的。


//! 獲取某個屬性在對應型別物件的記憶體偏移.
#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TYPE, FIELD)    \
  static_cast<int>(                                           \
      reinterpret_cast<const char*>(                          \
          &reinterpret_cast<const TYPE*>(16)->FIELD) -        \
      reinterpret_cast<const char*>(16))

DescriptorDatabase

DescriptorDatabase是一個純虛基類,描述了一系列符合通過名字(檔名,符號名。。。) 來獲取FileDescriptorProto的介面 :

// 這裡我幹掉了裡面的英文註釋.
class LIBPROTOBUF_EXPORT DescriptorDatabase {
    public:
        inline DescriptorDatabase() {}
        virtual ~DescriptorDatabase();
        virtual ~DescriptorDatabase();
        // 通過檔名字找.
        virtual bool FindFileByName(const string& filename,
                FileDescriptorProto* output) = 0;
        // 通過符號名字找.
        virtual bool FindFileContainingSymbol(const string& symbol_name,
                FileDescriptorProto* output) = 0;
        // 通過擴充套件資訊找.
        virtual bool FindFileContainingExtension(const string& containing_type,
                int field_number,
                FileDescriptorProto* output) = 0;
        // 通過擴充套件資訊的tag數字找...
        virtual bool FindAllExtensionNumbers(const string& /* extendee_type */,
                vector<int>* /* output */) {
            return false;
        }
    private:
        GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorDatabase);
};

核心的兩個派生類是 :

  • EncodedDescriptorDatabase

    • 支援DescriptorDatabase的全部介面
    • 接收序列化之後的FileDescriptorProto, 儲存在map中備查.
    • 這個類對應著預先編譯連結好的那些型別的反射機制。
  • SourceTreeDescriptorDatabase

    • 僅支援DescriptorDatabaseFindFileByName介面。其餘直接返回false.
    • 每次查詢某個檔案都是從磁碟讀入proto的原始檔,編譯解析後返回對應的FileDescriptorProto .
    • 這個類對應著動態編譯proto原始檔的時候的反射機制.

這裡我不探究protobuf 是如何執行時編譯proto原始檔.

DescriptorPool

任何時候想要查詢一個Descriptor , 都是去DescriptorPool裡面查詢。
DescriptorPool 實現了這樣的機制 :

  • 快取所有查詢的檔案的Descriptor
  • 查詢Descriptor的時候,如果自身快取查到就直接返回結果,
    否則去自帶的DescriptorDatabase中查FileDescriptorProto
    查到就轉化成Descriptor, 返回結果並且快取.

class LIBPROTOBUF_EXPORT  DescriptorPool{
 public:
  // Create a normal, empty DescriptorPool.
  DescriptorPool();

  // 幹掉一個灰常長的註釋,核心是下面兩條加一些注意事項.
  // 構造一個帶著DescriptorDatabase的Pool 。
  // 這樣查詢的時候,優先從Pool中查詢,找不到就到fallback_database中找.
  class ErrorCollector;
  explicit DescriptorPool(DescriptorDatabase* fallback_database,
                          ErrorCollector* error_collector = NULL);

  ~DescriptorPool();

  // 這個獲取編譯進入二進位制的那些訊息的pool 。這個介面就是我們獲取預先編譯連結好
  // 的訊息的入口。
  // Get a pointer to the generated pool.  Generated protocol message classes
  // which are compiled into the binary will allocate their descriptors in
  // this pool.  Do not add your own descriptors to this pool.
  static const DescriptorPool* generated_pool();


  // Find a FileDescriptor in the pool by file name.  Returns NULL if not
  // found.
  const FileDescriptor* FindFileByName(const string& name) const;
  // .... 一系列Find XXX By XXX  介面 ... , 不全部複製了.

  // Building descriptors --------------------------------------------

  class LIBPROTOBUF_EXPORT ErrorCollector {
  // 不關心這個錯誤收集類...
  };

  // 這個是用FileDescriptorProto 填充FileDescriptor的介面.
  const FileDescriptor* BuildFile(const FileDescriptorProto& proto);

  // Same as BuildFile() except errors are sent to the given ErrorCollector.
  const FileDescriptor* BuildFileCollectingErrors(
    const FileDescriptorProto& proto,
    ErrorCollector* error_collector);
 // 依賴相關介面.
  void AllowUnknownDependencies() { allow_unknown_ = true; }
  void EnforceWeakDependencies(bool enforce) { enforce_weak_ = enforce; }

  // Internal stuff --------------------------------------------------
    // 一系列實現細節的介面。。 不復制。。。
 private:
    // 一系列實現細節的介面。。 不復制。。。

  // 當從pool本身的table找不到的時候,試圖從database中查詢的介面。
  bool TryFindFileInFallbackDatabase(const string& name) const;
  bool TryFindSymbolInFallbackDatabase(const string& name) const;
  bool TryFindExtensionInFallbackDatabase(const Descriptor* containing_type,
                                          int field_number) const;

    // 一系列實現細節的介面。。 不復制。。。

  // See constructor.
  DescriptorDatabase* fallback_database_; // 持有的datebase
  scoped_ptr<Tables> tables_;   // Pool自身的table。 會快取所有查過的檔案內容.

    // 一系列實現細節的介面。。 不復制。。。
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorPool);
}

核心的查詢介面


Symbol DescriptorPool::Tables::FindByNameHelper(
    const DescriptorPool* pool, const string& name) {
  MutexLockMaybe lock(pool->mutex_);
  known_bad_symbols_.clear();
  known_bad_files_.clear();
  //先從快取中查詢.
  Symbol result = FindSymbol(name);

  // 這個是內部實現的細節 不要在意
  if (result.IsNull() && pool->underlay_ != NULL) {
    // Symbol not found; check the underlay.
    result =
      pool->underlay_->tables_->FindByNameHelper(pool->underlay_, name);
  }

  if (result.IsNull()) {
    // 這裡去資料庫嘗試獲取資料.
    // Symbol still not found, so check fallback database.
    if (pool->TryFindSymbolInFallbackDatabase(name)) {
        // 再次剛剛資料庫更新資料之後的快取中獲取資料.
        result = FindSymbol(name);
    }
  }

  return result;
}

MessageFactory

任何時候想要獲取一個型別的instance , 都要去MessageFactory裡面獲取。
MessageFactory 是一個純虛的基類,定義了通過Descripor來獲取對應型別instance的介面.

{
 public:
  inline MessageFactory() {}
  virtual ~MessageFactory();
  // 了通過Descripor來獲取對應型別instance 的介面
  virtual const Message* GetPrototype(const Descriptor* type) = 0;

  // 這個是獲取編譯連結好的那些型別的factory單例的入口.
  static MessageFactory* generated_factory();
  // 這個是對應的像上面哪個單例內填裝資料的介面,protoc自動生成的檔案都會有呼叫.
  static void InternalRegisterGeneratedMessage(const Descriptor* descriptor,
                                               const Message* prototype);
 private:
  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFactory);
}

同樣有兩個核心的派生類

  • GeneratedMessageFactory
    • 一個map , 儲存著DescriptorMessage *
    • 這個類對應著預先編譯連結好的那些型別的反射機制。
  • DynamicMessageFactory
    • 有簡單的快取,儲存自己解析過的Descriptor`` </li>
      <li>可以通過
      Descriptor“,動態的基於記憶體構造出一個Message !!!

解決問題的辦法

通過型別名字創建出型別物件 — 預編譯proto並且連結進入二進位制.

查表!!
是的,你沒猜錯,就是查表!!!

  • 資料儲存在哪裡
    所有的Descriptor儲存在單例的DescriptorPool 中。google::protobuf::DescriptorPool::generated_pool()來獲取他的指標。
    所有的instance 儲存在單例的MessageFactory中。google::protobuf::MessageFactory::generated_factory()來獲取他的指標。

  • 將所有的Descriptor & instance 提前維護到表中備查

在protoc 生成的每個cc檔案中, 都會有下面的程式碼(protobuf V2 版本) :


// xxx 應該替換為檔名,比如test.proto的test.

namespace {

//! 將本檔案內的全部型別的instance註冊進入MessageFactory的介面.
void protobuf_RegisterTypes(const ::std::string&) {
   // 初始化本檔案的reflection資料.
  protobuf_AssignDescriptorsOnce();
  ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
    Test_descriptor_, &Test::default_instance());
}
//! 本檔案的初始介面.
void protobuf_AddDesc_xxx_2eproto() {
  static bool already_here = false;
  if (already_here) return;
  already_here = true;
  GOOGLE_PROTOBUF_VERIFY_VERSION;
  // 註冊本檔案的Descriptor包. 這樣就可以用名字通過generated_pool獲取對應的Descriptor。
  ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
    "\n\013xxx.proto\022\001T\"\022\n\004Test\022\n\n\002id\030\001 \001(\005", 36);
  // 將本檔案的型別instance註冊介面註冊給MessageFactory.
  // 這裡註冊介面是為了實現型別的lazy註冊。如果沒有使用請求某個檔案的型別,就不註冊對應檔案的型別。
  ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
    "xxx.proto", &protobuf_RegisterTypes);
  // 構造並且初始化全部instance.
  Test::default_instance_ = new Test();
  Test::default_instance_->InitAsDefaultInstance();
  // 註冊清理介面.
  ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_xxx_2eproto);
}
//! 下面利用全域性變數的建構函式確保main函式執行之前資料已經進行註冊.
struct StaticDescriptorInitializer_xxx_2eproto {
  StaticDescriptorInitializer_xxx_2eproto() {
    protobuf_AddDesc_xxx_2eproto();
  }
} static_descriptor_initializer_xxx_2eproto_;
}

通過型別名字創建出型別物件 — 執行時態編譯proto

這裡要引入 Importer類.

class LIBPROTOBUF_EXPORT Importer {
 public:
     // 需要使用SourceTree來構造,
     // 不過SourceTree最終是用來構造SourceTreeDescriptorDatabase的。
  Importer(SourceTree* source_tree,
           MultiFileErrorCollector* error_collector);
  ~Importer();

  // 這個就是執行時態載入proto原始檔的介面.
  // 多次呼叫同一個檔案只有第一次有效。
  const FileDescriptor* Import(const string& filename);
  // 拿到DescriptorPool 的介面. 
  // 每個Importer都有自己的DescriptorPool。
  inline const DescriptorPool* pool() const {
    return &pool_;
  }
  // 下面是咱不在意的介面.
  void AddUnusedImportTrackFile(const string& file_name);
  void ClearUnusedImportTrackFiles();

 private:
  // 這兩個資料成員很好的解釋了Importer如何工作 :
  // 有 SourceTreeDescriptorDatabase 構造一個DescriptorPool,這樣
  // 每當有檔案被查詢的時候,如果快取中有,直接返回,如果沒有,
  // SourceTreeDescriptorDatabase 自然會去載入解析原始檔.
  // Import介面則是提前將proto解析載入進入快取的途徑.
  SourceTreeDescriptorDatabase database_;
  DescriptorPool pool_;

  GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Importer);
}

通過物件和物件的屬性的名字獲取,修改對應屬性.

  • GeneratedMessageReflection 的填裝和獲取
    對於每一個message , 都有一個對應的GeneratedMessageReflection 物件.
    這個物件儲存了對應message反射操作需要的資訊.

//!初始化本檔案的所有GeneratedMessageReflection物件.
void protobuf_AssignDesc_xxx_2eproto() {
  protobuf_AddDesc_xxx_2eproto();
  const ::google::protobuf::FileDescriptor* file =
    ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
      "xxx.proto");
  GOOGLE_CHECK(file != NULL);
  Test_descriptor_ = file->message_type(0);
  static const int Test_offsets_[1] = {
    //這裡在計算屬性的記憶體偏移.
    GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, id_),
  };
  // 這裡是個test包填裝的GeneratedMessageReflection物件.
  Test_reflection_ =
    new ::google::protobuf::internal::GeneratedMessageReflection(
      Test_descriptor_,
      Test::default_instance_,
      Test_offsets_,
      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, _has_bits_[0]),
      GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, _unknown_fields_),
      -1,
      ::google::protobuf::DescriptorPool::generated_pool(),
      ::google::protobuf::MessageFactory::generated_factory(),
      sizeof(Test));
}
inline void protobuf_AssignDescriptorsOnce() {
  ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
                 &protobuf_AssignDesc_xxx_2eproto);
}

// message.h 中 message的基本介面.
virtual const Reflection* GetReflection() const {
    return GetMetadata().reflection;
}
// 每個message獲取自己基本資訊的介面.
::google::protobuf::Metadata Test::GetMetadata() const {
  protobuf_AssignDescriptorsOnce();
  ::google::protobuf::Metadata metadata;
  metadata.descriptor = Test_descriptor_;
  metadata.reflection = Test_reflection_;
  return metadata;
}
  • GeneratedMessageReflection 操作具體物件的屬性

按照offset陣列的提示,註解獲取操作對應記憶體,這裡以int32欄位的SetInt32介面為例子.

    • 介面定義
#undef DEFINE_PRIMITIVE_ACCESSORS
#define DEFINE_PRIMITIVE_ACCESSORS(TYPENAME, TYPE, PASSTYPE, CPPTYPE)
void GeneratedMessageReflection::Set##TYPENAME(                            \
      Message* message, const FieldDescriptor* field,                        \
      PASSTYPE value) const {                                                \
    USAGE_CHECK_ALL(Set##TYPENAME, SINGULAR, CPPTYPE);                       \
    if (field->is_extension()) {    /*先不要在意這個*/                       \
      return MutableExtensionSet(message)->Set##TYPENAME(                    \
        field->number(), field->type(), value, field);                       \
    } else {
      /*一般的欄位走這裡*/\
      SetField<TYPE>(message, field, value);                                 \
    }                                                                        \
  }

DEFINE_PRIMITIVE_ACCESSORS(Int32 , int32 , int32 , INT32 )
#undef DEFINE_PRIMITIVE_ACCESSORS
    • 記憶體賦值.

// 找到對應的記憶體地址,返回合適型別的指標.
template <typename Type>
inline Type* GeneratedMessageReflection::MutableRaw(
    Message* message, const FieldDescriptor* field) const {
  int index = field->containing_oneof() ?
      descriptor_->field_count() + field->containing_oneof()->index() :
      field->index();
  void* ptr = reinterpret_cast<uint8*>(message) + offsets_[index];
  return reinterpret_cast<Type*>(ptr);
}
// 設定protobuf的標誌bit.
inline void GeneratedMessageReflection::SetBit(
    Message* message, const FieldDescriptor* field) const {
  if (has_bits_offset_ == -1) {
    return;
  }
  MutableHasBits(message)[field->index() / 32] |= (1 << (field->index() % 32));
}
// 設定某個欄位的值
template <typename Type>
inline void GeneratedMessageReflection::SetField(
    Message* message, const FieldDescriptor* field, const Type& value) const {
  if (field->containing_oneof() && !HasOneofField(*message, field)) {
    ClearOneof(message, field->containing_oneof()); // V3 oneof 型別的清理。
  }
  *MutableRaw<Type>(message, field) = value; // 先直接覆蓋
  field->containing_oneof() ?
      SetOneofCase(message, field) : SetBit(message, field); // 新增標記bit
}

通過物件和物件方法的名字呼叫對應的方法.

//TODO

相關推薦

google protobuf 反射機制學習筆記

持續更新中。。。 何為反射機制 基本概念 指程式可以訪問、檢測和修改它本身狀態或行為的一種能力 程式集包含模組,而模組包含型別,型別又包含成員。反射則提供了封裝程式集、模組和型別的物件。您可以使用反射動態地建立型別的例項,將型別繫結到現有物件,或從

Java Class類與反射機制學習筆記(一)

類的載入 當一個程式使用某一個類時,如果該類還未被載入到記憶體中,則系統會通過載入、連線、初始化三個步驟來實現對這個類的初始化。 載入 就是指將該類的 .class檔案讀入記憶體中,併為之建立一個

Java反射機制學習過程——筆記

Class類 在Java中所有的反射操作都是圍繞Class類來展開的,Class這個類是在JDK1.0的時候出現的,可以說這個類是Java類中的元老李,那麼這個類究竟有什麼特別之處呢?檢視文件發現Class類是一個final類,也就是我們所說的太監類,不可能有子類,但是雖說

java反射機制學習:初始反射機制

pro sta 加載 初始 完整 tac demo 獲得 sys   本人小白一枚,想和大家一起分享我學習java的筆記和心得。 反射機制: 指的是可以於運行時加載、探知、使用編譯期間完全未知的類。 程序在運行狀態中,可以動態加載一個只有名稱的類,對於任意一個已加載的

Java反射學習筆記

nsh boolean 案例 即使 框架 ade 原理 劉德華 種類型 今天重新學習了java中的反射,寫一篇學習筆記總結一下。代碼基本都是照著兩篇博客敲的: 參考一: https://blog.csdn.net/sinat

201711671103《JAVA程式設計》第十二章多執行緒機制學習筆記

教材學習內容總結 1.執行緒是依附於程序的,程序是分配資源的最小單位,執行緒是比程序更小的執行單位。一個程序可以產生多個執行緒,形成多條執行線索。每條線索,即每個執行緒也有它自身的產生,存在和消亡過程,也是一個動態的概念。 2.JAVA 中的多執行緒機制:多執行緒是指一個應用程式同時存在好幾

JAVA 類載入機制學習筆記

JAVA 類生命週期     如上圖所示,Java類的生命週期如圖所示,分別為載入、驗證、準備、解析、初始化、使用、解除安裝。其中驗證、準備、解析這三個步驟統稱為連結。   載入:JVM根據全限定名來獲取一段二進位制位元組流,將二進位制流轉化為方法區的執行時資料結構,在記憶體中生成一個代表

作業系統的記憶體對齊機制學習筆記

 資料對齊並不是作業系統的記憶體結構的一部分,而是C P U結構的一部分 是這麼理解嗎?cpu要讀取記憶體中的資料,以多少為單位進行讀取呢?以4個位元組,還是8個位元組。還是16個位元組為單位來讀取記憶體資料? 目前主要以2個位元組為單位嗎?是的。2個位元組作為對齊單位

Android觸控事件傳遞機制學習筆記

1、Android 觸控事件傳遞機制 http://blog.csdn.net/awangyunke/article/details/22047987 2、Android-onInterceptTouchEvent()和onTouchEvent()總結 h

Spark內部機制學習筆記

1 總體框架結構圖 由上圖我們可以看到Spark應用程式架構主要由Driver Program和Executor構成,Driver負責執行main()和建立SparkContext,Executor主要負責執行Task任務。 2 各大重要元件和概念

Linux下驅動:分層、分離機制學習筆記

 一、分層、分離的概念         分層即把硬體相關和相對穩定的東西給抽出來,並向上提供統一的介面,每一層專注於自己的事情,比如輸入子系統。後續文章中會繼續介紹相關例子;        分離即把硬體相關和相對穩定的東西給分離開來,實際上即是bus-dev-drv模型(平

Google Earth Engine(GEE)學習筆記

GEE工作空間簡介 工作空間預覽 各個按鈕模組簡介 執行簡單例子 訪問GEE要科學上網!!! 可以看到整個工作臺可以分為四大部分:指令碼檔案存放區、程式碼編輯區、輸出控制檯、結果地圖展

Google Earth Engine(GEE)學習筆記

初步列了一個大綱 1. GEE初識 2. GEE示例以及學習資源介紹 3. GEE工作臺介紹 4. GEE資源查詢 5. GEE資源上傳 6. GEE資源匯出 7. GEE控制元件介紹(1

google protobuf 反射的例子

反射就是可以知道一個物件自身的資料屬性,例如有哪些欄位,欄位的值,函式等等。 下面的程式碼展示了protobuf 物件反射的例子。將一個物件按照反射的欄位順序序列化到string,然後反序列化到物件。最後呼叫反射列印其欄位值,可以看到物件能夠還原。 proto檔案為(暫時不

Android非同步訊息處理機制學習筆記

(一)Handler 什麼是Handler Android訊息機制的上層介面,Handler通過傳送和處理Message和Runnable物件來關聯相對應執行緒的MessageQueeu. 可

Android Activity和Intent機制學習筆記

Activity Android中,Activity是所有程式的根本,所有程式的流程都執行在Activity之中,Activity具有自己的生命週期(見http://www.cnblogs.com/feisky/archive/2010/01/01/1637427.ht

Java中的鎖機制學習筆記

今天回顧了下java中的鎖機制,和object類中中的wait(),notify(),notifyAll();每次敲程式碼的時候老看見這幾個方法,總讓人將它們和Android中的notifyDataSetChanged()的方法想到一塊去,其實這些東西在Java

Java反射機制複習筆記

1.瞭解反射的基本原理 2.掌握class類的使用 3.使用Class類並結合java.lang.reflect包取得一個類的完整結構 4.通過反射機制動態的呼叫類中指定的方法,並能向這些方法中傳遞引數 核心概念:一切的操作都將使用object進行完成,包括類,陣列的應用都

JAVA 學習筆記 - 反射機制

-s .html upload 通過 tostring for catch span pack 1. JAVA反射機制的概念 2. 怎樣實例化一個 Class對象 Class.forName(包名.類名); 對象.getClass();

《瘋狂Java講義》學習筆記(十四)類載入機制反射

1、類的載入、連線和初始化 JVM和類 同一個JVM的所有執行緒、所有變數都處於同一個程序裡,它們都使用該JVM程序的記憶體區 當系統出現一下集中情況時,JVM程序將被終止: 程式執行到最後正常結束 程式執行到使用Sytem.exit()或Runti