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

首頁 > 編程 > PHP > 正文

PHP內核原理(二)內存管理

2019-11-08 00:57:50
字體:
來源:轉載
供稿:網友

轉載請注明出處http://blog.csdn.net/fanhengguang_php

PHP內核原理 Zvals內存管理

zval結構有兩個功能:第一,用于存儲一個變量的值以及變量類型。第二,有效的管理內存中的zval變量的值,本章將會介紹這個功能。

接下來我們看一下引用計數和copy-on-write 這兩個概念,以及在擴展中如何應用。

值和引用

在php中所有的變量都是值傳遞,除非你顯示的指明引用傳遞。即任何時刻你傳遞一個變量給個函數或者給另外一個變量賦值,你得到的兩個變量都會擁有一份獨立的值的拷貝??匆韵吕?/p><?php$a = 1;$b = $a;$a++;// Only $a was incremented, $b stays as is:var_dump($a, $b); // int(2), int(1)function inc($n) { $n++;}$c = 1;inc($c);// The $c value outside the function and the $n inside the function are distinctvar_dump($c); // int(1)

雖然上面例子有些簡單,但需要意識到這是php種一個基本的規則, 特別的這個規則頁適用于對象。

<?php$obj = (object) ['value' => 1];function fnByVal($val) { $val = 100;}function fnByRef(&$ref) { $ref = 100;}// The by-value function does not modify $obj, the by-reference function does:fnByVal($obj);var_dump($obj); // stdClass(value => 1)fnByRef($obj);var_dump($obj); // int(100)

人們經常說自從php5對象對象是自動的按引用傳遞的, 但是通過以上例子看出這是錯的:按值傳遞的函數不能修改傳遞給他的變量,而按引用傳遞的函數可以。

看一下例子:

<?phpclass myclass { public $PRop;}function myfun($obj) { $obj->prop = 'world';}$obj = new myclass();$obj->prop = 'hello';var_dump($obj);myfun($obj);var_dump($obj);打印出:object(myclass)#1 (1) { ["prop"]=> string(5) "hello"}object(myclass)#1 (1) { ["prop"]=> string(5) "world"}

對象確實表現出引用傳遞的行為:雖然你不能將其賦值為一個完全不同的值, 但是你可以在函數中修改對象的成員。這是由于對象的值僅僅是一個用來查找實際內容的ID, 引用傳遞可以阻止你將其ID改為一個不同的對象或者不同的類型,但是并不能阻止你修改對象的實際的值。

以上也適用于resource類型。因為它也是同樣僅僅存儲了用于查找實際值的ID,所以同樣的按引用傳遞可以阻止你修改其resource ID或者不同的類型,但是并不能阻止你resource的內容(如修改文件的指針位置)。

引用計數和寫時復制

稍加思考你會得到這樣的結論:php一定是做了可怕的大量拷貝。每次給函數傳遞變量,其值都會被拷貝一次,對于整形int或者double類型這可能沒啥問題,但是想像一下給函數傳遞一個擁有百萬元素的數組,每次調用都拷貝百萬的元素將是多么低效。

為了避免拷貝,php使用了寫時復制的方法:一個zval可以被多個變量、函數等共享,只要他們對變量是只讀的不會修改她。如果一個變量想要做修改,在修改之前需要將zval拷貝一份。

如果一個zval可以被共享,那么php需要一個方法判斷何時這個zval不被使用了,不再使用的zval將會被釋放掉。PHP通過簡單的跟蹤一個zval被引用次數來解決這個問題, 注意這里引用指的是一個zval被變量、函數等使用,而不是變量引用(如&方式引用變量)。引用個數存儲在zval結構的refcount__gc成員變量中。

為了理解引用計數原理,考慮下面的例子:

<?php$a = 1; // $a = zval_1(value=1, refcount=1)$b = $a; // $a = $b = zval_1(value=1, refcount=2)$c = $b; // $a = $b = $c = zval_1(value=1, refcount=3)$a++; // $b = $c = zval_1(value=1, refcount=2) // $a = zval_2(value=2, refcount=1)unset($b); // $c = zval_1(value=1, refcount=1) // $a = zval_2(value=2, refcount=1)unset($c); // zval_1 is destroyed, because refcount=0 // $a = zval_2(value=2, refcount=1)

