成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Full GC 頻率優(yōu)化實(shí)戰(zhàn)

開發(fā)
本文介紹了游戲業(yè)務(wù)使用MAT和GC日志等工具對 Full GC頻率進(jìn)行優(yōu)化的過程。

一、背景

圖片

圖片

游戲業(yè)務(wù)面對用戶端的某個(gè)工程,每天Full GC頻率達(dá)到120次,業(yè)務(wù)高峰期每7分鐘就會(huì)有一次Full GC。為了避免情況持續(xù)變差,最大程度減少對系統(tǒng)響應(yīng)時(shí)間的負(fù)面影響,需要對該工程的Full GC頻率進(jìn)行優(yōu)化。

該項(xiàng)目JDK版本為1.8,老年代使用CMS作為垃圾回收器,優(yōu)化前的部分啟動(dòng)參數(shù)如下:

-Xms4608M -Xmx4608M -Xmn2048M -XX:MetaspaceSize=320M -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFractinotallow=92 -XX:+UseCMSInitiatingOccupancyOnly

二、工具介紹

在本次優(yōu)化過程中,我們主要使用了MAT和GC日志作為排查工具。MAT是一個(gè)功能強(qiáng)大的內(nèi)存分析工具,而GC日志則用于記錄Java虛擬機(jī)中的垃圾回收行為和內(nèi)存情況。這兩者結(jié)合起來,能夠幫助開發(fā)人員深入分析程序的內(nèi)存使用情況,并進(jìn)行相應(yīng)的優(yōu)化。下文將詳細(xì)的介紹這兩種工具的使用方法,以及對應(yīng)的優(yōu)化案例。

2.1 MAT(Memory Analyzer Tool)

Eclipse Memory Analyzer Tool(MAT)是一個(gè)開源的Java堆轉(zhuǎn)儲(chǔ)分析工具。它旨在幫助開發(fā)人員識別和消除Java堆中的內(nèi)存泄漏和優(yōu)化內(nèi)存使用。MAT允許用戶分析Java堆轉(zhuǎn)儲(chǔ)文件,識別對象的內(nèi)存占用情況,查找潛在的內(nèi)存泄漏和冗余對象,以便執(zhí)行一些內(nèi)存優(yōu)化。

使用MAT打開dump文件后,首先進(jìn)入的是上圖頁面,此頁面會(huì)顯示dump包的縮略概覽信息,包括堆大小,類數(shù)量,對象數(shù)量等信息。其中的Biggest Objects By Retained Size和Leak Suspects在問題明顯時(shí)會(huì)比較有用,但對相對復(fù)雜的問題來說幫助不大。筆者比較常用的是下面這幾個(gè)功能,下文將依次介紹:

2.1.1 Dominator Tree

(1)功能

展示對象的支配關(guān)系。對象A支配對象B代表從GC Root(也不一定是GC ROOT,也可以是unreachable的起點(diǎn))達(dá)到對象B的所有路徑都必須經(jīng)過對象A,這也意味著對象A被垃圾回收后,對象B也會(huì)被回收。

這個(gè)功能相較于下面的Histogram更強(qiáng)調(diào)對象的引用關(guān)系,此外還可以通過Group By Class/Group By Package/Group By ClassLoader來進(jìn)一步的聚合對象。

MAT的各種圖標(biāo)中會(huì)頻繁的出現(xiàn)Shallow Heap Size和Retained Heap Size這兩個(gè)名詞,其含義如下:

  • Shallow Heap Size:這個(gè)對象自身在堆中的大小
  • Retained Heap Size:這個(gè)對象被垃圾回收后會(huì)釋放的堆內(nèi)存大小

上圖中,情況1里對象A的Retained Heap Size = A的Shallow Heap Size + B的Shallow Heap Size +C的Shallow Heap Size,情況2里對象A的Retained Heap Size = A的Shallow Heap Size + B的Shallow Heap Size。

(2)使用方法

①上圖為Group By Class的dominator tree。剛打開dominator tree時(shí)默認(rèn)是不進(jìn)行g(shù)roup的,此時(shí)可以排查單個(gè)大對象,排查完單個(gè)大對象后,需要將對象Group一下才能進(jìn)行下一步的排查。

