激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - PHP教程 - 深入探討PHP中的內(nèi)存管理問題

深入探討PHP中的內(nèi)存管理問題

2019-12-11 13:27PHP教程網(wǎng) PHP教程

內(nèi)存管理對于長期運行的程序,例如服務(wù)器守護程序,是相當(dāng)重要的影響;因此,理解PHP是如何分配與釋放內(nèi)存的對于創(chuàng)建這類程序極為重要。本文將重點探討PHP的內(nèi)存管理問題。

一、 內(nèi)存

  在PHP中,填充一個字符串變量相當(dāng)簡單,這只需要一個語句"<?php $str = 'hello world '; ?>"即可,并且該字符串能夠被自由地修改、拷貝和移動。而在C語言中,盡管你能夠編寫例如"char *str = "hello world ";"這樣的一個簡單的靜態(tài)字符串;但是,卻不能修改該字符串,因為它生存于程序空間內(nèi)。為了創(chuàng)建一個可操縱的字符串,你必須分配一個內(nèi)存塊,并且通過一個函數(shù)(例如strdup())來復(fù)制其內(nèi)容。

{
 char *str;
 str = strdup("hello world");
 if (!str) {
  fprintf(stderr, "Unable to allocate memory!");
 }
}


  由于后面我們將分析的各種原因,傳統(tǒng)型內(nèi)存管理函數(shù)(例如malloc(),free(),strdup(),realloc(),calloc(),等等)幾乎都不能直接為PHP源代碼所使用。

  二、 釋放內(nèi)存

  在幾乎所有的平臺上,內(nèi)存管理都是通過一種請求和釋放模式實現(xiàn)的。首先,一個應(yīng)用程序請求它下面的層(通常指"操作系統(tǒng)"):"我想使用一些內(nèi)存空間"。如果存在可用的空間,操作系統(tǒng)就會把它提供給該程序并且打上一個標(biāo)記以便不會再把這部分內(nèi)存分配給其它程序。
當(dāng)應(yīng)用程序使用完這部分內(nèi)存,它應(yīng)該被返回到OS;這樣以來,它就能夠被繼續(xù)分配給其它程序。如果該程序不返回這部分內(nèi)存,那么OS無法知道是否這塊內(nèi)存不再使用并進而再分配給另一個進程。如果一個內(nèi)存塊沒有釋放,并且所有者應(yīng)用程序丟失了它,那么,我們就說此應(yīng)用程序"存在漏洞",因為這部分內(nèi)存無法再為其它程序可用。

  在一個典型的客戶端應(yīng)用程序中,較小的不太經(jīng)常的內(nèi)存泄漏有時能夠為OS所"容忍",因為在這個進程稍后結(jié)束時該泄漏內(nèi)存會被隱式返回到OS。這并沒有什么,因為OS知道它把該內(nèi)存分配給了哪個程序,并且它能夠確信當(dāng)該程序終止時不再需要該內(nèi)存。

  而對于長時間運行的服務(wù)器守護程序,包括象Apache這樣的web服務(wù)器和擴展php模塊來說,進程往往被設(shè)計為相當(dāng)長時間一直運行。因為OS不能清理內(nèi)存使用,所以,任何程序的泄漏-無論是多么小-都將導(dǎo)致重復(fù)操作并最終耗盡所有的系統(tǒng)資源。

  現(xiàn)在,我們不妨考慮用戶空間內(nèi)的stristr()函數(shù);為了使用大小寫不敏感的搜索來查找一個字符串,它實際上創(chuàng)建了兩個串的各自的一個小型副本,然后執(zhí)行一個更傳統(tǒng)型的大小寫敏感的搜索來查找相對的偏移量。然而,在定位該字符串的偏移量之后,它不再使用這些小寫版本的字符串。如果它不釋放這些副本,那么,每一個使用stristr()的腳本在每次調(diào)用它時都將泄漏一些內(nèi)存。最后,web服務(wù)器進程將擁有所有的系統(tǒng)內(nèi)存,但卻不能夠使用它。

  你可以理直氣壯地說,理想的解決方案就是編寫良好、干凈的、一致的代碼。這當(dāng)然不錯;但是,在一個象PHP解釋器這樣的環(huán)境中,這種觀點僅對了一半。

  三、 錯誤處理

  為了實現(xiàn)"跳出"對用戶空間腳本及其依賴的擴展函數(shù)的一個活動請求,需要使用一種方法來完全"跳出"一個活動請求。這是在Zend引擎內(nèi)實現(xiàn)的:在一個請求的開始設(shè)置一個"跳出"地址,然后在任何die()或exit()調(diào)用或在遇到任何關(guān)鍵錯誤(E_ERROR)時執(zhí)行一個longjmp()以跳轉(zhuǎn)到該"跳出"地址。

  盡管這個"跳出"進程能夠簡化程序執(zhí)行的流程,但是,在絕大多數(shù)情況下,這會意味著將會跳過資源清除代碼部分(例如free()調(diào)用)并最終導(dǎo)致出現(xiàn)內(nèi)存漏洞。現(xiàn)在,讓我們來考慮下面這個簡化版本的處理函數(shù)調(diào)用的引擎代碼:

void call_function(const char *fname, int fname_len TSRMLS_DC){
 zend_function *fe;
 char *lcase_fname;
 /* PHP函數(shù)名是大小寫不敏感的,
 *為了簡化在函數(shù)表中對它們的定位,
 *所有函數(shù)名都隱含地翻譯為小寫的
 */
 lcase_fname = estrndup(fname, fname_len);
 zend_str_tolower(lcase_fname, fname_len);
 if (zend_hash_find(EG(function_table),lcase_fname, fname_len + 1, (void **)&fe) == FAILURE) {
  zend_execute(fe->op_array TSRMLS_CC);
 } else {
  php_error_docref(NULL TSRMLS_CC, E_ERROR,"Call to undefined function: %s()", fname);
 }
 efree(lcase_fname);
}


  當(dāng)執(zhí)行到php_error_docref()這一行時,內(nèi)部錯誤處理器就會明白該錯誤級別是critical,并相應(yīng)地調(diào)用longjmp()來中斷當(dāng)前程序流程并離開call_function()函數(shù),甚至根本不會執(zhí)行到efree(lcase_fname)這一行。你可能想把efree()代碼行移動到zend_error()代碼行的上面;但是,調(diào)用這個call_function()例程的代碼行會怎么樣呢?fname本身很可能就是一個分配的字符串,并且,在它被錯誤消息處理使用完之前,你根本不能釋放它。

  注意,這個php_error_docref()函數(shù)是trigger_error()函數(shù)的一個內(nèi)部等價實現(xiàn)。它的第一個參數(shù)是一個將被添加到docref的可選的文檔引用。第三個參數(shù)可以是任何我們熟悉的E_*家族常量,用于指示錯誤的嚴重程度。第四個參數(shù)(最后一個)遵循printf()風(fēng)格的格式化和變量參數(shù)列表式樣。

  四、 Zend內(nèi)存管理器

  在上面的"跳出"請求期間解決內(nèi)存泄漏的方案之一是:使用Zend內(nèi)存管理(ZendMM)層。引擎的這一部分非常類似于操作系統(tǒng)的內(nèi)存管理行為-分配內(nèi)存給調(diào)用程序。區(qū)別在于,它處于進程空間中非常低的位置而且是"請求感知"的;這樣以來,當(dāng)一個請求結(jié)束時,它能夠執(zhí)行與OS在一個進程終止時相同的行為。也就是說,它會隱式地釋放所有的為該請求所占用的內(nèi)存。圖1展示了ZendMM與OS以及PHP進程之間的關(guān)系。
 