當一個增加引用時refcount加1, 如果一個引用被刪除refcount減1, 如果refcount變為0, 這個zval將會被銷毀。

當出現循環引用時,這個方法就不適用了:

<?php$a = []; // $a = zval_1(value=[], refcount=1)$b = []; // $b = zval_2(value=[], refcount=1)$a[0] = $b; // $a = zval_1(value=[0 => zval_2], refcount=1) // $b = zval_2(value=[], refcount=2) // The refcount of zval_2 is incremented because it // is used in the array of zval_1$b[0] = $a; // $a = zval_1(value=[0 => zval_2], refcount=2) // $b = zval_2(value=[0 => zval_1], refcount=2) // The refcount of zval_1 is incremented because it // is used in the array of zval_2unset($a); // zval_1(value=[0 => zval_2], refcount=1) // $b = zval_2(value=[0 => zval_1], refcount=2) // The refcount of zval_1 is decremented, but the zval has // to stay alive because it's still referenced by zval_2unset($b); // zval_1(value=[0 => zval_2], refcount=1) // zval_2(value=[0 => zval_1], refcount=1) // The refcount of zval_2 is decremented, but the zval has // to stay alive because it's still referenced by zval_1

上面的代碼執行完后,出現這樣一種情況兩個zvals沒有被任何變量引用,但是卻將一直存在不被銷毀,因為refcount不為0。 這是一個引用計數無效的典型的例子

為了解決這個問題php有第二種垃圾回收算法:循環垃圾回收器。我們現在可以暫時忽略他,因為循環回收對與擴展開發者來說是透明的,如果你想要了解這部分內容,可參考php手冊上的介紹:http://php.net/manual/en/features.gc.collecting-cycles.php

另一種需要考慮的情況是實際的php引用(例如&$val, 而不是上面提到的引用計數的引用). php使用一個is_ref標記來表示php引用。這個標記存儲在zval結構體中的is_ref__gc中。

is_ref=1標記表示這是一個引用,當變量修改時應該直接修改zval的值, 不要進行拷貝。

<?php$a = 1; // $a = zval_1(value=1, refcount=1, is_ref=0)$b =& $a; // $a = $b = zval_1(value=1, refcount=2, is_ref=1)$b++; // $a = $b = zval_1(value=2, refcount=2, is_ref=1) // Due to the is_ref=1 PHP directly changes the zval // rather than making a copy

上面的例子中在對其進行引用之前$a對應的zval的refcount為1, 現在考慮一個非常類似的例子,然而其refcount大于1

<?php$a = 1; // $a = zval_1(value=1, refcount=1, is_ref=0)$b = $a; // $a = $b = zval_1(value=1, refcount=2, is_ref=0)$c = $b // $a = $b = $c = zval_1(value=1, refcount=3, is_ref=0)$d =& $c; // $a = $b = zval_1(value=1, refcount=2, is_ref=0) // $c = $d = zval_2(value=1, refcount=2, is_ref=1) // $d is a reference of $c, but *not* of $a and $b, so // the zval needs to be copied here. Now we have the // same zval once with is_ref=0 and once with is_ref=1.$d++; // $a = $b = zval_1(value=1, refcount=2, is_ref=0) // $c = $d = zval_2(value=2, refcount=2, is_ref=1) // Because there are two separate zvals $d++ does // not modify $a and $b (as expected).

如你所見,當引用一個is_ref=0 and refcount>1的變量時需要先進行zval拷貝。 同樣的當使用一個is_ref=1 and refcount>1的zval進行值傳遞的時候需要進行拷貝。 所以使用引用有時會使程序變慢:幾乎所有的php函數都使用值傳遞方式,所以當傳遞一個is_ref=1zval給函數時會導致值拷貝。

zval創建和初始化

Now that you are familiar with the general concepts underlying zval memory management, we can move on to their practical implementation. Lets start with zval allocation:

現在你應該熟悉了zval內存管理的基本概念,接下來我們它們的實際應用。我們從zval的創建開始

zval *zv_ptr;ALLOC_ZVAL(zv_ptr);