② 從上圖可以看出,這個(gè)堆內(nèi)的對象內(nèi)存占用比較分散,說明導(dǎo)致問題的原因可能不止一個(gè),這種情況下只能結(jié)合自身業(yè)務(wù)逐個(gè)排查內(nèi)存占用排在前面的對象。

③ 對這些可疑的對象,右鍵類,選擇List objects → with outgoing references展開對象列表,查看這類對象具體存了什么,判斷這些對象的值是否可以再分類。

④ 根據(jù)對象的值,判斷對象的業(yè)務(wù)含義,確定是哪段代碼創(chuàng)建的對象。

⑤ 結(jié)合代碼,思考這類對象是在新生代還是老年代,如果能確定都在新生代,那這些對象一般不會(huì)導(dǎo)致老年代快速增長。

⑥如果在老年代,需要確定其是怎么從新生代晉升的,內(nèi)存占用是否有上限,上限是多少,一般多長時(shí)間能達(dá)到上限,再確定有沒有問題。

2.1.2 Histogram

功能

histogram可以顯示出各類對象的Shallow Heap Size和Retained Heap Size,Retained Heap Size默認(rèn)不展示,需要點(diǎn)擊菜單欄的Calculate Retained Size進(jìn)行計(jì)算,堆較大時(shí)計(jì)算耗時(shí)較長。

這張表一般和Dominator Tree結(jié)合使用,我們能看到char[]占用了較大的內(nèi)存,但由于Dominator Tree里聚合好的char[]都是頂層支配者,上層不會(huì)再有引用,有時(shí)無法直接確定這些對象曾經(jīng)被誰持有過,這時(shí)可以通過Histogram查看同類對象,找到相似的并且reachable的對象來確定這類對象是誰創(chuàng)建的。但是這一步其實(shí)可以通過oql解決,所以這張表在排查過程中的使用率其實(shí)沒有Histogram高。

2.1.3 OQL

功能

MAT提供的一種類似SQL的查詢語句,可以對對象進(jìn)行過濾。這篇官方文章里給了很多查詢語句樣例:https://wiki.eclipse.org/MemoryAnalyzer/OQL,這里就簡單列一些筆者排查過程中用過的語句,不再贅述:

// 字符串模糊匹配
SELECT * FROM char[] b where toString(b) LIKE ".*traceId.*"
// 查找地址>0x700000000的對象
SELECT * FROM java.lang.Object t WHERE  toHex(t.@objectAddress) >= "0x700000000"
// 查找長度等于73并且retained heap size>1000B 的對象
SELECT * FROM java.lang.Object[] a where a.@length=73 and a.@retainedHeapSize>1000
// 查找長度等于65536并且上層有引用的對象
SELECT * FROM char[] a where a.@length=65536 and (inbounds(a).size()>0)

2.2 GC日志

GC日志是記錄Java虛擬機(jī)中垃圾回收活動(dòng)的日志文件。在GC日志中,可以看到包括垃圾回收的時(shí)間、類型(如新生代GC、老年代GC等)、回收周期、回收停頓時(shí)間、回收前后堆的使用情況等信息。GC日志打印的信息可以通過以下啟動(dòng)項(xiàng)控制:


三、 案例介紹

在這篇文章中,我們將聚焦于一些具體的案例,涉及到大量被Dubbo的FutureAdapter引用的對象、Jackson的BufferRecycler導(dǎo)致的大量char[65536]以及對象晉升年齡閾值過小等問題。通過這些案例,我們將探討這些具體問題的引起原因以及解決方案。

3.1 大量被Dubbo的FutureAdapter引用的對象

(1)分析過程

從上圖中,我們可以看到dubbo FutureAdapter占用了230M左右的內(nèi)存,前面的PSWMS對象雖然也占用了230M左右的內(nèi)存,但這是業(yè)務(wù)使用的本地緩存相關(guān)對象,其內(nèi)存占用是在預(yù)期范圍內(nèi)的,因此優(yōu)先分析FutureAdapter。先右鍵List objects→with outgoing references展開對象列表。

發(fā)現(xiàn)其中有大量大小幾乎一致的FutureAdapter,一個(gè)占用內(nèi)存328KB左右,大小和內(nèi)容幾乎一致的對象約有550多個(gè),總共占用內(nèi)存200M左右。

