亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > PHP > 正文

變量在 PHP7 內部的實現(二)

2024-05-04 23:41:13
字體:
來源:轉載
供稿:網友
在上篇文章給大家介紹了變量在 PHP7 內部的實現(一),本篇繼續給大家介紹php7內部實現相關知識,感興趣的朋友通過本篇文章一起學習吧
 

在上篇文章給大家介紹了變量在 PHP7 內部的實現(一),本篇繼續給大家介紹php7內部實現相關知識,感興趣的朋友通過本篇文章一起學習吧。

本文第一部分和第二均翻譯自Nikita Popov(nikic,PHP 官方開發組成員,柏林科技大學的學生) 的 博客 。為了更符合漢語的閱讀習慣,文中并不會逐字逐句的翻譯。

要理解本文,你應該對 PHP5 中變量的實現有了一些了解,本文重點在于解釋 PHP7 中 zval 的變化。

第一部分講了 PHP5 和 PHP7 中關于變量最基礎的實現和變化。這里再重復一下,主要的變化就是 zval 不再單獨分配內存,不自己存儲引用計數。整型浮點型等簡單類型直接存儲在 zval 中。復雜類型則通過指針指向一個獨立的結構體。

復雜的 zval 數據值有一個共同的頭,其結構由 zend_refcounted 定義:

struct _zend_refcounted { uint32_t refcount; union {  struct {   ZEND_ENDIAN_LOHI_3(    zend_uchar type,    zend_uchar flags,    uint16_t  gc_info)  } v;  uint32_t type_info; } u;};

這個頭存儲有 refcount (引用計數),值的類型 type 和循環回收的相關信息 gc_info 以及類型標志位 flags 。

接下來會對每種復雜類型的實現單獨進行分析并和 PHP5 的實現進行比較。引用雖然也屬于復雜類型,但是上一部分已經介紹過了,這里就不再贅述。另外這里也不會講到資源類型(因為作者覺得資源類型沒什么好講的)。

字符串

PHP7 中定義了一個新的結構體 zend_string 用于存儲字符串變量:

struct _zend_string { zend_refcounted gc; zend_ulong  h;  /* hash value */ size_t   len; char    val[1];};

除了引用計數的頭以外,字符串還包含哈希緩存 h ,字符串長度 len 以及字符串的值 val 。哈希緩存的存在是為了防止使用字符串做為 hashtable 的 key 在查找時需要重復計算其哈希值,所以這個在使用之前就對其進行初始化。

如果你對 C 語言了解的不是很深入的話,可能會覺得 val 的定義有些奇怪:這個聲明只有一個元素,但是顯然我們想存儲的字符串償付肯定大于一個字符的長度。這里其實使用的是結構體的一個『黑』方法:在聲明數組時只定義一個元素,但是實際創建 zend_string 時再分配足夠的內存來存儲整個字符串。這樣我們還是可以通過 val 訪問完整的字符串。

當然這屬于非常規的實現手段,因為我們實際的讀和寫的內容都超過了單字符數組的邊界。但是 C 語言編譯器卻不知道你是這么做的。雖然 C99 也曾明確規定過支持『柔性數組』,但是感謝我們的好朋友微軟,沒人能在不同的平臺上保證 C99 的一致性(所以這種手段是為了解決 Windows 平臺下柔性數組的支持問題)。

新的字符串類型的結構比原生的 C 字符串更方便使用:第一是因為直接存儲了字符串的長度,這樣就不用每次使用時都去計算。第二是字符串也有引用計數的頭,這樣也就可以在不同的地方共享字符串本身而無需使用 zval。一個經常使用的地方就是共享 hashtable 的 key。

但是新的字符串類型也有一個很不好的地方:雖然可以很方便的從 zend_string 中取出 C 字符串(使用 str->val 即可),但反過來,如果將 C 字符串變成 zend_string 就需要先分配 zend_string 需要的內存,再將字符串復制到 zend_string 中。這在實際使用的過程中并不是很方便。

字符串也有一些特有的標志(存儲在 GC 的標志位中的):

#define IS_STR_PERSISTENT   (1<<0) /* allocated using malloc */#define IS_STR_INTERNED    (1<<1) /* interned string */#define IS_STR_PERMANENT   (1<<2) /* interned string surviving request boundary */