深入探討PHP中的內(nèi)存管理問題
圖1.Zend內(nèi)存管理器代替系統(tǒng)調(diào)用來實現(xiàn)針對每一種請求的內(nèi)存分配。


  除了提供隱式內(nèi)存清除功能之外,ZendMM還能夠根據(jù)php.ini中memory_limit的設(shè)置控制每一種內(nèi)存請求的用法。如果一個腳本試圖請求比系統(tǒng)中可用內(nèi)存更多的內(nèi)存,或大于它每次應(yīng)該請求的最大量,那么,ZendMM將自動地發(fā)出一個E_ERROR消息并且啟動相應(yīng)的"跳出"進程。這種方法的一個額外優(yōu)點在于,大多數(shù)內(nèi)存分配調(diào)用的返回值并不需要檢查,因為如果失敗的話將會導(dǎo)致立即跳轉(zhuǎn)到引擎的退出部分。

  把PHP內(nèi)部代碼和OS的實際的內(nèi)存管理層"鉤"在一起的原理并不復(fù)雜:所有內(nèi)部分配的內(nèi)存都要使用一組特定的可選函數(shù)實現(xiàn)。例如,PHP代碼不是使用malloc(16)來分配一個16字節(jié)內(nèi)存塊而是使用了emalloc(16)。除了實現(xiàn)實際的內(nèi)存分配任務(wù)外,ZendMM還會使用相應(yīng)的綁定請求類型來標(biāo)志該內(nèi)存塊;這樣以來,當(dāng)一個請求"跳出"時,ZendMM可以隱式地釋放它。

  經(jīng)常情況下,內(nèi)存一般都需要被分配比單個請求持續(xù)時間更長的一段時間。這種類型的分配(因其在一次請求結(jié)束之后仍然存在而被稱為"永久性分配"),可以使用傳統(tǒng)型內(nèi)存分配器來實現(xiàn),因為這些分配并不會添加ZendMM使用的那些額外的相應(yīng)于每種請求的信息。然而有時,直到運行時刻才會確定是否一個特定的分配需要永久性分配,因此ZendMM導(dǎo)出了一組幫助宏,其行為類似于其它的內(nèi)存分配函數(shù),但是使用最后一個額外參數(shù)來指示是否為永久性分配。

  如果你確實想實現(xiàn)一個永久性分配,那么這個參數(shù)應(yīng)該被設(shè)置為1;在這種情況下,請求是通過傳統(tǒng)型malloc()分配器家族進行傳遞的。然而,如果運行時刻邏輯認為這個塊不需要永久性分配;那么,這個參數(shù)可以被設(shè)置為零,并且調(diào)用將會被調(diào)整到針對每種請求的內(nèi)存分配器函數(shù)。

  例如,pemalloc(buffer_len,1)將映射到malloc(buffer_len),而pemalloc(buffer_len,0)將被使用下列語句映射到emalloc(buffer_len):

#define in Zend/zend_alloc.h:
#define pemalloc(size, persistent) ((persistent)?malloc(size): emalloc(size))


  所有這些在ZendMM中提供的分配器函數(shù)都能夠從下表中找到其更傳統(tǒng)的對應(yīng)實現(xiàn)。

  表格1展示了ZendMM支持下的每一個分配器函數(shù)以及它們的e/pe對應(yīng)實現(xiàn):

  表格1.傳統(tǒng)型相對于PHP特定的分配器。

分配器函數(shù)

e/pe對應(yīng)實現(xiàn)

void *malloc(size_t count);

void *emalloc(size_t count);void *pemalloc(size_t count,char persistent);

void *calloc(size_t count);

void *ecalloc(size_t count);void *pecalloc(size_t count,char persistent);

void *realloc(void *ptr,size_t count);

void *erealloc(void *ptr,size_t count);
void *perealloc(void *ptr,size_t count,char persistent);

void *strdup(void *ptr);

void *estrdup(void *ptr);void *pestrdup(void *ptr,char persistent);

void free(void *ptr);

void efree(void *ptr);
void pefree(void *ptr,char persistent);


  你可能會注意到,即使是pefree()函數(shù)也要求使用永久性標(biāo)志。這是因為在調(diào)用pefree()時,它實際上并不知道是否ptr是一種永久性分配。針對一個非永久性分配調(diào)用free()能夠?qū)е码p倍的空間釋放,而針對一種永久性分配調(diào)用efree()有可能會導(dǎo)致一個段錯誤,因為內(nèi)存管理器會試圖查找并不存在的管理信息。因此,你的代碼需要記住它分配的數(shù)據(jù)結(jié)構(gòu)是否是永久性的。 

  除了分配器函數(shù)核心部分外,還存在其它一些非常方便的ZendMM特定的函數(shù),例如:

void *estrndup(void *ptr,int len);


  該函數(shù)能夠分配len+1個字節(jié)的內(nèi)存并且從ptr處復(fù)制len個字節(jié)到最新分配的塊。這個estrndup()函數(shù)的行為可以大致描述如下:

void *estrndup(void *ptr, int len)
{
 char *dst = emalloc(len + 1);
 memcpy(dst, ptr, len);
 dst[len] = 0;
 return dst;
}


  在此,被隱式放置在緩沖區(qū)最后的NULL字節(jié)可以確保任何使用estrndup()實現(xiàn)字符串復(fù)制操作的函數(shù)都不需要擔(dān)心會把結(jié)果緩沖區(qū)傳遞給一個例如printf()這樣的希望以為NULL為結(jié)束符的函數(shù)。當(dāng)使用estrndup()來復(fù)制非字符串?dāng)?shù)據(jù)時,最后一個字節(jié)實質(zhì)上都浪費了,但其中的利明顯大于弊。