FutureAdapter被用來執(zhí)行Dubbo的異步調(diào)用,項(xiàng)目使用的dubbo版本為2.7.18。dubbo的同步調(diào)用本質(zhì)上是一個(gè)異步轉(zhuǎn)同步的過程,發(fā)起異步調(diào)用將CompletableFuture對象放到ThreadLocal的FutureContext里,然后立刻調(diào)用CompletableFuture.get方法阻塞獲取返回值,獲取到返回值后,dubbo不會(huì)主動(dòng)清理FutureContext,因此該線程的ThreadLocal里會(huì)有一條FutureContext→ FutureAdapter→Result的引用,如下圖:

然而,那550多個(gè)FutureAdapter均為不可達(dá)對象,意味著其不被ThreadLocal引用,在下次GC時(shí)會(huì)被回收,不過我們無法直接確定這些對象是在老年代還是新生代,有可能這些對象都在新生代,下次young gc時(shí)就會(huì)被回收,不會(huì)晉升到老年代,更不會(huì)導(dǎo)致老年代增長。

但由于這個(gè)列表里的數(shù)據(jù)有明確的業(yè)務(wù)含義,可以找到對應(yīng)的業(yè)務(wù)接口,此接口單機(jī)峰值qps約為2,響應(yīng)時(shí)間約100ms,每被調(diào)用一次,就會(huì)創(chuàng)建一個(gè)該對象列表,此時(shí)該機(jī)器的young gc頻率約為10s一次。假設(shè)這些對象都在新生代沒有晉升老年代,那么這些對象在新生代最大的存活數(shù)量約為((接口響應(yīng)時(shí)間 + 兩次young gc間隔) * 對象創(chuàng)建速度) = (0.1s + 10s) * 2 ≈ 20,而堆里有550多個(gè),如果這些對象沒有晉升到老年代的話數(shù)量上對不上,所以可以推測出這些對象在老年代里,需要等下次Full GC時(shí)才會(huì)被回收。

那么這些不可達(dá)的FutureAdapter為什么會(huì)在老年代?每次執(zhí)行dubbo調(diào)用,dubbo都會(huì)用這次調(diào)用的FutureAdapter替換掉上次調(diào)用時(shí)存在FutureContext里的FutureAdapter,上次調(diào)用的FutureAdapter不再被GC Root引用,在下次GC時(shí)被回收。當(dāng)一個(gè)線程相對頻繁的執(zhí)行dubbo調(diào)用時(shí),F(xiàn)utureAdapter會(huì)被young gc回收,不會(huì)晉升到老年代。但在本例中,該dubbo調(diào)用被放到了corePoolSize=150,maxPoolSize=500的業(yè)務(wù)通用線程池中執(zhí)行,該線程池會(huì)執(zhí)行其他不需要調(diào)用dubbo服務(wù)的任務(wù),并且該線程池的使用率并不高,這就意味著一個(gè)線程調(diào)用完dubbo服務(wù)后可能要過一段時(shí)間才能執(zhí)行下一次dubbo調(diào)用。

由于這個(gè)原因?qū)е翭utureAdapter被放入ThreadLocal后,在新生代停留過長時(shí)間,最終晉升到老年代,這個(gè)"過長時(shí)間"對于此項(xiàng)目來說是6次young gc的間隔時(shí)長,這個(gè)時(shí)長的獲取方法會(huì)在后續(xù)說明。

(2)解決方案

對于此業(yè)務(wù)來說,這個(gè)dubbo調(diào)用可以改為查詢本地緩存,直接解決了問題。除此之外還有其他解決方案,需要結(jié)合自身業(yè)務(wù)選擇合適的方案。

  1. 直接使用dubbo的異步調(diào)用,而不是在上層再創(chuàng)建一個(gè)線程池來進(jìn)行調(diào)用。
  2. 合理設(shè)置線程池的大小,提高線程的利用率。
  3. 寫一個(gè)Dubbo Filter,每次同步調(diào)用完后清理FutureContext(影響面可能較大,需自行評估風(fēng)險(xiǎn))。

3.2. Jackson的BufferRecycler導(dǎo)致的大量char[65536]

(1)分析過程