持久化的字符串需要的內存直接從系統本身分配而不是 zend 內存管理器(ZMM),這樣它就可以一直存在而不是只在單次請求中有效。給這種特殊的分配打上標記便于 zval 使用持久化字符串。在 PHP5 中并不是這樣處理的,是在使用前復制一份到 ZMM 中。

保留字符(interned strings)有點特殊,它會一直存在直到請求結束時才銷毀,所以也就無需進行引用計數。保留字符串也不可重復(duplicate),所以在創建新的保留字符時也會先檢查是否有同樣字符的已經存在。所有 PHP 源碼中不可變的字符串都是保留字符(包括字符串常量、變量名函數名等)。持久化字符串也是請求開始之前已經創建好的保留字符。但普通的保留字符在請求結束后會銷毀,持久化字符串卻始終存在。

如果使用了 opcache 的話保留字符會被存儲在共享內存(SHM)中這樣就可以在所有 PHP 進程質檢共享。這種情況下持久化字符串也就沒有存在的意義了,因為保留字符也是不會被銷毀的。

數組

因為 之前的文章 有講過新的數組實現,所以這里就不再詳細描述了。雖然最近有些變化導致之前的描述不是十分準確了,但是基本的概念還是一致的。

這里要說的是之前的文章中沒有提到的數組相關的概念:不可變數組。其本質上和保留字符類似:沒有引用計數且在請求結束之前一直存在(也可能在請求結束之后還存在)。

因為某些內存管理方便的原因,不可變數組只會在開啟 opcache 時會使用到。我們來看看實際使用的例子,先看以下的腳本:

<?phpfor ($i = 0; $i < 1000000; ++$i) { $array[] = ['foo'];}var_dump(memory_get_usage());

開啟 opcache 時,以上代碼會使用 32MB 的內存,不開啟的情況下因為 $array 每個元素都會復制一份 ['foo'] ,所以需要 390MB。這里會進行完整的復制而不是增加引用計數值的原因是防止 zend 虛擬機操作符執行的時候出現共享內存出錯的情況。我希望不使用 opcache 時內存暴增的問題以后能得到改善。

PHP5 中的對象

在了解 PHP7 中的對象實現直線我們先看一下 PHP5 的并且看一下有什么效率上的問題。PHP5 中的 zval 會存儲一個 zend_object_value 結構,其定義如下:

typedef struct _zend_object_value { zend_object_handle handle; const zend_object_handlers *handlers;} zend_object_value;

handle 是對象的唯一 ID,可以用于查找對象數據。 handles 是保存對象各種屬性方法的虛函數表指針。通常情況下 PHP 對象都有著同樣的 handler 表,但是 PHP 擴展創建的對象也可以通過操作符重載等方式對其行為自定義。

對象句柄(handler)是作為索引用于『對象存儲』,對象存儲本身是一個存儲容器(bucket)的數組,bucket 定義如下:

typedef struct _zend_object_store_bucket { zend_bool destructor_called; zend_bool valid; zend_uchar apply_count; union _store_bucket {  struct _store_object {   void *object;   zend_objects_store_dtor_t dtor;   zend_objects_free_object_storage_t free_storage;   zend_objects_store_clone_t clone;   const zend_object_handlers *handlers;   zend_uint refcount;   gc_root_buffer *buffered;  } obj;  struct {   int next;  } free_list; } bucket;} zend_object_store_bucket;

這個結構體包含了很多東西。前三個成員只是些普通的元數據(對象的析構函數是否被調用過、bucke 是否被使用過以及對象被遞歸調用過多少次)。接下來的聯合體用于區分 bucket 是處于使用中的狀態還是空閑狀態。上面的結構中最重要的是 struct _store_object 子結構體:

第一個成員 object 是指向實際對象(也就是對象最終存儲的位置)的指針。對象實際并不是直接嵌入到對象存儲的 bucket 中的,因為對象不是定長的。對象指針下面是三個用于管理對象銷毀、釋放與克隆的操作句柄(handler)。這里要注意的是 PHP 銷毀和釋放對象是不同的步驟,前者在某些情況下有可能會被跳過(不完全釋放)??寺〔僮鲗嶋H上幾乎幾乎不會被用到,因為這里包含的操作不是普通對象本身的一部分,所以(任何時候)他們在每個對象中他們都會被單獨復制(duplicate)一份而不是共享。