void *safe_emalloc(size_t size, size_t count, size_t addtl);
void *safe_pemalloc(size_t size, size_t count,size_t addtl,char persistent);


  這些函數(shù)分配的內(nèi)存空間最終大小是((size*count)+addtl)。你可以會問:"為什么還要提供額外函數(shù)呢?為什么不使用一個emalloc/pemalloc呢?"原因很簡單:為了安全。盡管有時候可能性相當(dāng)小,但是,正是這一"可能性相當(dāng)小"的結(jié)果導(dǎo)致宿主平臺的內(nèi)存溢出。這可能會導(dǎo)致分配負數(shù)個數(shù)的字節(jié)空間,或更有甚者,會導(dǎo)致分配一個小于調(diào)用程序要求大小的字節(jié)空間。而safe_emalloc()能夠避免這種類型的陷井-通過檢查整數(shù)溢出并且在發(fā)生這樣的溢出時顯式地預(yù)以結(jié)束。

  注意,并不是所有的內(nèi)存分配例程都有一個相應(yīng)的p*對等實現(xiàn)。例如,不存在pestrndup(),并且在PHP 5.1版本前也不存在safe_pemalloc()。

  五、 引用計數(shù)

  慎重的內(nèi)存分配與釋放對于PHP(它是一種多請求進程)的長期性能有極其重大的影響;但是,這還僅是問題的一半。為了使一個每秒處理上千次點擊的服務(wù)器高效地運行,每一次請求都需要使用盡可能少的內(nèi)存并且要盡可能減少不必要的數(shù)據(jù)復(fù)制操作。請考慮下列PHP代碼片斷:

<?php
$a = 'Hello World';
$b = $a;
unset($a);
?>


  在第一次調(diào)用之后,只有一個變量被創(chuàng)建,并且一個12字節(jié)的內(nèi)存塊指派給它以便存儲字符串"Hello World",還包括一個結(jié)尾處的NULL字符。現(xiàn)在,讓我們來觀察后面的兩行:$b被置為與變量$a相同的值,然后變量$a被釋放。

  如果PHP因每次變量賦值都要復(fù)制變量內(nèi)容的話,那么,對于上例中要復(fù)制的字符串還需要復(fù)制額外的12個字節(jié),并且在數(shù)據(jù)復(fù)制期間還要進行另外的處理器加載。這一行為乍看起來有點荒謬,因為當(dāng)?shù)谌写a出現(xiàn)時,原始變量被釋放,從而使得整個數(shù)據(jù)復(fù)制顯得完全不必要。其實,我們不妨再遠一層考慮,讓我們設(shè)想當(dāng)一個10MB大小的文件的內(nèi)容被裝載到兩個變量中時會發(fā)生什么。這將會占用20MB的空間,此時,10已經(jīng)足夠了。引擎會把那么多的時間和內(nèi)存浪費在這樣一種無用的努力上嗎?

  你應(yīng)該知道,PHP的設(shè)計者早已深諳此理。

  記住,在引擎中,變量名和它們的值實際上是兩個不同的概念。值本身是一個無名的zval*存儲體(在本例中,是一個字符串值),它被通過zend_hash_add()賦給變量$a。如果兩個變量名都指向同一個值,會發(fā)生什么呢?

{
 zval *helloval;
 MAKE_STD_ZVAL(helloval);
 ZVAL_STRING(helloval, "Hello World", 1);
 zend_hash_add(EG(active_symbol_table), "a", sizeof("a"),&helloval, sizeof(zval*), NULL);
 zend_hash_add(EG(active_symbol_table), "b", sizeof("b"),&helloval, sizeof(zval*), NULL);
}


  此時,你可以實際地觀察$a或$b,并且會看到它們都包含字符串"Hello World"。遺憾的是,接下來,你繼續(xù)執(zhí)行第三行代碼"unset($a);"。此時,unset()并不知道$a變量指向的數(shù)據(jù)還被另一個變量所使用,因此它只是盲目地釋放掉該內(nèi)存。任何隨后的對變量$b的存取都將被分析為已經(jīng)釋放的內(nèi)存空間并因此導(dǎo)致引擎崩潰。

  這個問題可以借助于zval(它有好幾種形式)的第四個成員refcount加以解決。當(dāng)一個變量被首次創(chuàng)建并賦值時,它的refcount被初始化為1,因為它被假定僅由最初創(chuàng)建它時相應(yīng)的變量所使用。當(dāng)你的代碼片斷開始把helloval賦給$b時,它需要把refcount的值增加為2;這樣以來,現(xiàn)在該值被兩個變量所引用: 