從dominator tree中我們能看到char[]也占用了相當(dāng)大的一部分內(nèi)存,展開char[],發(fā)現(xiàn)其中包含大量的char[65536],使用oql統(tǒng)計(jì)得知不被gc root引用的有1600個(gè),占用內(nèi)存200M左右,被gc root引用的有500個(gè),這種char[65536]里存儲(chǔ)的數(shù)據(jù)均為http接口返回值反序列化后的字符。其被gc root引用時(shí)的鏈路如下,均被ThreadLocal里的BufferRecycler引用:

在Jackson庫中,BufferRecycler的主要作用是管理緩沖區(qū)的重用,可以減少頻繁的內(nèi)存分配和釋放,從而降低垃圾回收的負(fù)擔(dān),提高性能。但從堆上看,這些char數(shù)組里不可達(dá)的數(shù)量遠(yuǎn)大于可達(dá)的數(shù)量(1600:500),說明其復(fù)用率并不高,與其設(shè)計(jì)的目標(biāo)不符,需要查看源碼才能搞清原因。

項(xiàng)目使用jackson的ObjectMapper。

writeValueAsString()方法對http接口返回值進(jìn)行了反序列化,使用的Jackson版本為2.10,該方法完整的執(zhí)行流程如下圖:

簡單來說,jackson在反序列化時(shí),會(huì)將反序列化的結(jié)果存儲(chǔ)在多段char[]里,每當(dāng)最后一個(gè)char[]空間不夠存放結(jié)果時(shí),就新建一個(gè)char[],大小為最后使用的char[]的1.5倍,但不超過65536,反序列化結(jié)束后將char[]列表拼接起來就得到了結(jié)果,然后線程會(huì)將最后使用的那個(gè)char[]存放到ThreadLocal。此線程下次反序列化時(shí),會(huì)從ThreadLocal取出這個(gè)char[]進(jìn)行復(fù)用。這樣的一個(gè)復(fù)用邏輯會(huì)有一個(gè)問題,參考下圖:

圖中,_segements是當(dāng)前反序列化使用過的char[]列表,currentSegement是當(dāng)前正在使用的char[]。一個(gè)char[]的大小最大為65536。在第二次反序列化大對象時(shí)至少會(huì)創(chuàng)建一個(gè)新的大小為65536的char[](上一次的char[]是65536,再創(chuàng)建一個(gè)新char[]其大小仍不能超過65536)。可以看到在第一次反序列化結(jié)束后和第二次反序列化結(jié)束后,雖然ThreadLocal里存放的char[]大小都是65536,但其實(shí)它們已經(jīng)不是同一個(gè)對象了。這樣的一個(gè)替換是沒有必要的,完全可以一直復(fù)用同一個(gè)char[]。

當(dāng)業(yè)務(wù)所有http接口的返回值都大且流量也大時(shí),每次保存在ThreadLocal里的char[65536]雖然會(huì)在下次反序列化結(jié)束時(shí)被替換導(dǎo)致其失去引用,但由于其在新生代只存活了一次接口請求的時(shí)間,所以不會(huì)晉升到老年代,可以被young gc回收。但是我們項(xiàng)目用來處理http請求的線程池都是同一個(gè),這些接口的返回值只有一部分超過了65536,在小于的時(shí)候ThreadLocal里的char[]不會(huì)被替換,當(dāng)這個(gè)char[]在ThreadLocal里停留一段時(shí)間后,就會(huì)晉升到老年代,從而導(dǎo)致老年代內(nèi)存增長。

(2)解決方案

  1. 關(guān)閉Jackson的USE_THREAD_LOCAL_
    FOR_BUFFER_RECYCLING,關(guān)閉該開關(guān)會(huì)在每次反序列化時(shí)創(chuàng)建一個(gè)BufferRecycler,而不是復(fù)用ThreadLocal里的BufferRecycler,這樣可能導(dǎo)致young gc頻率提高。
  2. 升級Jackson版本,請參考此issue,2.17版本的jackson在調(diào)用releaseByteBuffer時(shí)會(huì)避免較小或者相同大小的char數(shù)組替換原有數(shù)組。

由于項(xiàng)目使用的jackson版本是2.10,直接升級到2.17的版本跨度較大,可能帶來不必要的風(fēng)險(xiǎn),因此采用了方案1,上線后,young gc頻率沒有明顯增加。方案2的issue里有提到使用2.16版本引入的RecyclerPool代替基于ThreadLocal的實(shí)現(xiàn),這也是解決方案之一。