這些對象存儲操作句柄后面是一個普通的對象 handlers 指針。存儲這幾個數據是因為有時候可能會在 zval 未知的情況下銷毀對象(通常情況下這些操作都是針對 zval 進行的)。

bucket 也包含了 refcount 的字段,不過這種行為在 PHP5 中顯得有些奇怪,因為 zval 本身已經存儲了引用計數。為什么還需要一個多余的計數呢?問題在于雖然通常情況下 zval 的『復制』行為都是簡單的增加引用計數即可,但是偶爾也會有深度復制的情況出現,比如創建一個全新的 zval 但是保存同樣的 zend_object_value 。這種情況下兩個不同的 zval 就用到了同一個對象存儲的 bucket,所以 bucket 自身也需要進行引用計數。這種『雙重計數』的方式是 PHP5 的實現內在的問題。GC 根緩沖區中的 buffered 指針也是由于同樣的原因才需要進行完全復制(duplicate)。

現在看看對象存儲中指針指向的實際的 object 的結構,通常情況下用戶層面的對象定義如下:

typedef struct _zend_object { zend_class_entry *ce; HashTable *properties; zval **properties_table; HashTable *guards;} zend_object;

zend_class_entry 指針指向的是對象實現的類原型。接下來的兩個元素是使用不同的方式存儲對象屬性。動態屬性(運行時添加的而不是在類中定義的)全部存在 properties 中,不過只是屬性名和值的簡單匹配。

不過這里有針對已經聲明的屬性的一個優化:編譯期間每個屬性都會被指定一個索引并且屬性本身是存儲在 properties_table 的索引中。屬性名稱和索引的匹配存儲在類原型的 hashtable 中。這樣就可以防止每個對象使用的內存超過 hashtable 的上限,并且屬性的索引會在運行時有多處緩存。

guards 的哈希表是用于實現魔術方法的遞歸行為的,比如 __get ,這里我們不深入討論。

除了上文提到過的雙重計數的問題,這種實現還有一個問題是一個最小的只有一個屬性的對象也需要 136 個字節的內存(這還不算 zval 需要的內存)。而且中間存在很多間接訪問動作:比如要從對象 zval 中取出一個元素,先需要取出對象存儲 bucket,然后是 zend object ,然后才能通過指針找到對象屬性表和 zval。這樣這里至少就有 4 層間接訪問(并且實際使用中可能最少需要七層)。

PHP7 中的對象

PHP7 的實現中試圖解決上面這些問題,包括去掉雙重引用計數、減少內存使用以及間接訪問。新的 zend_object 結構體如下:

struct _zend_object { zend_refcounted gc; uint32_t   handle; zend_class_entry *ce; const zend_object_handlers *handlers; HashTable  *properties; zval    properties_table[1];};

可以看到現在這個結構體幾乎就是一個對象的全部內容了: zend_object_value 已經被替換成一個直接指向對象和對象存儲的指針,雖然沒有完全移除,但已經是很大的提升了。

除了 PHP7 中慣用的 zend_refcounted 頭以外, handle 和 對象的 handlers 現在也被放到了 zend_object 中。這里的 properties_table 同樣用到了 C 結構體的小技巧,這樣 zend_object 和屬性表就會得到一整塊內存。當然,現在屬性表是直接嵌入到 zval 中的而不是指針。

現在對象結構體中沒有了 guards 表,現在如果需要的話這個字段的值會被存儲在 properties_table 的第一位中,也就是使用 __get 等方法的時候。不過如果沒有使用魔術方法的話,guards 表會被省略。

dtor 、 free_storage 和   clone 三個操作句柄之前是存儲在對象操作 bucket 中,現在直接存在 handlers 表中,其結構體定義如下:

struct _zend_object_handlers { /* offset of real object header (usually zero) */ int          offset; /* general object functions */ zend_object_free_obj_t     free_obj; zend_object_dtor_obj_t     dtor_obj; zend_object_clone_obj_t     clone_obj; /* individual object functions */ // ... rest is about the same in PHP 5};

handler 表的第一個成員是 offset ,很顯然這不是一個操作句柄。這個 offset 是現在的實現中必須存在的,因為雖然內部的對象總是嵌入到標準的 zend_object 中,但是也總會有添加一些成員進去的需求。在 PHP5 中解決這個問題的方法是添加一些內容到標準的對象后面:

struct custom_object { zend_object std; uint32_t something; // ...};

這樣如果你可以輕易的將 zend_object* 添加到 struct custom_object* 中。這也是 C 語言中常用的結構體繼承的做法。但是在 PHP7 中這種實現會有一個問題:因為 zend_object 在存儲屬性表時用了結構體 hack 的技巧, zend_object 尾部存儲的 PHP 屬性會覆蓋掉后續添加進去的內部成員。所以 PHP7 的實現中會把自己添加的成員添加到標準對象結構的前面:

struct custom_object { uint32_t something; // ... zend_object std;};

不過這樣也就意味著現在無法直接在 zend_object* 和 struct custom_object* 進行簡單的轉換了,因為兩者都一個偏移分割開了。所以這個偏移量就需要被存儲在對象 handler 表中的第一個元素中,這樣在編譯時通過 offsetof() 宏就能確定具體的偏移值。

也許你會好奇既然現在已經直接(在 zend_value 中)存儲了 zend_object 的指針,那現在就不需要再到對象存儲中去查找對象了,為什么 PHP7 的對象者還保留著 handle 字段呢?

這是因為現在對象存儲仍然存在,雖然得到了極大的簡化,所以保留 handle 仍然是有必要的?,F在它只是一個指向對象的指針數組。當對象被創建時,會有一個指針插入到對象存儲中并且其索引會保存在 handle 中,當對象被釋放時,索引也會被移除。

那么為什么現在還需要對象存儲呢?因為在請求結束的階段會在存在某個節點,在這之后再去執行用戶代碼并且取指針數據時就不安全了。為了避免這種情況出現 PHP 會在更早的節點上執行所有對象的析構函數并且之后就不再有此類操作,所以就需要一個活躍對象的列表。

并且 handle 對于調試也是很有用的,它讓每個對象都有了一個唯一的 ID,這樣就很容易區分兩個對象是同一個還是只是有相同的內容。雖然 HHVM 沒有對象存儲的概念,但它也存了對象的 handle。

和 PHP5 相比,現在的實現中只有一個引用計數(zval 自身不計數),并且內存的使用量有了很大的縮減:40 個字節用于基礎對象,每個屬性需要 16 個字節,并且這還是算了 zval 之后的。間接訪問的情況也有了顯著的改善,因為現在中間層的結構體要么被去掉了,要么就是直接嵌入的,所以現在讀取一個屬性只有一層訪問而不再是四層。

間接 zval

到現在我們已經基本提到過了所有正常的 zval 類型,但是也有一對特殊類型用于某些特定的情況的,其中之一就是 PHP7 新添加的 IS_INDIRECT 。

間接 zval 指的就是其真正的值是存儲在其他地方的。注意這個 IS_REFERENCE 類型是不同的,間接 zval 是直接指向另外一個 zval 而不是像 zend_reference 結構體一樣嵌入 zval。

為了理解在什么時候會出現這種情況,我們來看一下 PHP 中變量的實現(實際上對象屬性的存儲也是一樣的情況)。

所有在編譯過程中已知的變量都會被指定一個索引并且其值會被存在編譯變量(CV)表的相應位置中。但是 PHP 也允許你動態的引用變量,不管是局部變量還是全局變量(比如 $GLOBALS ),只要出現這種情況,PHP 就會為腳本或者函數創建一個符號表,這其中包含了變量名和它們的值之間的映射關系。

但是問題在于:怎么樣才能實現兩個表的同時訪問呢?我們需要在 CV 表中能夠訪問普通變量,也需要能在符號表中訪問編譯變量。在 PHP5 中 CV 表用了雙重指針 zval** ,通常這些指針指向中間的 zval* 的表, zval* 最終指向的才是實際的 zval:

+------ CV_ptr_ptr[0]| +---- CV_ptr_ptr[1]| | +-- CV_ptr_ptr[2]| | || | +-> CV_ptr[0] --> some zval| +---> CV_ptr[1] --> some zval+-----> CV_ptr[2] --> some zval

當需要使用符號表時存儲 zval* 的中間表其實是沒有用到的而 zval** 指針會被更新到 hashtable buckets 的響應位置中。我們假定有 $a 、 $b 和 $c 三個變量,下面是簡單的示意圖:

CV_ptr_ptr[0] --> SymbolTable["a"].pDataPtr --> some zvalCV_ptr_ptr[1] --> SymbolTable["b"].pDataPtr --> some zvalCV_ptr_ptr[2] --> SymbolTable["c"].pDataPtr --> some zval

但是 PHP7 的用法中已經沒有這個問題了,因為 PHP7 中的 hashtable 大小發生變化時 hashtable bucket 就失效了。所以 PHP7 用了一個相反的策略:為了訪問 CV 表中存儲的變量,符號表中存儲 INDIRECT 來指向 CV 表。CV 表在符號表的生命周期內不會重新分配,所以也就不會存在有無效指針的問題了。

所以加入你有一個函數并且在 CV 表中有 $a 、 $b 和 $c ,同時還有一個動態分配的變量 $d ,符號表的結構看起來大概就是這個樣子:

SymbolTable["a"].value = INDIRECT --> CV[0] = LONG 42SymbolTable["b"].value = INDIRECT --> CV[1] = DOUBLE 42.0SymbolTable["c"].value = INDIRECT --> CV[2] = STRING --> zend_string("42")SymbolTable["d"].value = ARRAY --> zend_array([4, 2])

間接 zval 也可以是一個指向 IS_UNDEF 類型 zval 的指針,當 hashtable 沒有和它關聯的 key 時就會出現這種情況。所以當使用 unset($a) 將 CV[0] 的類型標記為 UNDEF 時就會判定符號表不存在鍵值為 a 的數據。

常量和 AST

還有兩個需要說一下的在 PHP5 和 PHP7 中都存在的特殊類型 IS_CONSTANT 和 IS_CONSTANT_AST 。要了解他們我們還是先看以下的例子:

<?phpfunction test($a = ANSWER,    $b = ANSWER * ANSWER) { return $a + $b;}define('ANSWER', 42);var_dump(test()); // int(42 + 42 * 42)·

test() 函數的兩個參數的默認值都是由常量 ANSWER 構成,但是函數聲明時常量的值尚未定義。常量的具體值只有通過 define() 定義時才知道。

由于以上問題的存在,參數和屬性的默認值、常量以及其他接受『靜態表達式』的東西都支持『延時綁定』直到首次使用時。

常量(或者類的靜態屬性)這些需要『延時綁定』的數據就是最常需要用到 IS_CONSTANT 類型 zval 的地方。如果這個值是表達式,就會使用 IS_CONSTANT_AST 類型的 zval 指向表達式的抽象語法樹(AST)。

到這里我們就結束了對 PHP7 中變量實現的分析。后面我可能還會寫兩篇文章來介紹一些虛擬機優化、新的命名約定以及一些編譯器基礎結構的優化的內容(這是作者原話)。



注:相關教程知識閱讀請移步到PHP教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲大胆人体在线| 亚洲精品久久视频| 欧美激情综合色综合啪啪五月| 亚洲色图17p| 久色乳综合思思在线视频| 国产精品成人免费视频| 亚洲国产另类久久精品| 26uuu亚洲国产精品| 亚洲精品短视频| 欧美在线观看一区二区三区| 91久久精品美女高潮| 日韩av观看网址| 668精品在线视频| 日本三级久久久| 国产精品免费一区二区三区都可以| 欧美超级免费视 在线| 国产成人精品综合久久久| 中文字幕一区电影| 欧美疯狂xxxx大交乱88av| 亚洲国产福利在线| 久久久久久久久久久网站| 亚洲无限乱码一二三四麻| 国产噜噜噜噜久久久久久久久| 中日韩美女免费视频网址在线观看| 色琪琪综合男人的天堂aⅴ视频| 日本免费在线精品| 日韩视频免费在线观看| 国产一区二区动漫| 国精产品一区一区三区有限在线| 日韩av毛片网| 久久免费精品日本久久中文字幕| 精品性高朝久久久久久久| 国产精品99免视看9| 久久韩剧网电视剧| 亚洲成人在线网| 亚洲一区二区久久久久久久| 久久亚洲精品网站| 日韩亚洲欧美成人| 色视频www在线播放国产成人| 91免费综合在线| 亚洲天堂影视av| 日韩精品极品在线观看| 国产精品久久国产精品99gif| 中文字幕日韩欧美精品在线观看| 国产99视频精品免视看7| 国产不卡在线观看| 国产不卡视频在线| 奇米4444一区二区三区| 久久6免费高清热精品| 国产精品自产拍在线观| 国色天香2019中文字幕在线观看| 日韩久久精品电影| 姬川优奈aav一区二区| 国产v综合v亚洲欧美久久| 日韩欧美国产一区二区| 欧美一级黑人aaaaaaa做受| 成人在线国产精品| 欧美成人自拍视频| 亚洲午夜小视频| 色妞在线综合亚洲欧美| 成人女保姆的销魂服务| 国产女同一区二区| 欧美影院久久久| 精品日韩美女的视频高清| 日韩欧美高清视频| 成人h视频在线| 日韩精品在线看| 亚洲**2019国产| 日韩欧美999| 91精品视频专区| 国产精品大片wwwwww| 久久久999国产| 欧美自拍视频在线| 欧美肥老太性生活视频| 国产成人免费av| 久久久av网站| 日韩av大片在线| 成人国产亚洲精品a区天堂华泰| 日韩成人高清在线| 欧美极品美女视频网站在线观看免费| 亚洲精品国产品国语在线| 亚洲永久免费观看| 日韩av不卡在线| 成人亚洲欧美一区二区三区| 成人网欧美在线视频| 亚洲娇小xxxx欧美娇小| 国产精品国产三级国产aⅴ9色| 亚洲精品中文字幕女同| 日韩中文在线中文网在线观看| 亚洲成人精品在线| 91日本在线视频| 日韩中文av在线| 久久精品国产免费观看| 欧美日韩国产一区二区| 国产欧美精品日韩精品| 91探花福利精品国产自产在线| 欧美一级黄色网| 欧美成人久久久| 18性欧美xxxⅹ性满足| 久久国产精品久久久| 日韩欧美中文字幕在线观看| 国产视频久久久久久久| 精品在线小视频| 欧美激情第三页| 成人激情视频在线| 亚洲美女精品久久| 久久国产精品视频| 久久久亚洲天堂| 亚洲欧洲在线观看| 亚洲综合色激情五月| 国产一区二区黑人欧美xxxx| 中文字幕成人精品久久不卡| 午夜精品国产精品大乳美女| 久久久久女教师免费一区| 久久大大胆人体| 国产精品户外野外| 久久精品成人一区二区三区| 久久免费成人精品视频| 18性欧美xxxⅹ性满足| 亚洲男人的天堂在线| 成人精品网站在线观看| 这里只有精品久久| 日韩av三级在线观看| 中文字幕日韩在线观看| 日韩在线观看免费av| 亚洲成人精品久久久| 国产视频精品一区二区三区| …久久精品99久久香蕉国产| 久久精品国产精品亚洲| 久久成人国产精品| 日韩极品精品视频免费观看| 欧美福利视频在线| 91久久国产精品91久久性色| 国产精品视频区1| 国产午夜精品美女视频明星a级| 亚洲欧美一区二区激情| 亚洲丁香婷深爱综合| 亚洲精品suv精品一区二区| 91精品国产91久久| 欧美黑人巨大xxx极品| 欧美日韩免费在线| 琪琪第一精品导航| 另类色图亚洲色图| 国产精品老女人视频| 欧美极品美女电影一区| 国产999精品视频| 蜜臀久久99精品久久久无需会员| 国产精品免费小视频| 亚州国产精品久久久| 欧洲日韩成人av| 成人性生交大片免费观看嘿嘿视频| 在线看日韩欧美| 亚洲韩国日本中文字幕| 久久久www成人免费精品| 亚洲男人天堂2019| 欧美美女18p| 久久久久久一区二区三区| 992tv成人免费影院| 日韩av一区在线观看| 45www国产精品网站| 精品中文字幕久久久久久| 中文字幕亚洲自拍| 午夜精品久久久久久99热| 久久艳片www.17c.com|