{
 zval *helloval;
 MAKE_STD_ZVAL(helloval);
 ZVAL_STRING(helloval, "Hello World", 1);
 zend_hash_add(EG(active_symbol_table), "a", sizeof("a"),&helloval, sizeof(zval*), NULL);
 ZVAL_ADDREF(helloval);
 zend_hash_add(EG(active_symbol_table), "b", sizeof("b"),&helloval,sizeof(zval*),NULL);
}


  現(xiàn)在,當(dāng)unset()刪除原變量的$a相應(yīng)的副本時,它就能夠從refcount參數(shù)中看到,還有另外其他人對該數(shù)據(jù)感興趣;因此,它應(yīng)該只是減少refcount的計數(shù)值,然后不再管它。

 

  六、 寫復(fù)制(Copy on Write)

  通過refcounting來節(jié)約內(nèi)存的確是不錯的主意,但是,當(dāng)你僅想改變其中一個變量的值時情況會如何呢?為此,請考慮下面的代碼片斷:
 

<?php
$a = 1;
$b = $a;
$b += 5;
?>


  通過上面的邏輯流程,你當(dāng)然知道$a的值仍然等于1,而$b的值最后將是6。并且此時,你還知道,Zend在盡力節(jié)省內(nèi)存-通過使$a和$b都引用相同的zval(見第二行代碼)。那么,當(dāng)執(zhí)行到第三行并且必須改變$b變量的值時,會發(fā)生什么情況呢?

  回答是,Zend要查看refcount的值,并且確保在它的值大于1時對之進行分離。在Zend引擎中,分離是破壞一個引用對的過程,正好與你剛才看到的過程相反:

zval *get_var_and_separate(char *varname, int varname_len TSRMLS_DC)
{
 zval **varval, *varcopy;
 if (zend_hash_find(EG(active_symbol_table),varname, varname_len + 1, (void**)&varval) == FAILURE) {
  /* 變量根本并不存在-失敗而導(dǎo)致退出*/
  return NULL;
 }
 if ((*varval)->refcount < 2) {
  /* varname是唯一的實際引用,
  *不需要進行分離
  */
  return *varval;
 }
 /* 否則,再復(fù)制一份zval*的值*/
 MAKE_STD_ZVAL(varcopy);
 varcopy = *varval;
 /* 復(fù)制任何在zval*內(nèi)的已分配的結(jié)構(gòu)*/
 zval_copy_ctor(varcopy);
 /*刪除舊版本的varname
 *這將減少該過程中varval的refcount的值
 */
 zend_hash_del(EG(active_symbol_table), varname, varname_len + 1);
 /*初始化新創(chuàng)建的值的引用計數(shù),并把它依附到
 * varname變量
 */
 varcopy->refcount = 1;
 varcopy->is_ref = 0;
 zend_hash_add(EG(active_symbol_table), varname, varname_len + 1,&varcopy, sizeof(zval*), NULL);
 /*返回新的zval* */
 return varcopy;
}


  現(xiàn)在,既然引擎有一個僅為變量$b所擁有的zval*(引擎能知道這一點),所以它能夠把這個值轉(zhuǎn)換成一個long型值并根據(jù)腳本的請求給它增加5。

  七、 寫改變(change-on-write)

  引用計數(shù)概念的引入還導(dǎo)致了一個新的數(shù)據(jù)操作可能性,其形式從用戶空間腳本管理器看來與"引用"有一定關(guān)系。請考慮下列的用戶空間代碼片斷:

<?php
$a = 1;
$b = &$a;
$b += 5;
?>


  在上面的PHP代碼中,你能看出$a的值現(xiàn)在為6,盡管它一開始為1并且從未(直接)發(fā)生變化。之所以會發(fā)生這種情況是因為當(dāng)引擎開始把$b的值增加5時,它注意到$b是一個對$a的引用并且認為"我可以改變該值而不必分離它,因為我想使所有的引用變量都能看到這一改變"。

  但是,引擎是如何知道的呢?很簡單,它只要查看一下zval結(jié)構(gòu)的第四個和最后一個元素(is_ref)即可。這是一個簡單的開/關(guān)位,它定義了該值是否實際上是一個用戶空間風(fēng)格引用集的一部分。在前面的代碼片斷中,當(dāng)執(zhí)行第一行時,為$a創(chuàng)建的值得到一個refcount為1,還有一個is_ref值為0,因為它僅為一個變量($a)所擁有并且沒有其它變量對它產(chǎn)生寫引用改變。在第二行,這個值的refcount元素被增加為2,除了這次is_ref元素被置為1之外(因為腳本中包含了一個"&"符號以指示是完全引用)。

  最后,在第三行,引擎再一次取出與變量$b相關(guān)的值并且檢查是否有必要進行分離。這一次該值沒有被分離,因為前面沒有包括一個檢查。下面是get_var_and_separate()函數(shù)中與refcount檢查有關(guān)的部分代碼:

if ((*varval)->is_ref || (*varval)->refcount < 2) {
 /* varname是唯一的實際引用,
 * 或者它是對其它變量的一個完全引用
 *任何一種方式:都沒有進行分離
 */
 return *varval;
}


  這一次,盡管refcount為2,卻沒有實現(xiàn)分離,因為這個值是一個完全引用。引擎能夠自由地修改它而不必關(guān)心其它變量值的變化。

 

  八、 分離問題

  盡管已經(jīng)存在上面討論到的復(fù)制和引用技術(shù),但是還存在一些不能通過is_ref和refcount操作來解決的問題。請考慮下面這個PHP代碼塊:
 

<?php
$a = 1;
$b = $a;
$c = &$a;
?>


  在此,你有一個需要與三個不同的變量相關(guān)聯(lián)的值。其中,兩個變量是使用了"change-on-write"完全引用方式,而第三個變量處于一種可分離的"copy-on-write"(寫復(fù)制)上下文中。如果僅使用is_ref和refcount來描述這種關(guān)系,有哪些值能夠工作呢?

  回答是:沒有一個能工作。在這種情況下,這個值必須被復(fù)制到兩個分離的zval*中,盡管兩者都包含完全相同的數(shù)據(jù)(見圖2)。

深入探討PHP中的內(nèi)存管理問題
圖2.引用時強制分離


  同樣,下列代碼塊將引起相同的沖突并且強迫該值分離出一個副本(見圖3)。

深入探討PHP中的內(nèi)存管理問題
圖3.復(fù)制時強制分離

 

<?php
$a = 1;
$b = &$a;
$c = $a;
?>


  注意,在這里的兩種情況下,$b都與原始的zval對象相關(guān)聯(lián),因為在分離發(fā)生時引擎無法知道介于到該操作當(dāng)中的第三個變量的名字。

  九、 總結(jié)

  PHP是一種托管語言。從普通用戶角度來看,這種仔細地控制資源和內(nèi)存的方式意味著更為容易地進行原型開發(fā)并導(dǎo)致出現(xiàn)更少的沖突。然而,當(dāng)我們深入"內(nèi)里"之后,一切的承諾似乎都不復(fù)存在,最終還要依賴于真正有責(zé)任心的開發(fā)者來維持整個運行時刻環(huán)境的一致性。

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 在线看91| 久久国产精品久久久久久 | 亚洲欧美日韩一区二区三区在线观看 | 亚洲影院久久久av天天蜜桃臀 | 欧美一区二区三区免费观看 | 精品国产一区二区三区在线 | 国产亚洲精品久久久久5区 日韩一级片一区二区三区 国产精品久久久久av | 欧美18一19sex性护士农村 | 日韩精品中文字幕在线观看 | 91精品国产91久久久久久吃药 | 在线观看91精品 | 国产免费观看一区二区三区 | 国产一级一区二区 | 在线无码 | 国产精品午夜在线 | 久久久日韩av免费观看下载 | 黄色片网站在线看 | 露脸各种姿势啪啪的清纯美女 | 久久综合综合 | 91久久线看在观草草青青 | 成人午夜免费福利 | 特大黑人videos与另类娇小 | 久久草草影视免费网 | 18一20岁一级毛片 | 成人免费av在线播放 | 少妇一级淫片免费看 | 九草视频 | 羞羞电影在线观看 | 黄色大片免费看 | 亚洲精品有限 | 视频在线中文字幕 | 香蕉视频1024 | 成人爽a毛片免费啪啪红桃视频 | 欧美日韩手机在线观看 | 欧美国产日韩在线 | 国产成人精品一区在线播放 | 久久亚洲春色中文字幕久久 | 精精国产xxxx视频在线播放7 | 麻豆视频观看 | 国产精品成人亚洲一区二区 | 欧美性生交大片 |