這段代碼創建了一個zval,但是沒有初始化成員變量。有一個類似的宏辯題用來分配一個持久化的zval, 持久化zval在請求結束后不會被銷毀。

zval *zv_ptr;ALLOC_PERMANENT_ZVAL(zv_ptr);

The difference between the two macros is that the former makes use of emalloc() whereas the latter uses malloc(). It’s important to know though that trying to directly allocate zvals will not work:

上面這兩個宏的不同點是,前者使用emalloc() 二后者使用malloc()來分配內存,重要提示直接分配zvals是不行的:

/* This code is WRONG */zval *zv_ptr = emalloc(sizeof(zval));

The reason is that the cycle collector needs to store some additional information in the zval, so the structure that needs to be allocated is actually not a zval but a zval_gc_info:

原因是循環垃圾回收需要在zval中存儲一些額外的信息, 所以被創建的結構實際上是zval_gc_info二不是zval:

typedef struct _zval_gc_info { zval z; union { gc_root_buffer *buffered; struct _zval_gc_info *next; } u;} zval_gc_info;

Alloc*宏會創建一個zval_gc_info并初始化其額外的成員,但是在這之后這個值可以被透明的用作zval(因為這個結構中zval是其第一個元素).

在zval創建之后,有兩個宏可以對其進行初始化,第一個INIT_PZVAL, 他會設置refcount=1 and is_ref=0但是zval的值不會被初始化。

zval *zv_ptr;ALLOC_ZVAL(zv_ptr);INIT_PZVAL(zv_ptr);/* zv_ptr has garbage type+value here */

第二個宏INIT_ZVAL也會設置refcount=1 and is_ref=0, 但是除此之外會將zval的類型初始化為IS_NULL

zval *zv_ptr;ALLOC_ZVAL(zv_ptr);INIT_ZVAL(*zv_ptr);/* zv_ptr has type=IS_NULL here */

INIT_PZVAL()接受一個zval*參數,而INIT_ZVAL()接受一個zval參數,當傳遞zval*給第二個宏時需要先對其解引用。

由于常常需要創建并初始化一個zval, 有兩個宏可以完成創建并初始化合的工作。

zval *zv_ptr;MAKE_STD_ZVAL(zv_ptr);/* zv_ptr has garbage type+value here */zval *zv_ptr;ALLOC_INIT_ZVAL(zv_ptr);/* zv_ptr has type=IS_NULL here */

refcount管理和zval銷毀

一旦創建并初始化了一個zval, 你就可以使用之前介紹過的引用計數方法。php提供了幾個宏來管理refcount:

Z_REFCOUNT_P(zv_ptr) /* Get refcount */Z_ADDREF_P(zv_ptr) /* Increment refcount */Z_DELREF_P(zv_ptr) /* Decrement refcount */Z_SET_REFCOUNT(zv_ptr, 1) /* Set refcount to some particular value (here 1) */

想其他Z_類的宏一樣, 沒有后綴,一個P_P后綴,兩個P_PP的宏分別可以接受zval, zval*, zval** 的參數

最常用的宏是Z_ADDREF_P()。 一個例子:

zval *zv_ptr;MAKE_STD_ZVAL(zv_ptr);ZVAL_LONG(zv_ptr, 42);add_index_zval(some_array, 0, zv_ptr);add_assoc_zval(some_array, "num", zv_ptr);Z_ADDREF_P(zv_ptr);

以上代碼將42先插入數組到0號位置,然后用num這個key又插入了一次。所以這個zval被用在兩個位置上,在創建并初始化zval后, 他的refcount為1, 想要在兩個位置使用同一個zval需要將refcout設置為2, 所以必須用Z_ADDREF_P()對其加1.

The complement macro Z_DELREF_P() on the other hand is used rather rarely: Usually just decrementing the refcount is not enough, because you have to check for the refcount==0 case where the zval needs to be destroyed and freed:

另外一個宏Z_DELREF_P()相對來說用的很少,通常僅僅將refcount 減1 是不夠的,因為你需要檢查refcount是否等0,等0時需要銷毀釋放zval:

Z_DELREF_P(zv_ptr);if (Z_REFCOUNT_P(zv_ptr) == 0) { zval_dtor(zv_ptr); efree(zv_ptr);}

zval_dtor()宏接收一個zval*參數,它會將zval的value值釋放掉: 如果值是字符串,則釋放字符串,如果是一個數組那么對應的hashtable會被釋放掉,如果是一個對象或者資源resource類型, 對應的實際的值的refcount會被減1(如果refcount減少至0將會導致他們被銷毀和釋放).

上面的代碼中你必須自己檢查refcount的值, 你可以用另外一個宏zval_ptr_dtor():來替代上面的方式:

zval_ptr_dtor(&zv_ptr);

這個宏接收一個zval**(由于歷史原因,也可以傳遞一個zval*)。 這個宏會將refcount減1并且檢查這個zval是否需要被銷毀和釋放。不用于我們上面手動的方式,它還支持垃圾回收,下面是相關的實現:

static zend_always_inline void i_zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC TSRMLS_DC){ if (!Z_DELREF_P(zval_ptr)) { ZEND_ASSERT(zval_ptr != &EG(uninitialized_zval)); GC_REMOVE_ZVAL_FROM_BUFFER(zval_ptr); zval_dtor(zval_ptr); efree_rel(zval_ptr); } else { if (Z_REFCOUNT_P(zval_ptr) == 1) { Z_UNSET_ISREF_P(zval_ptr); } GC_ZVAL_CHECK_POSSIBLE_ROOT(zval_ptr); }}

Z_DELREF_P()先將refcount減1然后將新的refcount返回, 所以!Z_DELREF_P(zval_ptr)和先執行Z_DELREF_P(zval_ptr)然后在檢查 Z_REFCOUNT_P(zval_ptr) == 0是一樣的。

上面的函數中除了執行zval_dtor()efree()操作之外,還調用了GC_*宏斷言&EG(uninitialized_zval)不會被釋放(這是一個被引擎使用的神奇zval)。

另外zval只被一個變量引用的話,代碼中會將is_ref置為0。 因為&引用只有當兩個以上變量引用zval時才有意義,所以保留is_ref=1是沒有任何意義的。

提示:不要使用Z_DELREF_P()(除非你可以保證這個zval無需銷毀),當你想減少refcount時,你應當使用zval_ptr_dtor()代替。zval_dtor()宏可用于臨時的棧上創建的zvals:

zval zv;INIT_ZVAL(zv);/* Do something with zv here */zval_dtor(&zv);

一個臨時的棧上創建的zval是不能被共享的,當代碼塊退出后zval將會被銷毀和釋放。

拷貝zvals

雖然寫時復制避免了很多zval拷貝,但是有時拷貝無可避免,例如你想要修改zval的value 或者將zval存儲在其他地方。

針對不同使用場景,PHP提供了大量的宏用于拷貝。 最簡單的宏是ZVAL_COPY_VALUE(),他僅僅拷貝zval的value和type成員。

zval *zv_src;MAKE_STD_ZVAL(zv_src);ZVAL_STRING(zv_src, "test", 1);zval *zv_dest;ALLOC_ZVAL(zv_dest);ZVAL_COPY_VALUE(zv_dest, zv_src);

此刻zv_destzv_src擁有同樣的value和type。 注意同樣的value意味著這兩個zval使用同一個字符串指針(char*),這就是說如果zv_src被釋放掉,那么其字符串value也會被釋放掉,那么zv_dest的值將是一個懸空指針。 為了避免這中情況,應該使用zval_copy_ctor()進行拷貝

zval *zv_dest;ALLOC_ZVAL(zv_dest);ZVAL_COPY_VALUE(zv_dest, zv_src);zval_copy_ctor(zv_dest);

zval_copy_ctor會創建一個完全的拷貝。 即:如果是一個字符串那么對應char*會被拷貝,如果是一個數組,HashTable*會被拷貝, 如果是對象或者資源類型,zval內部的refcount將會+1

現在還剩下refcountis_ref的初始化沒有介紹, 你可以使用INIT_PZVAL宏,或者用MAKE_STD_ZVAL替代ALLOC_ZVAL來進行初始化操作,另一種方案是使用INIT_PZVAL_COPY()替代ZVAL_COPY_VALUE()函數,它會執行拷貝并對refcountis_ref進行初始化:

zval *zv_dest;ALLOC_ZVAL(zv_dest);INIT_PZVAL_COPY(zv_dest, zv_src);zval_copy_ctor(zv_dest);

由于INIT_PZVAL_COPY()zval_copy_ctor()的聯合使用非常廣泛, 這二者的功能被整合到了MAKE_COPY_ZVAL()宏中:

zval *zv_dest;ALLOC_ZVAL(zv_dest);MAKE_COPY_ZVAL(&zv_src, zv_dest);

這個宏有一點欺騙性,函數參數的順序與其他宏不同(目的指針參數不在第一位,而是第二位), 并且其接受zval**參數。

除了基本的zval拷貝宏之外還有一些比較復雜的宏,其中最重要的是ZVAL_ZVAL, 常用于從一個函數中返回zvals, 原型如下:

ZVAL_ZVAL(zv_dest, zv_src, copy, dtor)

copy參數指的是是否在目標zval上執行zval_copy_ctor(), dtor參數表示是否在源zval上執行zval_ptr_dtor()。 我們看下兩個參數所有4中不同的組合的行為, 舉一個最簡單的例子copy和dtor參數都是0:

ZVAL_ZVAL(zv_dest, zv_src, 0, 0);/* equivalent to: */ZVAL_COPY_VALUE(zv_dest, zv_src)

在這個例子中ZVAL_ZVAL()ZVAL_COPY_VALUE()效果一致, 想這樣參數0,0沒有實際意義。一個有用的變體為copy=1, dtor=0:

ZVAL_ZVAL(zv_dest, zv_src, 1, 0);/* equivalent to: */ZVAL_COPY_VALUE(zv_dest, zv_src);zval_copy_ctor(&zv_src);

這是一個常規的zval拷貝與MAKE_COPY_ZVAL()效果類似, 只是缺少了INIT_PZVAL()這一步。當拷貝一個已經初始化的zval很有用(如return_value). 另外設置dtor=1只是增加了zval_ptr_dtor()調用。

ZVAL_ZVAL(zv_dest, zv_src, 1, 1);/* equivalent to: */ZVAL_COPY_VALUE(zv_dest, zv_src);zval_copy_ctor(zv_dest);zval_ptr_dtor(&zv_src);

最有趣的情況是copy=0, dtor=1:

ZVAL_ZVAL(zv_dest, zv_src, 0, 1);/* equivalent to: */ZVAL_COPY_VALUE(zv_dest, zv_src);ZVAL_NULL(zv_src);zval_ptr_dtor(&zv_src);

這構成了一個zval的move操作, zv_src中的值value被移動到了zv_dest中。 當zv_srcrefcount=1時,zval將會被zval_ptr_dtor()銷毀。如果refcount大于1,zval將會以NULL值存在。

zval分離

之前介紹的一些宏主要用于拷貝zvals, 典型應用是拷貝一個zval的值到return_value中, 另外一些宏用于zval分離,這些宏用于寫時復制, 我們通過示例代碼來介紹他們的作用。

#define SEPARATE_ZVAL(ppzv) / do { / if (Z_REFCOUNT_PP((ppzv)) > 1) { / zval *new_zv; / Z_DELREF_PP(ppzv); / ALLOC_ZVAL(new_zv); / INIT_PZVAL_COPY(new_zv, *(ppzv)); / *(ppzv) = new_zv; / zval_copy_ctor(new_zv); / } / } while (0)

如果refcount是1 那么上面函數什么都不會執行, 如果refcount > 1 , 則先將refcount減1, 然后將value值拷貝到一個新的zval中。 這個函數接受一個zval**的參數,函數并不會修改這個指針指向的zval*。

這在實際中有什么用處呢,假設你想要修改一個數組中的元素如$array[42], 你需要先拿到一個指向數組元素的zval*的一個指針zval**. 由于引用計數的關系, 你不能直接修改它(因為這個zval的值可能同時被其他變量引用), 所以你需要先進行zval分離, 如果refcount = 1 那么zval分離會保留原有zval不動, 如果大于1會進行zval拷貝, 后一種情況下新的zval會被賦值給一個zval**指針。 在上面這個例子的情況下, 也就是指數組中42這個元素。