3.3 對象晉升年齡閾值過小

(1)背景知識

java對象從新生代晉升到老年代有多種原因,在本項(xiàng)目中,對象的主要晉升原因是在新生代長期存活,這個(gè)長期具體是多久有以下兩個(gè)判斷條件:

  • 對象晉升年齡閾值
    可通過-XX:MaxTenuringThreshold啟動(dòng)項(xiàng)進(jìn)行配置,對于CMS,默認(rèn)值是6。此參數(shù)定義了對象在年輕代存活的最大年齡,如果一個(gè)對象在年輕代經(jīng)過N次GC后依然存活,它將會(huì)被晉升到老年代。
  • 動(dòng)態(tài)年齡判定
    在survivor區(qū)中小于或等于某年齡的的所有對象大小的總和大于survivor空間的一定比例時(shí),大于或等于該年齡的對象就直接進(jìn)入老年代,這個(gè)比例可以通過-XX:TargetSurvivorRatio啟動(dòng)項(xiàng)控制,默認(rèn)值為50,代表50%。

一個(gè)對象的年齡滿足上述兩個(gè)條件之一時(shí),就會(huì)晉升到老年代,具體的晉升年齡可以通過在啟動(dòng)項(xiàng)里添加-XX:+PrintTenuringDistribution獲取,添加該參數(shù)后的gc日志如下圖:

其中區(qū)域2的(max 6)代表-XX:MaxTenuringThreshold啟動(dòng)項(xiàng)配置的值,也就是說對象到達(dá)這個(gè)年齡一定會(huì)晉升,而new threshold 6代表對象實(shí)際晉升的年齡,上圖代表這次young gc因?qū)ο蟮竭_(dá)年齡閾值會(huì)導(dǎo)致9946864 bytes的對象晉升。

區(qū)域1代表動(dòng)態(tài)年齡判定所需的空間大小,也就是(survivor空間大小 x targetSurvivotRatio)。此項(xiàng)目堆的單個(gè)survivor空間為200M,所以只要在survivor區(qū)中小于或等于某年齡的所有對象大小的總和大于200Mx50%,大于或等于該年齡的對象就會(huì)晉升。

而下圖的對象是因?yàn)閯?dòng)態(tài)年齡判定才晉升的,這次young gc因動(dòng)態(tài)年齡判定會(huì)導(dǎo)致38660424 bytes的對象晉升:

(2)分析過程

優(yōu)化前+調(diào)參前:

在進(jìn)行dubbo和jackson以及其他業(yè)務(wù)代碼上的優(yōu)化前,我們保存了當(dāng)時(shí)的gc日志,可以看到大部分對象都是因?yàn)槟挲g到達(dá)6晉升的,每次young gc約有10M~16M左右的對象晉升,顯然對象的晉升年齡閾值太小,需要調(diào)大。

優(yōu)化后+調(diào)參前:

在調(diào)整 JVM 參數(shù)之前,我們決定先著手進(jìn)行業(yè)務(wù)上的優(yōu)化。因?yàn)橹苯舆M(jìn)行參數(shù)調(diào)整可能會(huì)治標(biāo)不治本,無法消除潛在的隱患。在完成業(yè)務(wù)代碼上的優(yōu)化后,可以看到此時(shí)由于年齡達(dá)到6這一閾值晉升的對象大小從最開始的10M~16M降為了4M以下,意味著在新生代長期存活的對象數(shù)量明顯減少了,但仍然有優(yōu)化空間。

(3)解決方案

在完成業(yè)務(wù)代碼上的優(yōu)化后,我們對 JVM 參數(shù)進(jìn)行了調(diào)整。將-XX:MaxTenuringThreshold參數(shù)改為15,-XX:TargetSurvivorRatio改為75%(實(shí)際上,通過調(diào)參后的gc日志我們能確定對于這個(gè)項(xiàng)目來說,50%也已經(jīng)夠用,因?yàn)槟挲g1到15的對象占用的總內(nèi)存只有38M左右,遠(yuǎn)遠(yuǎn)小于單個(gè)survivor空間的50%),以延長對象在新生代的存活時(shí)間。我們可以觀察到,盡管會(huì)有一些對象存活到年齡15的閾值才晉升,但是這部分對象的總大小變小了,大部分情況下都是小于2M。這部分對象通過添加監(jiān)控的方式判斷大概率是被移除(大小不足被淘汰,過期等原因)的caffeine本地緩存。

