日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

脱了马甲我也认识你: 聊聊 Android 中类的真实形态

發布時間:2023/12/15 Android 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 脱了马甲我也认识你: 聊聊 Android 中类的真实形态 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這是 ZY 第 19 篇原創技術文章

我們在平時開發過程中,一定定義過無數個千奇百怪的類,但是大家有想過,一個 Java 文件中的 Class,在虛擬機中的真實形態是什么么?

這篇文章就帶大家探討一下在 Android ART 里,類的真實形態,以及類加載的過程

本文基于 ART-8.0.0_r1 分支代碼進行分析

預備知識

  • 了解 Java 基本開發
  • 了解 ClassLoader 基本使用
  • 看完本文可以達到什么程度

  • 了解 Android ART 中類的存在形式
  • 了解 Android ART 中類加載的過程
  • 閱讀前準備工作

  • 下載 ART 源碼 作為參照
  • 文章概覽

    一、在 Java 中如何定義一個類

    對于如何在 Java 代碼中定義一個類,我們一定非常熟悉了,代碼如下:

    class MInterface {void imethod() {} }class Parent { }class Child extends Parent implements MInterface { } 復制代碼

    二、ART 中如何表示一個 Java 類

    那么對于一個 Java 類,在 ART 中是如何表示的呢?
    在 ART 中,也定義了一個 Class 類,用來表示 Java 世界中的類。
    當然,這個類是 c++ 定義的,畢竟 ART 就是 c++ 實現的。???

    下面這張圖展示了 ART 中類的重要部分。

    下面我們就看看這個 Class 的具體定義:

    2.1 類的定義

    // C++ mirror of java.lang.Class class MANAGED Class FINAL : public Object {private:// 指向定義 Class 的 ClassLoader,如果為 null,說明是 bootstrap system loaderHeapReference<ClassLoader> class_loader_;// 對于數組類型有用,保存了數組的原始類型,比如 對于 String[],這里指向的是 String// 對非數組類型,值為 nullHeapReference<Class> component_type_;// 指向 DexCache,如果是運行時生成的 Class,值為 nullHeapReference<DexCache> dex_cache_;HeapReference<ClassExt> ext_data_;// interface table,接口方法表,IfTable 中保存了接口類指針和方法表指針HeapReference<IfTable> iftable_;// Descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName// 類描述符 eg: java.lang.Class 或者 [CHeapReference<String> name_;// 父類,如果是 java.lang.Object 值為 nullHeapReference<Class> super_class_;// 虛方法表,"invoke-virtual" 指令會用到,用來保存父類虛方法以及自身虛方法HeapReference<PointerArray> vtable_;// 保存類屬性,只保存自身屬性uint64_t ifields_;// 指向 ArtMethod 數組,保存了所有的方法,包括私有方法,靜態方法,final 方法,虛方法和繼承的方法uint64_t methods_;// 保存靜態屬性uint64_t sfields_;// 訪問修飾符uint32_t access_flags_;uint32_t class_flags_;// 類實例大小,GC 時使用uint32_t class_size_;// 線程 id,類加載時加鎖使用pid_t clinit_thread_id_;// ClassDex 在 DEX 文件中的 indexint32_t dex_class_def_idx_;// DEX 文件中的類型 idint32_t dex_type_idx_;// 實例屬性數量uint32_t num_reference_instance_fields_;// 靜態變量數量uint32_t num_reference_static_fields_;// 對象大小,GC 時使用uint32_t object_size_;uint32_t object_size_alloc_fast_path_;uint32_t primitive_type_;// ifields 的偏移量uint32_t reference_instance_offsets_;// 類初始化狀態Status status_;// methods_ 中第一個從接口中復制的虛方法的偏移uint16_t copied_methods_offset_;// methods_ 中第一個自身定義的虛方法的偏移uint16_t virtual_methods_offset_;// java.lang.Classstatic GcRoot<Class> java_lang_Class_; }; 復制代碼

    上面的類就是 Java 類在 ART 中的真實形態,各個屬性在上面做了注釋。
    這里對幾個比較重要的屬性再做一下解釋。

    和 Java 類方法有關的兩個屬性是 iftable_,vtable_ 和 methods_。
    其中 iftable_ 保存的是接口中的方法,vtable_ 保存的是虛方法,methods_ 保存的是所有方法。
    什么是虛方法呢?虛方法其實是 C++ 中的概念,在 C++ 中,被 virtual 關鍵字修飾的方法就是虛方法。
    而在 Java 中,我們可以理解為所有子類復寫的方法都是虛方法。

    和 Java 類屬性有關的兩個屬性是 ifields_ 和 sfields_。分別保存的是類的實例屬性和靜態屬性。

    從上面的我們可以看到,Java 類的屬性就都保存在 ART 中定義的 Class 里了。
    其中方法最終會指向 ArtMethod 實例上,屬性,最終會指向 ArtField 實例上。

    2.2 類方法的定義

    在 ART 中,一個 Java 的類方法是用 ArtMethod 實例來表示的。
    ArtMethod 結構如下:

    class ArtMethod FINAL {protected:// 定義此方法的類GcRoot<mirror::Class> declaring_class_;// 訪問修飾符std::atomic<std::uint32_t> access_flags_;// 方法 code 在 dex 中的偏移uint32_t dex_code_item_offset_;// 方法在 dex 中的 indexuint32_t dex_method_index_;// 方法 index,對于虛方法,指的是 vtable 中的 index,對于接口方法,指的是 ifTable 中的 indexuint16_t method_index_;// 方法的熱度計數,Jit 會根據此變量決定是否將方法進行編譯uint16_t hotness_count_;struct PtrSizedFields {ArtMethod** dex_cache_resolved_methods_;void* data_;// 方法的入口void* entry_point_from_quick_compiled_code_;} ptr_sized_fields_; } 復制代碼

    2.3 類屬性的定義

    在 ART 中,一個 Java 類屬性是用 ArtField 實例來表示的。
    ArtField 結構如下:

    class ArtField FINAL {private:// 定義此屬性的類GcRoot<mirror::Class> declaring_class_;// 訪問修飾符uint32_t access_flags_ = 0;// 變量在 dex 中的 iduint32_t field_dex_idx_ = 0;// 此變量在類或者類實例中的偏移uint32_t offset_ = 0; } 復制代碼

    三、ART 中加載類的過程

    3.1 類加載的本質

    在 Java 中定義好一個類之后,還需要通過 ClassLoader 進行加載。
    我們經常會說到類加載,但是類加載的本質是什么呢?
    在我們上面了解了一個 Java 類在 ART 中的真實形態以后,我們就比較容易理解類加載的本質了。
    我們都知道,Java 文件編譯完成的產物是 .class 文件,在 Android 中是 .dex 文件,類加載的本質就是解析 .class / .dex 文件,并根據對應的信息生成 ArtField,ArtMethod,最后生成 Class 實例。
    再簡單點來說,類加載的本質就是根據 .dex 文件內容創建 Class 實例。

    3.2 ART 中類加載的入口 -- ClassLinker#DefineClass

    在 Android 中,常見的兩個 ClassLoader 就是 PathClassLoader 和 DexClassLoader,都是繼承了 BaseDexClassLoader,我們就從 BaseDexClassLoader#findClass 開始看一下整個加載的流程。

    // BaseDexClassLoader#findClass protected Class<?> findClass(String name) throws ClassNotFoundException {List<Throwable> suppressedExceptions = new ArrayList<Throwable>();Class c = pathList.findClass(name, suppressedExceptions);// ...return c; } // DexPathList#findClass public Class<?> findClass(String name, List<Throwable> suppressed) {for (Element element : dexElements) {Class<?> clazz = element.findClass(name, definingContext, suppressed);if (clazz != null) {return clazz;}}// ...return null; } // Element#findCLass public Class<?> findClass(String name, ClassLoader definingContext,List<Throwable> suppressed) {return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed): null; } 復制代碼

    從上面的代碼來看,BaseDexClassLoader#findClass 一路調用,調用到 DexFile#loadClassBinaryName,我們再繼續往下看。

    // DexFile public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {return defineClass(name, loader, mCookie, this, suppressed); }private static Class defineClass(String name, ClassLoader loader, Object cookie,DexFile dexFile, List<Throwable> suppressed) {Class result = null;try {result = defineClassNative(name, loader, cookie, dexFile);} catch (NoClassDefFoundError e) {if (suppressed != null) {suppressed.add(e);}} catch (ClassNotFoundException e) {if (suppressed != null) {suppressed.add(e);}}return result; } 復制代碼

    在 DexFile 里,最終調用到 defineClassNative 方法去加載 Class,對應到 JNI 中的方法是 DexFile_defineClassNative,位于 runtime/native/dalvik_system_DexFile.cc 文件中。

    static jclass DexFile_defineClassNative(JNIEnv* env,jclass,jstring javaName,jobject javaLoader,jobject cookie,jobject dexFile) {// 調用for (auto& dex_file : dex_files) {ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),descriptor.c_str(),hash,class_loader,*dex_file,*dex_class_def);} } 復制代碼

    而在 defineClassNative 中,又是調用 ClassLinker#DefineClass 去加載類的。
    所以我們可以說,ClassLinker#DefineClass 就是 ART 中類加載的入口。
    入口已經出現,我們就進去探索一番,看看類加載的時候,是如何創建 Class 實例的~

    DefineClass 本身代碼比較多,我們這里把代碼簡化一下,看其主要流程。

    mirror::Class* ClassLinker::DefineClass(Thread* self,const char* descriptor,size_t hash,Handle<mirror::ClassLoader> class_loader,const DexFile& dex_file,const DexFile::ClassDef& dex_class_def) {auto klass = hs.NewHandle<mirror::Class>(nullptr);// 一些常用的,并且類大小可以確定的,會提前構造好對應的 Class,所以這里直接使用if (UNLIKELY(!init_done_)) {// finish up init of hand crafted class_roots_if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {klass.Assign(GetClassRoot(kJavaLangObject));} else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {klass.Assign(GetClassRoot(kJavaLangClass));} else if (strcmp(descriptor, "Ljava/lang/String;") == 0) {klass.Assign(GetClassRoot(kJavaLangString));} else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) {klass.Assign(GetClassRoot(kJavaLangRefReference));} else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) {klass.Assign(GetClassRoot(kJavaLangDexCache));} else if (strcmp(descriptor, "Ldalvik/system/ClassExt;") == 0) {klass.Assign(GetClassRoot(kDalvikSystemClassExt));}}if (klass == nullptr) {// 創建其他類實例klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));}// 設置對應的 DEX 緩存klass->SetDexCache(dex_cache);// 設置 Class 的一些屬性,包括 ClassLoader,訪問修飾符,Class 在 DEX 中對應的 index 等等SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get());// 把 Class 插入 ClassLoader 的 class_table 中做一個緩存ObjPtr<mirror::Class> existing = InsertClass(descriptor, klass.Get(), hash);// 加載類屬性LoadClass(self, *new_dex_file, *new_class_def, klass);// 加載父類if (!LoadSuperAndInterfaces(klass, *new_dex_file)) {// 加載失敗的處理}if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {// 連接失敗的處理}// ...return h_new_class.Get(); } 復制代碼

    從上面 DefineClass 的代碼里我們可以看到,加載分為幾個步驟:

  • 創建類實例
  • 設置 Class 訪問修飾符,ClassLoader 等一些屬性
  • 加載類成員 LoadClass
  • 加載父類和接口 LoadSuperAndInterfaces
  • 連接 LinkClass
  • 下面我們主要看下后面加載類成員,加載父類,連接這三個步驟。

    3.3 加載類成員 -- LoadClass

    加載類成員這一過程,主要有下面幾個步驟:

  • 加載靜態變量
  • 加載實例變量
  • 加載方法,分為虛方法和非虛方法 由于這里代碼比較長,我們分段來看。
  • 3.3.1 加載靜態變量
    // class_linker.cc void ClassLinker::LoadClassMembers(Thread* self,const DexFile& dex_file,const uint8_t* class_data,Handle<mirror::Class> klass) {{// Load static fields.// 獲取 DEX 文件中的變量迭代器ClassDataItemIterator it(dex_file, class_data);LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self,allocator,it.NumStaticFields());// ...// 遍歷靜態變量for (; it.HasNextStaticField(); it.Next()) {// ...LoadField(it, klass, &sfields->At(num_sfields));}// ...klass->SetSFieldsPtr(sfields);} }// 加載變量,設置變量 Class 以及訪問修飾符 void ClassLinker::LoadField(const ClassDataItemIterator& it,Handle<mirror::Class> klass,ArtField* dst) {const uint32_t field_idx = it.GetMemberIndex();dst->SetDexFieldIndex(field_idx);dst->SetDeclaringClass(klass.Get());dst->SetAccessFlags(it.GetFieldAccessFlags()); } 復制代碼

    加載靜態變量時,取出 DEX 文件中對應的 Class 數據,遍歷其中的靜態變量,設置給 Class#sfield_ 變量。

    3.3.2 加載實例變量

    加載實例變量和加載靜態變量是類似的,這里不做過多的解讀了。

    void ClassLinker::LoadClassMembers(Thread* self,const DexFile& dex_file,const uint8_t* class_data,Handle<mirror::Class> klass) {{// Load instance fields.LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self,allocator,it.NumInstanceFields());for (; it.HasNextInstanceField(); it.Next()) {LoadField(it, klass, &ifields->At(num_ifields));}// ...klass->SetIFieldsPtr(ifields);} } 復制代碼
    3.3.3 加載方法
    void ClassLinker::LoadClassMembers(Thread* self,const DexFile& dex_file,const uint8_t* class_data,Handle<mirror::Class> klass) {{for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);LoadMethod(dex_file, it, klass, method);LinkCode(this, method, oat_class_ptr, class_def_method_index);// ...}for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);LoadMethod(dex_file, it, klass, method);LinkCode(this, method, oat_class_ptr, class_def_method_index);// ...}} } 復制代碼

    加載方法時分為兩個步驟,LoadMethod 和 LinkCode。

    void ClassLinker::LoadMethod(const DexFile& dex_file,const ClassDataItemIterator& it,Handle<mirror::Class> klass,ArtMethod* dst) {// ...dst->SetDexMethodIndex(dex_method_idx);dst->SetDeclaringClass(klass.Get());dst->SetCodeItemOffset(it.GetMethodCodeItemOffset());dst->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods(), image_pointer_size_);uint32_t access_flags = it.GetMethodAccessFlags();// ...dst->SetAccessFlags(access_flags); } 復制代碼

    LoadMethod 主要是給 ArtMethod 設置訪問修飾符等屬性。

    LinkCode 這一步驟,可以理解為是給 ArtMethod 設置方法入口,即從其他方法如何跳轉到此方法進行執行。這里也分為了幾種情況:

  • 如果此方法已經通過 OAT 編譯成了本地機器指令,那么這里會將入口設置為跳轉到本地機器指令執行
  • 如果是靜態方法,設置跳板方法,此時不會具體指定方法如何執行,后面會在 ClassLinker::InitializeClass 里被 ClassLinker::FixupStaticTrampolines 替換掉
  • 如果是 Native 方法,入口設置為跳轉到 JNI 動態連接的方法中
  • 如果是解釋模式,入口設置為跳轉到解釋器中
  • static void LinkCode(ClassLinker* class_linker,ArtMethod* method,const OatFile::OatClass* oat_class,uint32_t class_def_method_index) REQUIRES_SHARED(Locks::mutator_lock_) {if (oat_class != nullptr) {// 判斷方法是否已經被 OAT const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index);oat_method.LinkMethod(method);}// Install entry point from interpreter.const void* quick_code = method->GetEntryPointFromQuickCompiledCode();bool enter_interpreter = class_linker->ShouldUseInterpreterEntrypoint(method, quick_code);if (method->IsStatic() && !method->IsConstructor()) {// 對于靜態方法,后面會在 ClassLinker::InitializeClass 里被 ClassLinker::FixupStaticTrampolines 替換掉method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub());} else if (quick_code == nullptr && method->IsNative()) {// Native 方法跳轉到 JNImethod->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub());} else if (enter_interpreter) {// 解釋模式,跳轉到解釋器method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());}// ... } 復制代碼

    這就是解析方法的主要過程,關于方法的調用,其實還比較復雜,如果大家感興趣,后面可以再專門說說。

    3.4 加載父類和接口 -- LoadSuperAndInterfaces

    自身類成員加載完成后,就去加載父類。加載父類調用的是 LoadSuperAndInterfaces,主要代碼如下:

    bool ClassLinker::LoadSuperAndInterfaces(Handle<mirror::Class> klass, const DexFile& dex_file) {// 加載父類ObjPtr<mirror::Class> super_class = ResolveType(dex_file, super_class_idx, klass.Get());// 檢查父類可見性if (!klass->CanAccess(super_class)) {// ...}// 設置父類klass->SetSuperClass(super_class);// 加載接口const DexFile::TypeList* interfaces = dex_file.GetInterfacesList(class_def);for (size_t i = 0; i < interfaces->Size(); i++) {ObjPtr<mirror::Class> interface = ResolveType(dex_file, idx, klass.Get());// ...// 檢查接口可見性if (!klass->CanAccess(interface)) {}}// 此時說明類已經加載完畢了mirror::Class::SetStatus(klass, mirror::Class::kStatusLoaded, nullptr); } 復制代碼

    加載父類和接口都是通過 ResolveType 來的,ResolveType 中又是調用了 ClassLinker#FindClass -> ClassLinker#DefineClass 來的,于是加載父類的流程又回到了我們本小結開頭。
    就這樣遞歸加載下去,直到父類全部加載完成,也就標識著類自身也加載完成了。

    3.5 連接 -- LinkClass

    之后就是 LinkClass,這里步驟比較清晰,我們先看一下主要代碼:

    bool ClassLinker::LinkClass(Thread* self,const char* descriptor,Handle<mirror::Class> klass,Handle<mirror::ObjectArray<mirror::Class>> interfaces,MutableHandle<mirror::Class>* h_new_class_out) {if (!LinkSuperClass(klass)) {return false;}// ...if (!LinkMethods(self, klass, interfaces, &new_conflict, imt_data)) {return false;}if (!LinkInstanceFields(self, klass)) {return false;}size_t class_size;if (!LinkStaticFields(self, klass, &class_size)) {return false;}// ...return true; } 復制代碼

    從主要代碼中可以看到,主要有四個步驟:

  • LinkSuperClass
  • LinkMethods
  • LinkInstanceFields
  • LinkStaticFields
  • 3.5.1 LinkSuperClass

    這里主要是對父類權限做了一下檢查,包括是否是 final,是否對子類可見(父類為 public 或者同包名),以及繼承父類一些屬性(包括是否有 finalize?方法,ClassFlags 等等)。

    bool ClassLinker::LinkSuperClass(Handle<mirror::Class> klass) {ObjPtr<mirror::Class> super = klass->GetSuperClass();// // Verifyif (super->IsFinal() || super->IsInterface()) {}if (!klass->CanAccess(super)) {}if (super->IsFinalizable()) {klass->SetFinalizable();}if (super->IsClassLoaderClass()) {klass->SetClassLoaderClass();}uint32_t reference_flags = (super->GetClassFlags() & mirror::kClassFlagReference);klass->SetClassFlags(klass->GetClassFlags() | reference_flags);return true; } 復制代碼
    3.5.2 LinkMethods

    LinkMethods 主要做的事情是填充 vtable 和 itable。主要通過 SetupInterfaceLookupTable,LinkVirtualMethods,LinkInterfaceMethods 三個方法來進行的:

    bool ClassLinker::LinkMethods(Thread* self,Handle<mirror::Class> klass,Handle<mirror::ObjectArray<mirror::Class>> interfaces,bool* out_new_conflict,ArtMethod** out_imt) {// ...return SetupInterfaceLookupTable(self, klass, interfaces)&& LinkVirtualMethods(self, klass, /*out*/ &default_translations)&& LinkInterfaceMethods(self, klass, default_translations, out_new_conflict, out_imt); } 復制代碼

    SetupInterfaceLookupTable 用來填充 iftable_,就是上面說到保存接口的地方。iftable_ 對應的是 IfTable 類。IfTable 類結構如下:

    class MANAGED IfTable FINAL : public ObjectArray<Object> {enum {// Points to the interface class.kInterface = 0,// Method pointers into the vtable, allow fast map from interface method index to concrete// instance method.kMethodArray = 1,kMax = 2,}; } 復制代碼

    其中 kInterface 指向 Interface 的 Class 對象,kMethodArray 指向的是 vtable,通過此變量可以方便的找到接口方法的實現。

    LinkVirtualMethods 和 LinkInterfaceMethods 會填充 vtable_,這里具體的代碼很長,我們暫且不分析(這里具體流程對于理解本文主旨其實影響不大),有兩個重要的過程是:

  • 首先會拷貝父類的 vtable 到當前類的 vtable
  • 如果類中覆蓋了父類的抽象方法,就在 vtable 中替換掉父類的方法
  • 通過上面兩個過程,我們可以知道,vtable 中保存的就是真正方法的實現,也就是 Java 中多態的實現原理。

    3.5.3 LinkInstanceFields & LinkStaticFields

    這里的兩個方法最終都調用了 LinkFields?方法里做了兩件事情:

  • 為了對齊內存,對 fields 進行排序
  • 計算 Class 大小
  • 其中 fields 排序規則如下:
    引用類型 -> long (64-bit) -> double (64-bit) -> int (32-bit) -> float (32-bit) -> char (16-bit) -> short (16-bit) -> boolean (8-bit) -> byte (8-bit)

    總結

    通過上面的分析,我們知道了一個 Java 類在 Android ART 中的真實形態,也對 ART 中類加載的過程做了一些簡單的分析。
    其實在寫這篇文章的時候,里面有一些知識點也會有些疑問,如果大家有任何想法,歡迎討論~
    最后用文章開始的圖總結一下,回顧一下 ART 中類的全貌。

    參考資料

    www.zhihu.com/question/48…
    blog.csdn.net/guoguodaern…

    關于我

    ZYLAB 專注高質量原創,把代碼寫成詩

    歡迎關注下面賬號,獲取更新:
    微信搜索公眾號: ZYLAB
    Github
    知乎
    掘金

    總結

    以上是生活随笔為你收集整理的脱了马甲我也认识你: 聊聊 Android 中类的真实形态的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 久久精品人人爽 | 午夜免费网 | 天天操天天操天天干 | 国产亚洲综合精品 | 毛片网站免费观看 | 四虎首页 | 日韩八区 | 免费久草视频 | 免费看黄色三级三级 | 胸网站 | 久久精品人人爽 | 久久精品人人爽 | 久久97| 日本黄网免费 | 伊人老司机| 真人一及毛片 | 成人特级片 | 亚洲mv一区 | 97久久国产亚洲精品超碰热 | 五月天黄色网址 | 亚洲精品中字 | 91精品国产色综合久久不卡电影 | 成人av中文字幕 | 久久久久亚洲色欲AV无码网站 | 二色av | 大陆极品少妇内射aaaaa | 外国av在线 | 精品久久一区 | 亚洲国产精品久久久久婷蜜芽 | 麻豆偷拍| 成人3d动漫一区二区三区 | 黑白配av | 亚洲破处视频 | 一本到免费视频 | 国产啊啊啊啊 | 黄色av一级片 | 国产99页 | 亚洲国产精品毛片av不卡在线 | 日韩欧美一区二区在线观看 | av在线短片| 欧美日韩在线免费播放 | av网站在线免费观看 | 91丨九色丨丰满人妖 | 色播av| 午夜毛片电影 | 久久亚洲熟女cc98cm | 熟妇高潮一区二区三区 | 毛片随便看 | 国产在线一区二区视频 | 日韩精品一区二区三区丰满 | 亚洲欧美日韩在线一区 | 最近中文字幕在线中文视频 | 超碰最新上传 | 婷婷在线影院 | 色网在线视频 | 91啦丨九色丨刺激 | 99久久婷婷国产综合精品草原 | 爱爱动态图 | av操操操| 久久久久久久一区 | 又黄又爽的视频在线观看 | 欧美比基尼 | 自拍99页 | 欧美乱妇在线观看 | 91综合视频 | 中文字幕亚洲国产 | 欧美大片在线看免费观看 | 欧美日韩一区二区视频在线观看 | 亚洲午夜天堂 | 国产91在线高潮白浆在线观看 | 美女又大又黄 | 成人午夜激情 | www.天天操.com | 久久久香蕉 | 91干干干 | 无码熟妇αⅴ人妻又粗又大 | 日本黄色片免费 | 超碰97最新| 亚洲系列在线 | 肉色超薄丝袜脚交一区二区 | 日韩国产在线观看 | 无码精品人妻一区二区 | 中文有码视频 | 新亚洲天堂 | 成人久久视频 | ass亚洲熟妇毛耸耸pics | 最新的黄色网址 | 日日操天天操夜夜操 | 啪啪av | 免费无遮挡在线观看视频网站 | 久久综合鬼色 | 在线精品一区二区三区 | 亚洲欧洲精品一区二区 | 67194av| 777av| 天天综合天天 | 成人国产网站 | 91精品日韩 | 国产日韩在线看 |