在這里簡單通過MAKE_COPY_ZVAL()進行拷貝時不夠的, 因為拷貝后的zval并不是數組中存儲的那個zval。

當修改zval前直接通過SEPARATE_ZVAL()函數進行分離并不適用于所有情況。 當zval is_ref=1時zval分離不會發生。 為解決這個問題,我們看一下php提供的is_ref操作宏:

Z_ISREF_P(zv_ptr) /* Get if zval is reference */ Z_SET_ISREF_P(zv_ptr) /* Set is_ref=1 */ Z_UNSET_ISREF_P(zv_ptr) /* Set is_ref=0 */ Z_SET_ISREF_TO_P(zv_ptr, 1) /* Same as Z_SET_ISREF_P(zv_ptr) */ Z_SET_ISREF_TO_P(zv_ptr, 0) /* Same as Z_UNSET_ISREF_P(zv_ptr) */

同樣的上面這些宏也有諸如_P_PP之類的變體用于處理zval, zval*, zval**。另外還存在一個老的宏PZVAL_IS_REF()它等同于Z_ISREF_P()宏。

PHP提供了兩個SEPARATE_ZVAL()宏的變體:

#define SEPARATE_ZVAL_IF_NOT_REF(ppzv) / if (!PZVAL_IS_REF(*ppzv)) { / SEPARATE_ZVAL(ppzv); / } #define SEPARATE_ZVAL_TO_MAKE_IS_REF(ppzv) / if (!PZVAL_IS_REF(*ppzv)) { / SEPARATE_ZVAL(ppzv); / Z_SET_ISREF_PP((ppzv)); / }

SEPARATE_ZVAL_IF_NOT_REF在修改zval中經常用到, SEPARATE_ZVAL_TO_MAKE_IS_REF宏主要由php引擎調用,擴展開發中很少用到

有另外一個用于zval分離的宏, 與上面介紹的有些不同:

#define SEPARATE_ARG_IF_REF(varptr) / if (PZVAL_IS_REF(varptr)) { / zval *original_var = varptr; / ALLOC_ZVAL(varptr); / INIT_PZVAL_COPY(varptr, original_var); / zval_copy_ctor(varptr); / } else { / Z_ADDREF_P(varptr); / }

首先它接收一個zval*而不是zval**指針。 其次這個宏會增加refcount, 而其他宏不會。

