• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            陳碩的Blog

            C++ 工程實(shí)踐(2):不要重載全局 ::operator new()

            陳碩 (giantchen_AT_gmail)

            Blog.csdn.net/Solstice

             

            本文只考慮 Linux x86 平臺(tái),服務(wù)端開發(fā)(不考慮 Windows 的跨 DLL 內(nèi)存分配釋放問題)。本文假定讀者知道 ::operator new() 和 ::operator delete() 是干什么的,與通常用的 new/delete 表達(dá)式有和區(qū)別和聯(lián)系,這方面的知識(shí)可參考侯捷先生的文章《池內(nèi)春秋》[1],或者這篇文章

            C++ 的內(nèi)存管理是個(gè)老生常談的話題,我在《當(dāng)析構(gòu)函數(shù)遇到多線程》第 7 節(jié)“插曲:系統(tǒng)地避免各種指針錯(cuò)誤”中簡(jiǎn)單回顧了一些常見的問題以及在現(xiàn)代 C++ 中的解決辦法。基本上,按現(xiàn)代 C++ 的手法(RAII)來管理內(nèi)存,你很難遇到什么內(nèi)存方面的錯(cuò)誤。“沒有錯(cuò)誤”是基本要求,不代表“足夠好”。我們常常會(huì)設(shè)法優(yōu)化性能,如果 profiling 表明 hot spot 在內(nèi)存分配和釋放上,重載全局的 ::operator new() 和 ::operator delete() 似乎是一個(gè)一勞永逸好辦法(以下簡(jiǎn)寫為“重載 ::operator new()”),本文試圖說明這個(gè)辦法往往行不通。

            內(nèi)存管理的基本要求

            如果只考慮分配和釋放,內(nèi)存管理基本要求是“不重不漏”:既不重復(fù) delete,也不漏掉 delete。也就說我們常說的 new/delete 要配對(duì),“配對(duì)”不僅是個(gè)數(shù)相等,還隱含了 new 和 delete 的調(diào)用本身要匹配,不要“東家借的東西西家還”。例如:

            • 用系統(tǒng)默認(rèn)的 malloc() 分配的內(nèi)存要交給系統(tǒng)默認(rèn)的 free() 去釋放;
            • 用系統(tǒng)默認(rèn)的 new 表達(dá)式創(chuàng)建的對(duì)象要交給系統(tǒng)默認(rèn)的 delete 表達(dá)式去析構(gòu)并釋放;
            • 用系統(tǒng)默認(rèn)的 new[] 表達(dá)式創(chuàng)建的對(duì)象要交給系統(tǒng)默認(rèn)的 delete[] 表達(dá)式去析構(gòu)并釋放;
            • 用系統(tǒng)默認(rèn)的 ::operator new() 分配的的內(nèi)存要交給系統(tǒng)默認(rèn)的 ::operator delete() 去釋放;
            • 用 placement new 創(chuàng)建的對(duì)象要用 placement delete (為了表述方便,姑且這么說吧)去析構(gòu)(其實(shí)就是直接調(diào)用析構(gòu)函數(shù));
            • 從某個(gè)內(nèi)存池 A 分配的內(nèi)存要還給這個(gè)內(nèi)存池。
            • 如果定制 new/delete,那么要按規(guī)矩來。見 Effective C++ 相關(guān)條款。

            做到以上這些不難,是每個(gè) C++ 開發(fā)人員的基本功。不過,如果你想重載全局的 ::operator new(),事情就麻煩了。

            重載 ::operator new() 的理由

            Effective C++ 第三版第 50 條列舉了定制 new/delete 的幾點(diǎn)理由:

            • 檢測(cè)代碼中的內(nèi)存錯(cuò)誤
            • 優(yōu)化性能
            • 獲得內(nèi)存使用的統(tǒng)計(jì)數(shù)據(jù)

            這些都是正當(dāng)?shù)男枨螅哪┪覀儗?huì)看到,不重載 ::operator new() 也能達(dá)到同樣的目的。

            ::operator new() 的兩種重載方式

            1. 不改變其簽名,無(wú)縫直接替換系統(tǒng)原有的版本,例如:

            #include <new>

            void* operator new(size_t size);

            void operator delete(void* p);

            用這種方式的重載,使用方不需要包含任何特殊的頭文件,也就是說不需要看見這兩個(gè)函數(shù)聲明。“性能優(yōu)化”通常用這種方式。

            2. 增加新的參數(shù),調(diào)用時(shí)也提供這些額外的參數(shù),例如:

            void* operator new(size_t size, const char* file, int line);  // 其返回的指針必須能被普通的 ::operator delete(void*) 釋放

            void operator delete(void* p, const char* file, int line);  // 這個(gè)函數(shù)只在析構(gòu)函數(shù)拋異常的情況下才會(huì)被調(diào)用

            然后用的時(shí)候是

            Foo* p = new (__FILE, __LINE__) Foo;  // 這樣能跟蹤是哪個(gè)文件哪一行代碼分配的內(nèi)存

            我們也可以用宏替換 new 來節(jié)省打字。用這第二種方式重載,使用方需要看到這兩個(gè)函數(shù)聲明,也就是說要主動(dòng)包含你提供的頭文件。“檢測(cè)內(nèi)存錯(cuò)誤”和“統(tǒng)計(jì)內(nèi)存使用情況”通常會(huì)用這種方式重載。當(dāng)然,這不是絕對(duì)的。

             

            在學(xué)習(xí) C++ 的階段,每個(gè)人都可以寫個(gè)一兩百行的程序來驗(yàn)證教科書上的說法,重載 ::operator new() 在這樣的玩具程序里邊不會(huì)造成什么麻煩。

            不過,我認(rèn)為在現(xiàn)實(shí)的產(chǎn)品開發(fā)中,重載 ::operator new() 乃是下策,我們有更簡(jiǎn)單安全的辦法來到達(dá)以上目標(biāo)。

            現(xiàn)實(shí)的開發(fā)環(huán)境

            作為 C++ 應(yīng)用程序的開發(fā)人員,在編寫稍具規(guī)模的程序時(shí),我們通常會(huì)用到一些 library。我們可以根據(jù) library 的提供方把它們大致分為這么幾大類:

            1. C 語(yǔ)言的標(biāo)準(zhǔn)庫(kù),也包括 Linux 編程環(huán)境提供的 Posix 系列函數(shù)。
            2. 第三方的 C 語(yǔ)言庫(kù),例如 OpenSSL。
            3. C++ 語(yǔ)言的標(biāo)準(zhǔn)庫(kù),主要是 STL。(我想沒有人在產(chǎn)品中使用 IOStream 吧?)
            4. 第三方的通用 C++ 庫(kù),例如 Boost.Regex,或者某款 XML 庫(kù)。
            5. 公司其他團(tuán)隊(duì)的人開發(fā)的內(nèi)部基礎(chǔ) C++ 庫(kù),比如網(wǎng)絡(luò)通信和日志等基礎(chǔ)設(shè)施。
            6. 本項(xiàng)目組的同事自己開發(fā)的針對(duì)本應(yīng)用的基礎(chǔ)庫(kù),比如某三維模型的仿射變換模塊。

            在使用這些 library 的時(shí)候,不可避免地要在各個(gè) library 之間交換數(shù)據(jù)。比方說 library A 的輸出作為 library B 的輸入,而 library A 的輸出本身常常會(huì)用到動(dòng)態(tài)分配的內(nèi)存(比如 std::vector<double>)。

            如果所有的 C++ library 都用同一套內(nèi)存分配器(就是系統(tǒng)默認(rèn)的 new/delete ),那么內(nèi)存的釋放就很方便,直接交給 delete 去釋放就行。如果不是這樣,那就得時(shí)時(shí)刻刻記住“這一塊內(nèi)存是屬于哪個(gè)分配器,是系統(tǒng)默認(rèn)的還是我們定制的,釋放的時(shí)候不要還錯(cuò)了地方”。

            (由于 C 語(yǔ)言不像 C++ 一樣提過了那么多的定制性,C library 通常都會(huì)默認(rèn)直接用 malloc/free 來分配和釋放內(nèi)存,不存在上面提到的“內(nèi)存還錯(cuò)地方”問題。或者有的考慮更全面的 C library 會(huì)讓你注冊(cè)兩個(gè)函數(shù),用于它內(nèi)部分配和釋放內(nèi)存,這就就能完全掌控該 library 的內(nèi)存使用。這種依賴注入的方式在 C++ 里變得花哨而無(wú)用,見陳碩寫的《C++ 標(biāo)準(zhǔn)庫(kù)中的allocator是多余的》。)

            但是,如果重載了 ::operator new(),事情恐怕就沒有這么簡(jiǎn)單了。

            重載 ::operator new() 的困境

            首先,重載 ::operator new() 不會(huì)給 C 語(yǔ)言的庫(kù)帶來任何麻煩,當(dāng)然,重載它得到的三點(diǎn)好處也無(wú)法讓 C 語(yǔ)言的庫(kù)享受到。

            以下僅考慮 C++ library 和 C++ 主程序。

            規(guī)則 1:絕對(duì)不能在 library 里重載 ::operator new()

            如果你是某個(gè) library 的作者,你的 library 要提供給別人使用,那么你無(wú)權(quán)重載全局 ::operator new(size_t) (注意這是上面提到的第一種重載方式),因?yàn)檫@非常具有侵略性:任何用到你的 library 的程序都被迫使用了你重載的 ::operator new(),而別人很可能不愿意這么做。另外,如果有兩個(gè) library 都試圖重載 ::operator new(size_t),那么它們會(huì)打架,我估計(jì)會(huì)發(fā)生 duplicated symbol link error。干脆,作為 library 的編寫者,大家都不要重載 ::operator new(size_t) 好了。

            那么第二種重載方式呢?首先,::operator new(size_t size, const char* file, int line) 這種方式得到的 void* 指針必須同時(shí)能被 ::operator delete(void*) 和 ::operator delete(void* p, const char* file, int line) 這兩個(gè)函數(shù)釋放。這時(shí)候你需要決定,你的 ::operator new(size_t size, const char* file, int line) 返回的指針是不是兼容系統(tǒng)默認(rèn)的 ::operator delete(void*)。

            • 如果不兼容(也就是說不能用系統(tǒng)默認(rèn)的 ::operator delete(void*) 來釋放內(nèi)存),那么你得重載 ::operator delete(void*),讓它的行為與你的 operator new(size_t size, const char* file, int line) 匹配。一旦你決定重載 ::operator delete(void*),那么你必須重載 ::operator new(size_t),這就回到了情況 1:你無(wú)權(quán)重載全局 ::operator new(size_t)。
            • 如果選擇兼容系統(tǒng)默認(rèn)的 ::operator delete(void*),那么你在 operator new(size_t size, const char* file, int line) 里能做的事情非常有限,比方說你不能額外動(dòng)態(tài)分配內(nèi)存來做 house keeping 或保存統(tǒng)計(jì)數(shù)據(jù)(無(wú)論顯示還是隱式),因?yàn)橄到y(tǒng)默認(rèn)的 ::operator delete(void*) 不會(huì)釋放你額外分配的內(nèi)存。(這里隱式分配內(nèi)存指的是往 std::map<> 這樣的容器里添加元素。)

            看到這里估計(jì)很多人已經(jīng)暈了,但這還沒完。

            其次,在 library 里重載 operator new(size_t size, const char* file, int line) 還涉及到你的重載要不要暴露給 library 的使用者(其他 library 或主程序)。這里“暴露”有兩層意思:1) 包含你的頭文件的代碼會(huì)不會(huì)用你重載的 ::operator new(),2) 重載之后的 ::operator new() 分配的內(nèi)存能不能在你的 library 之外被安全地釋放。如果不行,那么你是不是要暴露某個(gè)接口函數(shù)來讓使用者安全地釋放內(nèi)存?或者返回 shared_ptr ,利用其“捕獲”deleter 的特性?聽上去好像挺復(fù)雜?這里就不一一展開討論了,總之,作為 library 的作者,絕對(duì)不要?jiǎng)?#8220;重載 operator new()”的念頭。

            事實(shí) 2:在主程序里重載 ::operator new() 作用不大

            這不是一條規(guī)則,而是我試圖說明這么做沒有多大意義。

            如果用第一種方式重載全局 ::operator new(size_t),會(huì)影響本程序用到的所有 C++ library,這么做或許不會(huì)有什么問題,不過我建議你使用下一節(jié)介紹的更簡(jiǎn)單的“替代辦法”。

            如果用第二種方式重載 ::operator new(size_t size, const char* file, int line),那么你的行為是否惠及本程序用到的其他 C++ library 呢?比方說你要不要統(tǒng)計(jì) C++ library 中的內(nèi)存使用情況?如果某個(gè) library 會(huì)返回它自己用 new 分配的內(nèi)存和對(duì)象,讓你用完之后自己釋放,那么是否打算對(duì)錯(cuò)誤釋放內(nèi)存做檢查?

            C++ library 從代碼組織上有兩種形式:1) 以頭文件方式提供(如以 STL 和 Boost 為代表的模板庫(kù));2) 以頭文件+二進(jìn)制庫(kù)文件方式提供(大多數(shù)非模板庫(kù)以此方式發(fā)布)。

            對(duì)于純以頭文件方式實(shí)現(xiàn)的 library,那么你可以在你的程序的每個(gè) .cpp 文件的第一行包含重載 ::operator new 的頭文件,這樣程序里用到的其他 C++ library 也會(huì)轉(zhuǎn)而使用你的 ::operator new 來分配內(nèi)存。當(dāng)然這是一種相當(dāng)有侵略性的做法,如果運(yùn)氣好,編譯和運(yùn)行都沒問題;如果運(yùn)氣差一點(diǎn),可能會(huì)遇到編譯錯(cuò)誤,這其實(shí)還不算壞事;運(yùn)氣更差一點(diǎn),編譯沒有錯(cuò)誤,運(yùn)行的時(shí)候時(shí)不時(shí)出現(xiàn)非法訪問,導(dǎo)致 segment fault;或者在某些情況下你定制的分配策略與 library 有沖突,內(nèi)存數(shù)據(jù)損壞,出現(xiàn)莫名其妙的行為。

            對(duì)于以庫(kù)文件方式實(shí)現(xiàn)的 library,這么做并不能讓其受惠,因?yàn)?library 的源文件已經(jīng)編譯成了二進(jìn)制代碼,它不會(huì)調(diào)用你新重載的 ::operator new(想想看,已經(jīng)編譯的二進(jìn)制代碼怎么可能提供額外的 new (__FILE__, __LINE__) 參數(shù)呢?)更麻煩的是,如果某些頭文件有 inline function,還會(huì)引起詭異的“串?dāng)_”。即 library 有的部分用了你的分配器,有的部分用了系統(tǒng)默認(rèn)的分配器,然后在釋放內(nèi)存的時(shí)候沒有給對(duì)地方,造成分配器的數(shù)據(jù)結(jié)構(gòu)被破壞。

            總之,第二種重載方式看似功能更豐富,但其實(shí)與程序里使用的其他 C++ library 很難無(wú)縫配合。

             

            綜上,對(duì)于現(xiàn)實(shí)生活中的 C++ 項(xiàng)目,重載 ::operator new() 幾乎沒有用武之地,因?yàn)楹茈y處理好與程序所用的 C++ library 的關(guān)系,畢竟大多數(shù) library 在設(shè)計(jì)的時(shí)候沒有考慮到你會(huì)重載 ::operator new() 并強(qiáng)塞給它。

            如果確實(shí)需要定制內(nèi)存分配,該如何辦?

            替代辦法

            很簡(jiǎn)單,替換 malloc。如果需要,直接從 malloc 層面入手,通過 LD_PRELOAD 來加載一個(gè) .so,其中有 malloc/free 的替代實(shí)現(xiàn)(drop-in replacement),這樣能同時(shí)為 C 和 C++ 代碼服務(wù),而且避免 C++ 重載 ::operator new() 的陰暗角落。

            對(duì)于“檢測(cè)內(nèi)存錯(cuò)誤”這一用法,我們可以用 valgrind 或者 dmalloc 或者 efence 來達(dá)到相同的目的,專業(yè)的除錯(cuò)工具比自己山寨一個(gè)內(nèi)存檢查器要靠譜。

            對(duì)于“統(tǒng)計(jì)內(nèi)存使用數(shù)據(jù)”,替換 malloc 同樣能得到足夠的信息,因?yàn)槲覀兛梢杂?backtrace() 函數(shù)來獲得調(diào)用棧,這比 new (__FILE__, __LINE__) 的信息更豐富。比方說你通過分析 (__FILE__, __LINE__) 發(fā)現(xiàn) std::string 大量分配釋放內(nèi)存,有超出預(yù)期的開銷,但是你卻不知道代碼里哪一部分在反復(fù)創(chuàng)建和銷毀 std::string 對(duì)象,因?yàn)?(__FILE__, __LINE__) 只能告訴你最內(nèi)層的調(diào)用函數(shù)。用 backtrace() 能找到真正的發(fā)起調(diào)用者。

            對(duì)于“性能優(yōu)化”這一用法,我認(rèn)為這目前的多線程開發(fā)中,自己實(shí)現(xiàn)一個(gè)能打敗系統(tǒng)默認(rèn)的 malloc 的內(nèi)存分配器是不現(xiàn)實(shí)的。一個(gè)通用的內(nèi)存分配器本來就有相當(dāng)?shù)碾y度,為多線程程序?qū)崿F(xiàn)一個(gè)安全和高效的通用(全局)內(nèi)存分配器超出了一般開發(fā)人員的能力。不如使用現(xiàn)有的針對(duì)多核多線程優(yōu)化的 malloc,例如 Google tcmalloc 和 Intel TBB 2.2 里的內(nèi)存分配器。好在這些 allocator 都不是侵入式的,也無(wú)須重載 ::operator new()。

            為單獨(dú)的 class 重載 operator new() 有問題嗎?

            與全局 ::operator new() 不同,per-class operator new() 和 operator delete () 的影響面要小得多,它只影響本 class 及其派生類。似乎重載 member operator new() 是可行的。我對(duì)此持反對(duì)態(tài)度。

            如果一個(gè) class Node 需要重載 member operator new(),說明它用到了特殊的內(nèi)存分配策略,常見的情況是使用了內(nèi)存池或?qū)ο蟪亍N覍幵赴堰@一事實(shí)明顯地?cái)[出來,而不是改變 new Node 的默認(rèn)行為。具體地說,是用 factory 來創(chuàng)建對(duì)象,比如 static Node* Node::createNode() 或者 static shared_ptr<Node> Node::createNode();。

            這可以歸結(jié)為最小驚訝原則:如果我在代碼里讀到 Node* p = new Node,我會(huì)認(rèn)為它在 heap 上分配了內(nèi)存,如果 Node class 重載了 member operator new(),那么我要事先仔細(xì)閱讀 node.h 才能發(fā)現(xiàn)其實(shí)這行代碼使用了私有的內(nèi)存池。為什么不寫得明確一點(diǎn)呢?寫成 Node* p = Node::createNode(),那么我能猜到 Node::createNode() 肯定做了什么與 new Node 不一樣的事情,免得將來大吃一驚。

            The Zen of Python 說 explicit is better than implicit,我深信不疑。

             

            總結(jié):重載 ::operator new() 或許在某些臨時(shí)的場(chǎng)合能應(yīng)個(gè)急,但是不應(yīng)該作為一種策略來使用。如果需要,我們可以從 malloc 層面入手,徹底而全面地替換內(nèi)存分配器。

            參考文獻(xiàn):

            [1] 侯捷,《池內(nèi)春秋—— Memory Pool 的設(shè)計(jì)哲學(xué)與無(wú)痛運(yùn)用》,《程序員》2002 年第 9 期。

            posted on 2011-02-22 01:02 陳碩 閱讀(20116) 評(píng)論(12)  編輯 收藏 引用

            評(píng)論

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new()[未登錄] 2011-02-22 07:10 欲三更

            不同模塊的內(nèi)存問題,我覺得這簡(jiǎn)直是一個(gè)c++設(shè)計(jì)欠考慮的地方。就算是不重載new也不見得沒問題。只要不是自己源碼編譯的模塊都不保險(xiǎn)。  回復(fù)  更多評(píng)論   

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new() 2011-02-22 10:00 空明流轉(zhuǎn)

            allocator的作用很重要。  回復(fù)  更多評(píng)論   

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new() 2011-02-22 13:41 陳梓瀚(vczh)

            VC++內(nèi)置內(nèi)存跟蹤工具  回復(fù)  更多評(píng)論   

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new() 2011-02-22 15:15 泛泛

            不錯(cuò)~~~~~~~~~~~~~~~~~~  回復(fù)  更多評(píng)論   

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new()[未登錄] 2011-02-22 16:01 ccc

            寫的很好  回復(fù)  更多評(píng)論   

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new() 2011-02-22 17:02 zwp

            受教了:)  回復(fù)  更多評(píng)論   

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new()[未登錄] 2011-02-22 21:10 xxx

            被同事的重載operator new害過的人路過。  回復(fù)  更多評(píng)論   

            # Term Paper Writing Service 2011-03-12 13:52 Term Paper Writing Service

            Writing Services to help students write essays, term papers, research papers. Buy custom research essay, custom term papers, thesis and dissertation writing.
              回復(fù)  更多評(píng)論   

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new()[未登錄] 2011-12-22 14:09 Chipset

            內(nèi)存管理系統(tǒng)的事情,對(duì)C++要求這么高,是不是太過分了。new就是一個(gè)殼,調(diào)了API。當(dāng)然,最好別重載全局new,局部的我看也最好別重載。一般程序new就足矣,多CPU并行和吞吐量的程序,只能自己來管內(nèi)存,否則慢也就罷了,還出一堆碎片。

            系統(tǒng)管理內(nèi)存都做不到完美,指望語(yǔ)言級(jí)別依賴于實(shí)現(xiàn)的new能做好?看看系統(tǒng)內(nèi)核每次升級(jí),有多少改動(dòng)發(fā)生在虛擬內(nèi)存管理器...  回復(fù)  更多評(píng)論   

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new() 2012-01-13 21:32 幻の上帝

            不重載class scope operator new這種自我閹割的限制性手法居然成了符合最小驚訝?呵呵。我倒是挺愿意打那些既沒學(xué)好基本語(yǔ)義又懶得看清楚文檔的家伙們的臉的。
            Explicit is better than implicit,雖然原意是沒什么錯(cuò),嘛,某些人就是擺脫不了用得羅嗦的原罪。要在這方面鼓吹這個(gè)是拉攏BASIC用戶么。
              回復(fù)  更多評(píng)論   

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new() 2013-07-12 18:15 thomax

            great content  回復(fù)  更多評(píng)論   

            # re: C++ 工程實(shí)踐(2):不要重載全局 ::operator new() 2013-07-12 20:17 david parker

            nice post  回復(fù)  更多評(píng)論   


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            <2015年10月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            導(dǎo)航

            統(tǒng)計(jì)

            常用鏈接

            隨筆分類

            隨筆檔案

            相冊(cè)

            搜索

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            1000部精品久久久久久久久| 久久久久久A亚洲欧洲AV冫| 久久免费精品一区二区| 国产成人精品白浆久久69 | 日日噜噜夜夜狠狠久久丁香五月| 亚洲精品美女久久久久99| 亚洲精品无码专区久久久| 午夜精品久久久久| 久久亚洲欧美国产精品| 久久亚洲精品中文字幕| 午夜久久久久久禁播电影| 午夜精品久久久久久久久| 久久精品国产99久久无毒不卡| 久久996热精品xxxx| 一本色道久久综合狠狠躁篇 | 国产精品久久免费| 国产精品女同久久久久电影院| 久久久久免费看成人影片| 国产精品久久网| 久久天天躁狠狠躁夜夜av浪潮| 99久久国产精品免费一区二区| 久久免费小视频| 国产精品gz久久久| 99久久国语露脸精品国产| 99久久成人18免费网站| 99国产欧美久久久精品蜜芽| 久久狠狠色狠狠色综合| 久久精品无码午夜福利理论片 | 久久久老熟女一区二区三区| 99国产欧美久久久精品蜜芽| 久久青青草原精品国产不卡| 亚洲va久久久噜噜噜久久天堂| 久久久久综合网久久| 亚洲国产成人精品无码久久久久久综合 | 亚洲中文久久精品无码ww16| 久久99精品久久久久久hb无码 | 国产香蕉久久精品综合网| 免费国产99久久久香蕉| 亚洲色欲久久久久综合网| 99久久99久久| 色欲综合久久中文字幕网|