在經(jīng)過上述一系列優(yōu)化措施以及一些其他問題的修復(fù)后,該工程的 Full GC 頻率從最初的每天 120 次,總耗時(shí) 1 分鐘到 1.5 分鐘,成功降低到每天約 30 次左右,總耗時(shí)控制在 15 秒到 25 秒之間。

① GC次數(shù)優(yōu)化曲線


② GC總耗時(shí)優(yōu)化曲線


總的來說,進(jìn)行GC優(yōu)化時(shí),可以使用以下工具分析當(dāng)前內(nèi)存/GC情況:

  1. 先用jamp生成dump文件,再使用MAT進(jìn)行分析,找到可能引發(fā)問題的對象。
  2. 使用內(nèi)存分配火焰圖找到哪些代碼在頻繁的分配內(nèi)存。
  3. 使用GC日志分析GC情況,了解GC頻率/觸發(fā)GC的原因等信息。

使用這些工具找到問題后,可以修改對應(yīng)的業(yè)務(wù)代碼或者調(diào)整JVM相關(guān)參數(shù),以優(yōu)化Full GC頻率。

責(zé)任編輯:龐桂玉 來源: vivo互聯(lián)網(wǎng)技術(shù)
相關(guān)推薦

2021-04-12 09:36:14

JVM生產(chǎn)問題JVM FULL GC

2020-03-03 17:35:09

Full GCMinor

2021-04-14 10:14:34

JVM生產(chǎn)問題定位內(nèi)存泄露

2017-06-09 08:49:07

加載器Full GCJVM

2022-12-17 19:49:37

GCJVM故障

2025-03-31 04:25:00

2009-07-08 15:11:58

JVM GC調(diào)整優(yōu)化

2021-11-17 08:16:03

內(nèi)存控制Go

2017-11-08 15:23:57

Java GC優(yōu)化jvm

2009-04-20 08:51:50

MySQL查詢優(yōu)化數(shù)據(jù)庫

2017-03-29 14:44:20

網(wǎng)絡(luò)性能優(yōu)化

2022-05-17 09:02:30

前端性能優(yōu)化

2019-12-13 10:25:08

Android性能優(yōu)化啟動(dòng)優(yōu)化

2023-05-11 07:30:10

KV存儲(chǔ)GC優(yōu)化

2023-08-01 08:20:42

JVM優(yōu)化虛擬機(jī)

2017-03-14 18:48:06

Android性能優(yōu)化內(nèi)存優(yōu)化

2018-04-13 10:38:49

數(shù)據(jù)庫SQLJoin

2018-05-09 14:45:50

蘇寧前端Nodejs

2015-05-30 10:04:24

線下公開課51CTO沙龍MDSA

2023-12-11 06:27:39

MySQL線上業(yè)務(wù)優(yōu)化后臺(tái)上傳文件
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 97中文视频| 国产视频三级 | 日韩在线91| 午夜精品影院 | 精品国产乱码久久久久久蜜柚 | 欧美亚洲第一区 | av黄色片| 国产精品亚洲综合 | 一级在线免费观看 | 午夜免费成人 | 亚洲电影专区 | 中文字幕在线观看一区 | 国产精品中文字幕在线播放 | www.中文字幕.com | 成人三级视频 | 中文字幕第90页 | 九九免费在线视频 | 中文视频在线 | 国产精品久久久久婷婷二区次 | 亚洲精品视频导航 | 在线免费观看黄色 | 国产精久久久久久久 | 精品视频免费 | 精品国产青草久久久久福利 | 久久国产一区二区 | 国产日韩精品在线 | 亚洲精品久久久久久国产精华液 | 久久精品亚洲一区 | av在线一区二区 | 国产精品毛片久久久久久 | 久久久久精| 国产精品久久久久久久久 | 欧美日韩看片 | 亚洲欧美日韩电影 | a久久 | 中文字幕在线看第二 | 欧美极品一区二区 | 国产亚洲一区二区在线观看 | 欧美一区二区在线观看 | 农夫在线精品视频免费观看 | 成人欧美一区二区三区在线播放 |