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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

从实际案例聊聊Java应用的GC优化--转

發布時間:2025/4/5 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从实际案例聊聊Java应用的GC优化--转 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

https://tech.meituan.com/jvm_optimize.html

當Java程序性能達不到既定目標,且其他優化手段都已經窮盡時,通常需要調整垃圾回收器來進一步提高性能,稱為GC優化。但GC算法復雜,影響GC性能的參數眾多,且參數調整又依賴于應用各自的特點,這些因素很大程度上增加了GC優化的難度。即便如此,GC調優也不是無章可循,仍然有一些通用的思考方法。本篇會介紹這些通用的GC優化策略和相關實踐案例,主要包括如下內容:

優化前準備: 簡單回顧JVM相關知識、介紹GC優化的一些通用策略。
優化方法: 介紹調優的一般流程:明確優化目標→優化→跟蹤優化結果。
優化案例: 簡述筆者所在團隊遇到的GC問題以及優化方案。

一、優化前的準備

GC優化需知

為了更好地理解本篇所介紹的內容,你需要了解如下內容。

  • GC相關基礎知識,包括但不限于:
    a) GC工作原理。
    b) 理解新生代、老年代、晉升等術語含義。
    c) 可以看懂GC日志。

  • GC優化不能解決一切性能問題,它是最后的調優手段。

  • 如果對第一點中提及的知識點不是很熟悉,可以先閱讀小結-JVM基礎回顧;如果已經很熟悉,可以跳過該節直接往下閱讀。

    JVM基礎回顧

    JVM內存結構

    簡單介紹一下JVM內存結構和常見的垃圾回收器。

    當代主流虛擬機(Hotspot VM)的垃圾回收都采用“分代回收”的算法。“分代回收”是基于這樣一個事實:對象的生命周期不同,所以針對不同生命周期的對象可以采取不同的回收方式,以便提高回收效率。

    Hotspot VM將內存劃分為不同的物理區,就是“分代”思想的體現。如圖所示,JVM內存主要由新生代、老年代、永久代構成。

    ① 新生代(Young Generation):大多數對象在新生代中被創建,其中很多對象的生命周期很短。每次新生代的垃圾回收(又稱Minor GC)后只有少量對象存活,所以選用復制算法,只需要少量的復制成本就可以完成回收。

    新生代內又分三個區:一個Eden區,兩個Survivor區(一般而言),大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被復制到兩個Survivor區(中的一個)。當這個Survivor區滿時,此區的存活且不滿足“晉升”條件的對象將被復制到另外一個Survivor區。對象每經歷一次Minor GC,年齡加1,達到“晉升年齡閾值”后,被放到老年代,這個過程也稱為“晉升”。顯然,“晉升年齡閾值”的大小直接影響著對象在新生代中的停留時間,在Serial和ParNew GC兩種回收器中,“晉升年齡閾值”通過參數MaxTenuringThreshold設定,默認值為15。

    ② 老年代(Old Generation):在新生代中經歷了N次垃圾回收后仍然存活的對象,就會被放到年老代,該區域中對象存活率高。老年代的垃圾回收(又稱Major GC)通常使用“標記-清理”或“標記-整理”算法。整堆包括新生代和老年代的垃圾回收稱為Full GC(HotSpot VM里,除了CMS之外,其它能收集老年代的GC都會同時收集整個GC堆,包括新生代)。

    ③ 永久代(Perm Generation):主要存放元數據,例如Class、Method的元信息,與垃圾回收要回收的Java對象關系不大。相對于新生代和年老代來說,該區域的劃分對垃圾回收影響比較小。

    常見垃圾回收器

    不同的垃圾回收器,適用于不同的場景。常用的垃圾回收器:

    • 串行(Serial)回收器是單線程的一個回收器,簡單、易實現、效率高。
    • 并行(ParNew)回收器是Serial的多線程版,可以充分的利用CPU資源,減少回收的時間。
    • 吞吐量優先(Parallel Scavenge)回收器,側重于吞吐量的控制。
    • 并發標記清除(CMS,Concurrent Mark Sweep)回收器是一種以獲取最短回收停頓時間為目標的回收器,該回收器是基于“標記-清除”算法實現的。

    GC日志

    每一種回收器的日志格式都是由其自身的實現決定的,換而言之,每種回收器的日志格式都可以不一樣。但虛擬機設計者為了方便用戶閱讀,將各個回收器的日志都維持一定的共性。JavaGC日志?中簡單介紹了這些共性。

    參數基本策略

    各分區的大小對GC的性能影響很大。如何將各分區調整到合適的大小,分析活躍數據的大小是很好的切入點。

    活躍數據的大小是指,應用程序穩定運行時長期存活對象在堆中占用的空間大小,也就是Full GC后堆中老年代占用空間的大小。可以通過GC日志中Full GC之后老年代數據大小得出,比較準確的方法是在程序穩定后,多次獲取GC數據,通過取平均值的方式計算活躍數據的大小。活躍數據和各分區之間的比例關系如下(見參考文獻1):

    空間倍數
    總大小3-4?倍活躍數據的大小
    新生代1-1.5?活躍數據的大小
    老年代2-3?倍活躍數據的大小
    永久代1.2-1.5?倍Full GC后的永久代空間占用

    例如,根據GC日志獲得老年代的活躍數據大小為300M,那么各分區大小可以設為:

    總堆:1200MB = 300MB × 4
    新生代:450MB = 300MB × 1.5
    老年代: 750MB = 1200MB - 450MB*

    這部分設置僅僅是堆大小的初始值,后面的優化中,可能會調整這些值,具體情況取決于應用程序的特性和需求。

    二、優化步驟

    GC優化一般步驟可以概括為:確定目標、優化參數、驗收結果。

    確定目標

    明確應用程序的系統需求是性能優化的基礎,系統的需求是指應用程序運行時某方面的要求,譬如:

    • 高可用,可用性達到幾個9。
    • 低延遲,請求必須多少毫秒內完成響應。
    • 高吞吐,每秒完成多少次事務。

    明確系統需求之所以重要,是因為上述性能指標間可能沖突。比如通常情況下,縮小延遲的代價是降低吞吐量或者消耗更多的內存或者兩者同時發生。

    由于筆者所在團隊主要關注高可用和低延遲兩項指標,所以接下來分析,如何量化GC時間和頻率對于響應時間和可用性的影響。通過這個量化指標,可以計算出當前GC情況對服務的影響,也能評估出GC優化后對響應時間的收益,這兩點對于低延遲服務很重要。

    舉例:假設單位時間T內發生一次持續25ms的GC,接口平均響應時間為50ms,且請求均勻到達,根據下圖所示:

    那么有(50ms+25ms)/T比例的請求會受GC影響,其中GC前的50ms內到達的請求都會增加25ms,GC期間的25ms內到達的請求,會增加0-25ms不等,如果時間T內發生N次GC,受GC影響請求占比=(接口響應時間+GC時間)×N/T?。可見無論降低單次GC時間還是降低GC次數N都可以有效減少GC對響應時間的影響。

    優化

    通過收集GC信息,結合系統需求,確定優化方案,例如選用合適的GC回收器、重新設置內存比例、調整JVM參數等。

    進行調整后,將不同的優化方案分別應用到多臺機器上,然后比較這些機器上GC的性能差異,有針對性的做出選擇,再通過不斷的試驗和觀察,找到最合適的參數。

    驗收優化結果

    將修改應用到所有服務器,判斷優化結果是否符合預期,總結相關經驗。

    接下來,我們通過三個案例來實踐以上的優化流程和基本原則(本文中三個案例使用的垃圾回收器均為ParNew+CMS,CMS失敗時Serial Old替補)。

    三、GC優化案例

    案例一 Major GC和Minor GC頻繁

    確定目標

    服務情況:Minor GC每分鐘100次 ,Major GC每4分鐘一次,單次Minor GC耗時25ms,單次Major GC耗時200ms,接口響應時間50ms。

    由于這個服務要求低延時高可用,結合上文中提到的GC對服務響應時間的影響,計算可知由于Minor GC的發生,12.5%的請求響應時間會增加,其中8.3%的請求響應時間會增加25ms,可見當前GC情況對響應時間影響較大。

    (50ms+25ms)× 100次/60000ms = 12.5%,50ms × 100次/60000ms = 8.3%?。

    優化目標:降低TP99、TP90時間。

    優化

    首先優化Minor GC頻繁問題。通常情況下,由于新生代空間較小,Eden區很快被填滿,就會導致頻繁Minor GC,因此可以通過增大新生代空間來降低Minor GC的頻率。例如在相同的內存分配率的前提下,新生代中的Eden區增加一倍,Minor GC的次數就會減少一半。

    這時很多人有這樣的疑問,擴容Eden區雖然可以減少Minor GC的次數,但會增加單次Minor GC時間么?根據上面公式,如果單次Minor GC時間也增加,很難保證最后的優化效果。我們結合下面情況來分析,單次Minor GC時間主要受哪些因素影響?是否和新生代大小存在線性關系?
    首先,單次Minor GC時間由以下兩部分組成:T1(掃描新生代)和 T2(復制存活對象到Survivor區)如下圖。(注:這里為了簡化問題,我們認為T1只掃描新生代判斷對象是否存活的時間,其實該階段還需要掃描部分老年代,后面案例中有詳細描述。)

    • 擴容前:新生代容量為R ,假設對象A的存活時間為750ms,Minor GC間隔500ms,那么本次Minor GC時間= T1(掃描新生代R)+T2(復制對象A到S)。

    • 擴容后:新生代容量為2R ,對象A的生命周期為750ms,那么Minor GC間隔增加為1000ms,此時Minor GC對象A已不再存活,不需要把它復制到Survivor區,那么本次GC時間 = 2 × T1(掃描新生代R),沒有T2復制時間。

    可見,擴容后,Minor GC時增加了T1(掃描時間),但省去T2(復制對象)的時間,更重要的是對于虛擬機來說,復制對象的成本要遠高于掃描成本,所以,單次Minor GC時間更多取決于GC后存活對象的數量,而非Eden區的大小。因此如果堆中短期對象很多,那么擴容新生代,單次Minor GC時間不會顯著增加。下面需要確認下服務中對象的生命周期分布情況:

    通過上圖GC日志中兩處紅色框標記內容可知:

  • new threshold = 2(動態年齡判斷,對象的晉升年齡閾值為2),對象僅經歷2次Minor GC后就晉升到老年代,這樣老年代會迅速被填滿,直接導致了頻繁的Major GC。
  • Major GC后老年代使用空間為300M+,意味著此時絕大多數(86% = 2G/2.3G)的對象已經不再存活,也就是說生命周期長的對象占比很小。
  • 由此可見,服務中存在大量短期臨時對象,擴容新生代空間后,Minor GC頻率降低,對象在新生代得到充分回收,只有生命周期長的對象才進入老年代。這樣老年代增速變慢,Major GC頻率自然也會降低。

    優化結果

    通過擴容新生代為為原來的三倍,單次Minor GC時間增加小于5ms,頻率下降了60%,服務響應時間TP90,TP99都下降了10ms+,服務可用性得到提升。

    調整前:

    調整后:

    小結

    如何選擇各分區大小應該依賴應用程序中對象生命周期的分布情況:如果應用存在大量的短期對象,應該選擇較大的年輕代;如果存在相對較多的持久對象,老年代應該適當增大。

    更多思考

    關于上文中提到晉升年齡閾值為2,很多同學有疑問,為什么設置了MaxTenuringThreshold=15,對象仍然僅經歷2次Minor GC,就晉升到老年代?這里涉及到“動態年齡計算”的概念。

    動態年齡計算:Hotspot遍歷所有對象時,按照年齡從小到大對其所占用的大小進行累積,當累積的某個年齡大小超過了survivor區的一半時,取這個年齡和MaxTenuringThreshold中更小的一個值,作為新的晉升年齡閾值。在本案例中,調優前:Survivor區 = 64M,desired survivor = 32M,此時Survivor區中age<=2的對象累計大小為41M,41M大于32M,所以晉升年齡閾值被設置為2,下次Minor GC時將年齡超過2的對象被晉升到老年代。

    JVM引入動態年齡計算,主要基于如下兩點考慮:

  • 如果固定按照MaxTenuringThreshold設定的閾值作為晉升條件:
    a)MaxTenuringThreshold設置的過大,原本應該晉升的對象一直停留在Survivor區,直到Survivor區溢出,一旦溢出發生,Eden+Svuvivor中對象將不再依據年齡全部提升到老年代,這樣對象老化的機制就失效了。
    b)MaxTenuringThreshold設置的過小,“過早晉升”即對象不能在新生代充分被回收,大量短期對象被晉升到老年代,老年代空間迅速增長,引起頻繁的Major GC。分代回收失去了意義,嚴重影響GC性能。

  • 相同應用在不同時間的表現不同:特殊任務的執行或者流量成分的變化,都會導致對象的生命周期分布發生波動,那么固定的閾值設定,因為無法動態適應變化,會造成和上面相同的問題。

  • 總結來說,為了更好的適應不同程序的內存情況,虛擬機并不總是要求對象年齡必須達到Maxtenuringthreshhold再晉級老年代。

    案例二 請求高峰期發生GC,導致服務可用性下降

    確定目標

    GC日志顯示,高峰期CMS在重標記(Remark)階段耗時1.39s。Remark階段是Stop-The-World(以下簡稱為STW)的,即在執行垃圾回收時,Java應用程序中除了垃圾回收器線程之外其他所有線程都被掛起,意味著在此期間,用戶正常工作的線程全部被暫停下來,這是低延時服務不能接受的。本次優化目標是降低Remark時間。

    優化

    解決問題前,先回顧一下CMS的四個主要階段,以及各個階段的工作內容。下圖展示了CMS各個階段可以標記的對象,用不同顏色區分。

  • Init-mark初始標記(STW) ,該階段進行可達性分析,標記GC ROOT能直接關聯到的對象,所以很快。
  • Concurrent-mark并發標記,由前階段標記過的綠色對象出發,所有可到達的對象都在本階段中標記。
  • Remark重標記(STW) ,暫停所有用戶線程,重新掃描堆中的對象,進行可達性分析,標記活著的對象。因為并發標記階段是和用戶線程并發執行的過程,所以該過程中可能有用戶線程修改某些活躍對象的字段,指向了一個未標記過的對象,如下圖中紅色對象在并發標記開始時不可達,但是并行期間引用發生變化,變為對象可達,這個階段需要重新標記出此類對象,防止在下一階段被清理掉,這個過程也是需要STW的。特別需要注意一點,這個階段是以新生代中對象為根來判斷對象是否存活的。
  • 并發清理,進行并發的垃圾清理。
  • 可見,Remark階段主要是通過掃描堆來判斷對象是否存活。那么準確判斷對象是否存活,需要掃描哪些對象?CMS對老年代做回收,Remark階段僅掃描老年代是否可行?結論是不可行,原因如下:

    如果僅掃描老年代中對象,即以老年代中對象為根,判斷對象是否存在引用,上圖中,對象A因為引用存在新生代中,它在Remark階段就不會被修正標記為可達,GC時會被錯誤回收。
    新生代對象持有老年代中對象的引用,這種情況稱為“跨代引用”。因它的存在,Remark階段必須掃描整個堆來判斷對象是否存活,包括圖中灰色的不可達對象。

    灰色對象已經不可達,但仍然需要掃描的原因:新生代GC和老年代的GC是各自分開獨立進行的,只有Minor GC時才會使用根搜索算法,標記新生代對象是否可達,也就是說雖然一些對象已經不可達,但在Minor GC發生前不會被標記為不可達,CMS也無法辨認哪些對象存活,只能全堆掃描(新生代+老年代)。由此可見堆中對象的數目影響了Remark階段耗時。
    分析GC日志可以得出同樣的規律,Remark耗時>500ms時,新生代使用率都在75%以上。這樣降低Remark階段耗時問題轉換成如何減少新生代對象數量。

    新生代中對象的特點是“朝生夕滅”,這樣如果Remark前執行一次Minor GC,大部分對象就會被回收。CMS就采用了這樣的方式,在Remark前增加了一個可中斷的并發預清理(CMS-concurrent-abortable-preclean),該階段主要工作仍然是并發標記對象是否存活,只是這個過程可被中斷。此階段在Eden區使用超過2M時啟動,當然2M是默認的閾值,可以通過參數修改。如果此階段執行時等到了Minor GC,那么上述灰色對象將被回收,Reamark階段需要掃描的對象就少了。

    除此之外CMS為了避免這個階段沒有等到Minor GC而陷入無限等待,提供了參數CMSMaxAbortablePrecleanTime ,默認為5s,含義是如果可中斷的預清理執行超過5s,不管發沒發生Minor GC,都會中止此階段,進入Remark。
    根據GC日志紅色標記2處顯示,可中斷的并發預清理執行了5.35s,超過了設置的5s被中斷,期間沒有等到Minor GC ,所以Remark時新生代中仍然有很多對象。

    對于這種情況,CMS提供CMSScavengeBeforeRemark參數,用來保證Remark前強制進行一次Minor GC。

    優化結果

    經過增加CMSScavengeBeforeRemark參數,單次執行時間>200ms的GC停頓消失,從監控上觀察,GCtime和業務波動保持一致,不再有明顯的毛刺。

    小結

    通過案例分析了解到,由于跨代引用的存在,CMS在Remark階段必須掃描整個堆,同時為了避免掃描時新生代有很多對象,增加了可中斷的預清理階段用來等待Minor GC的發生。只是該階段有時間限制,如果超時等不到Minor GC,Remark時新生代仍然有很多對象,我們的調優策略是,通過參數強制Remark前進行一次Minor GC,從而降低Remark階段的時間。

    更多思考

    案例中只涉及老年代GC,其實新生代GC存在同樣的問題,即老年代可能持有新生代對象引用,所以Minor GC時也必須掃描老年代。

    JVM是如何避免Minor GC時掃描全堆的?
    經過統計信息顯示,老年代持有新生代對象引用的情況不足1%,根據這一特性JVM引入了卡表(card table)來實現這一目的。如下圖所示:

    卡表的具體策略是將老年代的空間分成大小為512B的若干張卡(card)。卡表本身是單字節數組,數組中的每個元素對應著一張卡,當發生老年代引用新生代時,虛擬機將該卡對應的卡表元素設置為適當的值。如上圖所示,卡表3被標記為臟(卡表還有另外的作用,標識并發標記階段哪些塊被修改過),之后Minor GC時通過掃描卡表就可以很快的識別哪些卡中存在老年代指向新生代的引用。這樣虛擬機通過空間換時間的方式,避免了全堆掃描。

    總結來說,CMS的設計聚焦在獲取最短的時延,為此它“不遺余力”地做了很多工作,包括盡量讓應用程序和GC線程并發、增加可中斷的并發預清理階段、引入卡表等,雖然這些操作犧牲了一定吞吐量但獲得了更短的回收停頓時間。

    案例三 發生Stop-The-World的GC

    確定目標

    GC日志如下圖(在GC日志中,Full GC是用來說明這次垃圾回收的停頓類型,代表STW類型的GC,并不特指老年代GC),根據GC日志可知本次Full GC耗時1.23s。這個在線服務同樣要求低時延高可用。本次優化目標是降低單次STW回收停頓時間,提高可用性。

    優化

    首先,什么時候可能會觸發STW的Full GC呢?

  • Perm空間不足;
  • CMS GC時出現promotion failed和concurrent mode failure(concurrent mode failure發生的原因一般是CMS正在進行,但是由于老年代空間不足,需要盡快回收老年代里面的不再被使用的對象,這時停止所有的線程,同時終止CMS,直接進行Serial Old GC);
  • 統計得到的Young GC晉升到老年代的平均大小大于老年代的剩余空間;
  • 主動觸發Full GC(執行jmap -histo:live [pid])來避免碎片問題。
  • 然后,我們來逐一分析一下:

    • 排除原因2:如果是原因2中兩種情況,日志中會有特殊標識,目前沒有。
    • 排除原因3:根據GC日志,當時老年代使用量僅為20%,也不存在大于2G的大對象產生。
    • 排除原因4:因為當時沒有相關命令執行。
    • 鎖定原因1:根據日志發現Full GC后,Perm區變大了,推斷是由于永久代空間不足容量擴展導致的。

    找到原因后解決方法有兩種:

  • 通過把-XX:PermSize參數和-XX:MaxPermSize設置成一樣,強制虛擬機在啟動的時候就把永久代的容量固定下來,避免運行時自動擴容。
  • CMS默認情況下不會回收Perm區,通過參數CMSPermGenSweepingEnabled、CMSClassUnloadingEnabled ,可以讓CMS在Perm區容量不足時對其回收。
  • 由于該服務沒有生成大量動態類,回收Perm區收益不大,所以我們采用方案1,啟動時將Perm區大小固定,避免進行動態擴容。

    優化結果

    調整參數后,服務不再有Perm區擴容導致的STW GC發生。

    小結

    對于性能要求很高的服務,建議將MaxPermSize和MinPermSize設置成一致(JDK8開始,Perm區完全消失,轉而使用元空間。而元空間是直接存在內存中,不在JVM中),Xms和Xmx也設置為相同,這樣可以減少內存自動擴容和收縮帶來的性能損失。虛擬機啟動的時候就會把參數中所設定的內存全部化為私有,即使擴容前有一部分內存不會被用戶代碼用到,這部分內存在虛擬機中被標識為虛擬內存,也不會交給其他進程使用。

    四、總結

    結合上述GC優化案例做個總結:

  • 首先再次聲明,在進行GC優化之前,需要確認項目的架構和代碼等已經沒有優化空間。我們不能指望一個系統架構有缺陷或者代碼層次優化沒有窮盡的應用,通過GC優化令其性能達到一個質的飛躍。
  • 其次,通過上述分析,可以看出虛擬機內部已有很多優化來保證應用的穩定運行,所以不要為了調優而調優,不當的調優可能適得其反。
  • 最后,GC優化是一個系統而復雜的工作,沒有萬能的調優策略可以滿足所有的性能指標。GC優化必須建立在我們深入理解各種垃圾回收器的基礎上,才能有事半功倍的效果。
  • 本文中案例均來北京業務安全中心(也稱風控)對接服務的實踐經驗。同時感謝風控的小伙伴們,是他們專業負責的審閱,才讓這篇文章更加完善。對于本文中涉及到的內容,歡迎大家指正和補充。

    參考文獻

  • Scott O. Java Performance:The Definitive Guide. O'Reilly, 2014.
  • 周志明,深入理解Java虛擬機[M],機械工業出版社,2013.
  • CMS垃圾回收機制.
  • 轉載于:https://www.cnblogs.com/davidwang456/p/8745403.html

    總結

    以上是生活随笔為你收集整理的从实际案例聊聊Java应用的GC优化--转的全部內容,希望文章能夠幫你解決所遇到的問題。

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

    主站蜘蛛池模板: 男人的天堂99 | 热の国产 | 亚洲777| 中文字幕第31页 | 视频1区2区 | 精品国产一区二区视频 | 色香视频首页 | 欧美三级午夜理伦三级老人 | 亚洲精品国产精品乱码不99 | 99精品视频在线观看免费 | 国产学生美女无遮拦高潮视频 | 欧美三级黄色大片 | 日本在线中文字幕专区 | av中亚 | 野花中文免费观看6 | 亚洲天天视频 | 欧美一区二区福利 | 日本三级在线 | 成人高清在线观看 | 久久国产剧情 | 思思精品视频 | 插入综合网 | 黄频网站在线观看 | av少妇在线| 男生坤坤放进女生坤坤里 | 久久精品成人 | 少妇大叫太粗太大爽一区二区 | 色哟哟一区二区三区 | 午夜一区二区三区 | 伊人在线| 久久精品大全 | 超碰自拍 | 一区二区三区高清在线观看 | 一级不卡毛片 | 亚洲性网 | 免费观看av毛片 | 国产调教在线观看 | 无遮挡在线观看 | 九九热在线视频播放 | 内射一区二区三区 | 中出视频在线观看 | 91口爆一区二区三区在线 | 午夜国产 | 国产精品7777| 中文字幕一区二区在线观看视频 | 求个黄色网址 | 香蕉视频一区二区三区 | 免费的av片| 久久在线免费观看视频 | 亚洲天堂123 | 西欧free性满足hd老熟妇 | 综合天天色 | 国产精品国产三级国产普通话蜜臀 | 自偷自拍亚洲 | 操少妇视频 | 成人免费aaa | 美女校花脱精光 | 寡妇一级片 | 午夜激情国产 | 婷婷色六月 | 特级大胆西西4444人体 | 青娱乐精品视频 | 草草影院一区二区三区 | 少妇高潮一区二区三区99刮毛 | 91大神小宝寻花在线观看 | 就要爱爱tv | 欧美不卡在线视频 | 性史性dvd影片农村毛片 | 精品少妇人妻av一区二区三区 | 黄色永久网站 | 亚洲不卡中文字幕 | 婷婷九月丁香 | 久久九九免费视频 | 很色的网站 | 婷婷综合激情 | 日本乱淫视频 | 玖玖爱在线精品视频 | 久操青青 | 黄色在线观看视频网站 | 波多野结衣一区二区三区四区 | 日本一级淫片色费放 | 国产专区视频 | 国产精品久久久久久久久毛片 | 亚洲av无码专区国产乱码不卡 | 色导航在线 | 国产麻豆91视频 | 日韩视频免费在线观看 | 91桃色污| 男人插女人免费视频 | 久久精品免费av | 国产成人小视频 | 欧美成人精品欧美一级乱黄 | 日本韩国欧美一区二区三区 | 校园春色综合网 | 天天想你在线观看完整版电影高清 | 欧美成人国产va精品日本一级 | 黄色一级在线播放 | 亚洲激情片 | 手机成人av |