除此之外這個宏是SEPARATE_ZVAL_IF_NO_REF宏的補充:當一個zval是一個引用時進行zval分離。 它主要用于確保一個參數是一個值而不是引用。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久精品中文字幕一区| 欧美大全免费观看电视剧大泉洋| 国产色婷婷国产综合在线理论片a| 欧美怡红院视频一区二区三区| 狠狠久久亚洲欧美专区| 欧美日韩免费观看中文| 中文字幕av一区二区| 91久久精品在线| 日韩av电影在线网| 欧美性xxxx极品高清hd直播| 久久久国产精品视频| 欧洲成人在线观看| 亚洲欧美日韩爽爽影院| 国产色综合天天综合网| 欧美大奶子在线| 欧美日韩国产色| 欧美一级淫片播放口| 亚洲毛片在线观看| 欧美精品videos性欧美| 91精品免费久久久久久久久| 欧美国产一区二区三区| 情事1991在线| 国产亚洲精品美女久久久| 亚洲第一视频在线观看| 成人激情视频网| 一区二区成人精品| 亚洲精品videossex少妇| 97人洗澡人人免费公开视频碰碰碰| 亚洲字幕一区二区| 国产成人拍精品视频午夜网站| 欧美视频在线免费看| 亚洲影影院av| 深夜精品寂寞黄网站在线观看| 中文字幕在线观看日韩| 日韩国产精品视频| 欧美在线性爱视频| 日本19禁啪啪免费观看www| 久久久久久久激情视频| 91免费人成网站在线观看18| 国产不卡一区二区在线播放| 成人性生交xxxxx网站| 国产精品美女久久| 久久天天躁夜夜躁狠狠躁2022| 国产福利精品视频| 久久精品视频播放| 国产黑人绿帽在线第一区| 中文字幕欧美在线| 国产+人+亚洲| 全亚洲最色的网站在线观看| 在线不卡国产精品| 久久久久这里只有精品| 亚洲一区二区久久| 欧美一级视频一区二区| 精品久久久久久中文字幕| 精品视频—区二区三区免费| 亚洲乱码国产乱码精品精天堂| 亚洲男女性事视频| 国产精品专区h在线观看| 国产精品青草久久久久福利99| 精品久久久久久久久久久久| 欧美黄色片在线观看| 国语自产精品视频在线看一大j8| 日韩小视频在线观看| 久久久精品在线观看| 亚洲国产成人久久| 中文字幕欧美精品日韩中文字幕| 国产中文欧美精品| 色婷婷av一区二区三区久久| 国产视频在线观看一区二区| 97久久久免费福利网址| 亚洲成人aaa| 久久久国产精品免费| 亚洲欧美国产精品久久久久久久| 日韩视频免费观看| 亚洲国产精品久久久久秋霞不卡| 久久久电影免费观看完整版| 亚洲美女黄色片| 欧美大尺度激情区在线播放| 国产欧美久久久久久| 91精品国产综合久久香蕉最新版| 亚洲欧美日韩中文在线| 成人黄色短视频在线观看| 91久久精品视频| 国产精品中文字幕久久久| 尤物九九久久国产精品的分类| 97久久精品人搡人人玩| 狠狠干狠狠久久| 精品久久久久人成| 欧美日本在线视频中文字字幕| 欧美日韩国产成人在线| 国产成人精品视频在线观看| 欧美日韩一区二区在线播放| 日韩有码视频在线| 欧美亚洲在线播放| 亚洲毛茸茸少妇高潮呻吟| 欧美怡春院一区二区三区| 色婷婷久久av| 在线观看国产精品91| 日韩中文字幕在线精品| 亚洲区bt下载| 久久久伊人日本| 66m—66摸成人免费视频| 亚洲图片在区色| 97精品视频在线| 97国产在线视频| 97视频在线免费观看| 亚洲精品国精品久久99热一| 亚洲色图第三页| 久久久精品2019中文字幕神马| 日韩女在线观看| 92版电视剧仙鹤神针在线观看| 久久精品91久久久久久再现| 亚洲精品一区二三区不卡| 国产精品久久久久久久久粉嫩av| 国产成人+综合亚洲+天堂| 亚洲欧洲国产一区| 在线观看视频亚洲| 成人久久久久爱| 亚洲国产成人久久综合| 欧美综合激情网| 亚洲欧洲国产一区| 成人免费高清完整版在线观看| 亚洲精品美女久久久久| 日韩中文字幕视频在线| 国产成人精品在线| 欧美性猛交xxx| 久久天天躁狠狠躁老女人| 亚洲香蕉伊综合在人在线视看| 日韩三级影视基地| 日韩在线视频观看| 中文字幕av一区二区三区谷原希美| 国产精品一区二区3区| 久久精品视频亚洲| 国产99久久精品一区二区| 久久久久九九九九| 日韩大胆人体377p| 国产美女高潮久久白浆| 国产精品无av码在线观看| 国产精品爽爽爽爽爽爽在线观看| 亚洲欧美中文日韩v在线观看| 欧美亚洲另类视频| 久久人人爽人人爽人人片av高请| 久久久免费精品| 亚洲精品日韩欧美| 美女精品视频一区| 欧美成人免费小视频| 成人动漫网站在线观看| 亚洲国产成人精品一区二区| 91豆花精品一区| 日日骚久久av| 欧美激情videoshd| 久久精品国产欧美激情| 国产精品久久久久久久久粉嫩av| 久久视频精品在线| 亚洲va欧美va国产综合久久| 中文字幕日韩欧美精品在线观看| 日本高清久久天堂| 国产亚洲激情视频在线| 中文字幕日韩精品有码视频| 亚洲乱码av中文一区二区| 日韩成人在线电影网| 欧美日韩色婷婷| 亚洲va久久久噜噜噜| 91久久精品国产91性色|