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

解決GC毛刺問題——轉轉搜索推薦服務JDK17升級實踐

開發 前端
截止至發文,服務已成功部署應用JDK17并平穩運行一月有余。通過本次升級,我們獲得了顯著的GC停頓時間和內存占用率的改善效果,有效解決了服務GC問題,進而降低了服務高分位延遲指標,充分驗證了JDK17新版本GC算法的優勢。

1 升級背景

隨著轉轉業務規模的不斷增長,我們的搜索推薦服務正在面臨嚴峻的垃圾回收(Garbage Colletion, GC)帶來的服務接口耗時毛刺問題。

我們當前所使用的JDK1.8版本中的CMS和G1收集器,在應對請求高峰時均不理想,經常出現的停頓問題直接影響了服務的可用性及用戶體驗。

我們面臨的核心挑戰是:

  • 服務請求流量激增時,GC次數頻繁是我們的一大痛點,每分鐘有可能達到十幾次以上。另一方面,單次GC停頓時間也較長,可高達數十毫秒。這不但降低了服務的可用性,也限制了服務的吞吐量,對于我們的在線服務是難以接受的。
  • 同時GC參數的調優工作遇到瓶頸,盡管還可以通過減少新對象創建速率等方式繼續優化,但整體投入產出比偏低。

為此,我們計劃通過升級JDK版本來實現GC問題的改善。JDK新版本帶來了如ZGC、Shenandoah等新一代GC算法,它們能夠提供極低的GC停頓時間,有望解決我們的在線服務目前的GC毛刺問題。

我們的升級目標是利用新版本JDK中的新GC算法,將搜索推薦服務的GC停頓時間降低90%以上,保證高流量服務的可用性和吞吐量,進一步提升用戶體驗。

2 JDK17簡介

我們選擇將JDK版本升級到JDK17,主要原因有:

  • 一方面,JDK17是目前最新的長期支持(Long Term Support,LTS)版本,相比其他版本,它能提供更穩定和持久的支持,同時也有大量企業應用了JDK17,有豐富成熟的使用經驗。可以預見JDK17在未來一段時間也將會是主流版本,能得到更好的社區支持。
  • 另一方面,JDK17作為新一代版本,與舊版JDK8相比,既能與現有代碼上保持兼容性,又在語法和GC算法等多個方面做出了重要改進和優化。如JDK17包含了可用于生產環境的ZGC,且它的性能在歷代版本迭代下,得到大幅增強。

2.1 新語法簡介

具體來看,此次JDK 17的升級在語法上帶來了以下幾個值得注意的新特性:

類型推斷

從JDK10版本開始,引入了局部變量類型推斷(Local Variable Type Inference)功能,它可以讓我們在聲明局部變量時省略變量類型,而由編譯器根據變量初始化的值自動推斷出類型。    

// 傳統變量聲明方法
String str = "hello";
// 使用類型推斷的變量聲明方法
var str = "hello";

Stream API的增強

JDK新版本對Stream API進行了一些增強,主要有:

takeWhile和dropWhile會對流中每個元素逐一校驗,遇到第一個不符合條件的元素終止,takeWhile返回終止位置前面的所有元素,而dropWhile則返回包含終止位置后面的所有元素。它們功能雖然與filter類似,區別是前者并非對整個流進行校驗,可以提升過濾效率,但需要注意流內元素的順序。

var list = List.of(1,2,3,4,5,6);
// 輸出:1,2;
list.stream().takeWhile(n -> n < 3).forEach(System.out::println); 
// 輸出:3,4,5,6
list.stream().dropWhile(n -> n < 3).forEach(System.out::println);

iterate可以生成一個無限的流,它在JDK9之前需要limit()等操作來配合終止,否則將無限遞歸下去。在JDK9中iterate新增了一個重載方法,現在支持使用條件來終止,它在語法上更簡潔,也提供了更多的靈活性。

// 輸出:1,2,4,8,...,512,1024
Stream.iterate(1, n -> n <= 1024, n -> n * 2).forEach(System.out::println);

集合API新增方法

操作集合將更加方便,如可以更加簡潔的創建List和Map,但需要注意這種方式創建的集合均是不可變的。

var list = List.of(1, 2, 3, 4, 5);
var map1 = Map.of("a", 1, "b", 2);
var map2 = Map.ofEntries(Map.entry("a", 1), Map.entry("b", 2));

同時新增了多種Collector方法,如可以通過groupingBy新增的重載方法實現多級分組,假設Product類有Cate、Brand、Model成員,則可以做如下多層分組收集:

// 按Cate、Brand分組,收集Model列表
List<Product> products = ...;
Map<Cate, Map<Brand, List<Model>>> result = products.stream()
  .collect(Collectors.groupingBy(Product::getCate,
          Collectors.groupingBy(Product::getBrand,
          Collectors.mapping(Product::getModel, Collectors.toList()))));

Swtich新語法

JDK12開始,switch語句增加了新的語法形式,允許使用更靈活的表達式匹配,并可以返回值,提升了代碼的簡潔性。

int month = ...;
String days = switch(month) {
  case 1, 3, 5, 7, 8, 10, 12 -> "31 days";
  case 4, 6, 9, 11 -> "30 days";
  case 2 -> "28 or 29 days";
}

文本塊

JDK13開始提供了一種新的字符串格式,用戶可以選擇用三個雙引號(""")作為字符串開頭及結尾,直接編寫多行文本,它為JSON、SQL等格式的字符串編寫提升了簡潔性和便利性。

String textBlock = """
                   This is a text block
                   spanning multiple lines.
                   """;

Record類型

JDK14中新增的Record提供了更簡潔的語法來生成只用于數據存儲的類,并自動生成訪問方法、equals和hashCode比較方法以及toString方法,它可以在類內部或方法內部生成。它相比class類更輕量簡潔,相比Pair、Triple等組合類Record的語義上更加明確、代碼可讀性更強。

void someMethod() {
    record Product(long id, String category);
    Product product = new Product(101L, "phone");
    long productId = product.getId();
    String productCategory = product.getCategory();
}

模式匹配新語法

JDK14版本引入了模式匹配新語法,避免了冗余的類型轉換語句。

Object obj = ...;
if (obj instanceof String s) {
  System.out.println("String: " + s.length());
} else if (obj instanceof Integer i) {
  System.out.println("Integer: " + i);
} else {
  System.out.println("Unknown object");
}

JDK17版本后,新的模式匹配方式也可以在Switch語句中使用了。

Object obj = ...;
switch(obj) {
    case String s -> System.out.println("String: " + s.length());
    case Integer i -> System.out.println("Integer: " + i);
    default -> System.out.println("Unknown object");
}

密封類

密封類(Sealed Class)是JDK15引入的新特性,當使用sealed關鍵字修飾一個抽象類時,表示這個抽象類只允許指定的類來繼承實現。

如下ProductField類只允許Cate、Brand、Model類繼承,這種特性避免了意料外的類型擴展,提升了類型安全性。

sealed abstract class ProductField permits Cate, Brand, Model {
   //...
}

此外,JDK新版本還有向量API等新特性和諸多改進等待我們探索發現。

2.2 新GC算法簡介

ZGC介紹

ZGC在JDK11作為實驗性的GC算法被引入時,最初的設計目標是實現10毫秒以內的最大停頓時間。在過去一段時間里,ZGC經過JDK版本的數次迭代,在JDK15中被宣布為可用于生產,目前據官方介紹已經可以實現亞毫秒級的最大停頓時間,且停頓時間不隨堆內存、存活對象集合或GCRoot集合大小的增加而增加,它可以處理從8MB到16TB的大范圍堆內存。

在官方介紹里,ZGC是并發的、基于區域的(Region-based)、壓縮的(Compacting)、NUMA感知(NUMA-aware)的垃圾回收器。它主要使用了染色指針(Colored Pointor)和讀屏障(Load Barriers)技術,并在新一代的JDK21版本中實現了分代回收,它的主要工作是在用戶線程工作執行時完成的,這大大降低了GC對應用響應時間的影響。

使用如下JVM啟動參數可以快速應用ZGC:

-XX:+UseZGC

Shenandoah GC介紹

Shenandoah GC是一種全新的低延遲垃圾收集器,在JDK 8的部分版本可用,從JDK 11版本正式引入,它通過讀寫屏障和并發標記技術,可以極大縮短GC時的應用程序停頓。

相比CMS、G1等算法,其停頓時間更短,支持超大內存,非常適合對響應時間敏感類型的服務。由于Shenandoah使用了讀寫屏障技術,雖然可能導致吞吐量略降,但總體來說是更有效的GC算法之一。

使用如下參數可以快速應用Shenandoah GC:

-XX:+UseShenandoahGC

3 升級過程

JDK版本升級無需做太多代碼改動,但要平滑過渡到新版本,也需要做充分準備和規劃。本節將分享我們升級到JDK17的具體步驟,在此過程中遇到的問題及解決方法,以及對ZGC相關問題的分析。

3.1 升級步驟

安裝JDK17

我們在本地測試時選擇了Eclipse Temurin Build版本,根據官網介紹它是由基于OpenJDK的開源Java SE產生的構建版本,這里根據開發環境的機器配置下載并安裝了jdk-17.0.7+7 macos aarch64版本。

調整IDE配置

在使用IntelliJ Idea開發環境時,可以在文件--項目結構配置中,將SDK選項調整到剛剛安裝的JDK版本。

圖片圖片

調整項目配置

由于我們的項目是Maven項目,需要選擇POM文件,修改Maven的編譯插件的source和target配置到17。

<properties>
  <jdk.version>17</jdk.version>
</properties>
...
<plugins>
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
      <source>${jdk.version}</source>
      <target>${jdk.version}</target>
    </configuration>
  </plugin>
</plugins>

部署測試

本地編譯測試通過后,意味著可以到測試環境進行部署和驗證了,驗證內容包括全場景的功能驗證、DIFF驗證、壓力性能測試等等(由于部署功能是由公司其他系統提供,不展開敘述)。

升級JDK17后,JVM啟動參數需要調整,一些舊參數被廢棄,同時增加新的參數,我們用于測試環境部署的參數為:

-Xms6g -Xmx6g -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -Xss256k -XX:+UseZGC -XX:ParallelGCThreads=12

生產環境部署及效果數據回收

升級JDK后,回收線上服務的效果數據至關重要,我們主要關注服務延時表現、GC暫停表現以及內存消耗表現。

我們選擇服務集群的50%節點來部署JDK17,另外50%節點保持JDK8不變作為對照組,分配節點時,保持各組的節點機器配置情況一致,將實驗變量控制在僅JDK版本的切換上。

同時將服務訪問的延時信息、GC暫停信息、堆內存使用信息上報到日志收集系統。由于前者的日志規模龐大,為了獲取更精確的統計信息,通過上報到大數據平臺并使用HiveSql分析,后兩者則通過上報Promethues監控平臺來實現實時信息收集。

3.2 遇到問題及解決方法

在實際編譯和部署的過程中,還可能會遇到各種各樣的問題,下面我們對遇到的問題及解決方法做了一些梳理。

以下為編譯期間遇到的一些問題:

非法字符引發的異常

Maven編譯期間遇見如下報錯:

[ERROR] Internal error: java.lang.IllegalArgumentException: Malformed \uxxxx encoding. -> [Help 1]

問題原因:這是Maven在加載一些配置文件時遇到了不兼容的編碼字符導致的。本地Maven倉庫路徑下的resolver-status.properties文件中存在格式不正確的unicode編碼字符,這些字符在JDK 17的字符串處理方式下無法解析。

解決方法:使用以下命令,遞歸刪除本地倉庫下所有的resolver-status.properties文件:

find ~/.m2/ -name resolver-status.properties -delete

包不存在引發的異常

編譯器期間提示包不存在:

import javafx.util.Pair

問題原因:javafx等包在JDK新版本中被默認移除。

解決方法:可以使用apache.commons提供的Pair類替代,也可以手動引入被移除的依賴,其他被移除的類也可以通過類似的方法解決。

以下為部署期間遇到的問題:

JVM參數引發的異常

啟動階段可能遇到類似如下問題:

Unrecognized VM option 'UseGCLogFileRotation'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.

問題原因:部分JVM參數在新版本不再兼容,導致不能識別。

解決方法:從啟動參數里將不兼容的參數移除即可,同時尋找替代參數。

反射訪問引發的異常

如以下日志所示,我們在初始化apollo配置中心組件時遇到了啟動異常,從異常描述看是程序反射訪問期間引起的。

java.lang.IllegalArgumentException: Cannot instantiate interface org.springframework.context.ApplicationContextInitializer : com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer
        ...
        at com.bj58.spat.scf.server.bootstrap.Main.main(Main.java:27) [zzscf.server-2.7.12.jar:?]
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.ctrip.framework.apollo.spring.boot.ApolloApplicationContextInitializer]: Constructor threw exception; nested exception is com.ctrip.framework.apollo.exceptions.ApolloConfigException: [ARCH_APOLLO_CLIENT]Unable to load instance for com.ctrip.framework.apollo.spring.config.ConfigPropertySourceFactory!
        at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:154) ~[spring-beans-4.3.12.RELEASE.jar:4.3.12.RELEASE]
        at org.springframework.boot.SpringApplication.createSpringFactoriesInstances(SpringApplication.java:409) ~[spring-boot-1.5.8.RELEASE.jar:1.5.8.RELEASE]
        ... 8 more
...
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @5e265ba4
        at java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) ~[?:?]
        at java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) ~[?:?]
        at java.lang.reflect.Method.checkCanSetAccessible(Method.java:199) ~[?:?]
        at java.lang.reflect.Method.setAccessible(Method.java:193) ~[?:?]
...

問題原因:新版本JDK引入了模塊訪問控制,跨模塊時無法簡單的直接通過反射訪問了,上述異常是想要通過反射訪問Java內部模塊而拋出的

解決方法:對于此類問題,可以通過臨時增加如下啟動參數解決,也可以查閱依賴包的新版本,了解它們是否已對JDK新版本做出了適配

--add-opens java.base/java.lang=ALL-UNNAMED

java.base/java.lang是本次異常需要用到的模塊參數,在解決此類異常時,需要根據實際要訪問的模塊名進行調整,以下為我們收集的一些啟動參數,可以按需增加啟動參數配置。

--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/jdk.internal.access=ALL-UNNAMED --add-opens java.base/jdk.internal.misc=ALL-UNNAMED

注解類型被默認移除引發的異常

啟動過程中,發現拋出了如下空指針異常

Caused by: java.lang.NullPointerException: Cannot invoke "String.length()" because "s" is null
        at java.base/java.net.URLEncoder.encode(URLEncoder.java:224)
        at java.base/java.net.URLEncoder.encode(URLEncoder.java:196)
        at com.bj58.zhuanzhuan.arch.service.manager.sdk.client.CallPermissionService.initUri(CallPermissionService.java:192)
        at com.bj58.zhuanzhuan.arch.service.manager.sdk.client.CallPermissionService.<init>(CallPermissionService.java:72)
        at com.bj58.spat.scf.server.filter.BlackKeyRequestFilter.afterPropertiesSet(BlackKeyRequestFilter.java:120)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
        ... 15 more

問題原因:分析調用鏈路后發現,問題發生在一個上下文類,它的init方法是通過@PostConstruct注解觸發執行的,該注解在JDK新版本中被默認移除了,導致init方法未能執行

解決方法:短期可以通過手動引入以下依賴方式解決,長期同樣可以查閱依賴包維護方的更新日志,或與維護方進行溝通,將依賴更新到已適配版本。

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

此外我們在發布到生產環境后,還遇到了以下問題:

ZGC的虛擬內存申請的疑問

我們的服務升級JDK并在實際生產環境部署后,同物理節點的其他服務曾出現了一次短暫的資源耗盡異常,當時我們懷疑導致問題原因之一是ZGC申請了過多的虛擬內存。

針對ZGC申請過多虛擬內存問題,我們經過排查發現,這并不是JDK17存在的問題,而是由ZGC自身的實現機制所導致的。ZGC通過染色指針和多重映射技術來實現高吞吐低延遲的GC。

為了實現染色指針,ZGC需要使用地址高位額外的bit來記錄對象狀態,所以需要的虛擬內存空間遠高于實際堆大小。此外,以目前JDK17版本,它還會為不同狀態的對象分配獨立的虛擬內存,以實現并發回收,具體來說需要為remapped、marked0、marked1三種狀態申請三份獨立虛擬內存空間。

以4TB堆為例,ZGC需要4bit用于染色,所以需要4TB * 2^4 = 128TB虛擬內存。它還會為每種染色狀態各自申請128TB空間。所以4TB堆最終會申請128TB * 3約等于384TB的虛擬內存。

本例中我們的服務實際使用6GB堆,通過與內存工具Native Memory Tracking輸出結果比對,發現跟公式計算結果一致,ZGC申請了約300GB的虛擬內存,符合其技術實現的需要。

所以結論是ZGC申請虛擬內存并非JDK問題,是其特有的技術實現方式導致。

4 升級效果

以下是我們在轉轉的通用推薦服務升級過程中,持續對比三個全天收集到的效果數據,我們設立50%節點升級到JDK17作為實驗組,另50%節點不升級作為對照組。

4.1 整體耗時對比

首先看下服務的整體耗時數據,如下圖所示,可以看到該服務升級JDK17后tp999及tp9999時間有顯著降低。

圖片圖片

整體耗時對比

通過新版本GC算法的引入,服務處理請求的尾部延時情況得到了改善,響應時間的毛刺問題明顯減輕。

下表為詳細數據:

指標/版本

JDK8

JDK17

降幅

AVG耗時

22ms

22ms

持平

TP50耗時

11ms

11ms

持平

TP90耗時

57ms

57ms

持平

TP99耗時

149ms

148ms

0.67%

TP999耗時

249ms

242ms

2.81%

TP9999耗時

601ms

458ms

23.78%

4.2 分節點耗時對比

在分節點的指標對比上,我們發現應用JDK17的節點在tp999和tp9999這兩個高延遲分位數指標上的表現更加平穩。

如下圖所示,相比保持JDK8的對照組節點,升級到JDK17的實驗組節點,其tp999和tp9999指標的變化曲線更加平坦。

節點TP999耗時對比節點TP999耗時對比


節點TP9999耗時對比節點TP9999耗時對比


4.3 GC停頓時長對比

對于GC數據,我們收集了服務晚間4小時JDK8和JDK17版本的GC停頓數據。JDK8統計了其Young GC的暫停時間,而JDK 17統計了ZGC Pause時間。

從下表可以明顯看出,使用JDK17的ZGC算法后,GC停頓時長大幅減少。JDK8下YGC每分鐘平均暫停時間為221ms,而JDK 17下的ZGC只有0.37ms,降幅高達99.83%。

指標/版本

JDK8

JDK17

降幅

統計口徑

YGC時間

ZGC Pause時間

-

總時長

106250ms

221ms

99.67%

每分鐘平均時長

355ms

0.37ms

99.83%

停頓時間的降低不僅提高了服務的可用性,也使系統吞吐量獲得大幅提升。

4.4 堆空間占用對比

從下表統計數據可以看出,使用JDK 17后,相同堆空間配置下,實際堆內存占用有所降低,堆空間的利用效率得到提高。

在同為6G堆大小情況下,JDK 8堆占用平均為2.92G,占比48.7%;而JDK 17堆占用平均減少至2.42G,占比降至40.3%。堆內存占用比降低了17.2%。

這表明在不改變堆區設置的前提下,JDK 17可以提高堆空間的利用效率,降低內存占用,為系統留出更多可用內存空間,從而提高系統穩定性。

指標/版本

JDK8

JDK17

降幅

堆空間申請

6G

6G

-

每分鐘平均堆占用

2.922G

2.419G

17.20%

每分鐘平均堆占用比

48.70%

40.32%

17.20%

另外ZGC提供了-XX:SoftMaxHeapSize參數,用于彈性調節堆空間的最大值,當堆大小未超出設定值時可以釋放更多空閑內存。

5 總結

截止至發文,服務已成功部署應用JDK17并平穩運行一月有余。通過本次升級,我們獲得了顯著的GC停頓時間和內存占用率的改善效果,有效解決了服務GC問題,進而降低了服務高分位延遲指標,充分驗證了JDK17新版本GC算法的優勢。

同時,我們也積累了語法改進、升級中跨部門協調、問題排查等方面的寶貴經驗。升級過程中遇到了服務穩定性問題,也讓我們意識到需要對新特性有更深入的理解,平穩地應用到生產環境。

后續我們將繼續關注JDK新版本的特性改進,并逐步將搜索推薦核心服務完全升級到JDK17新版本,以獲得更好的開發體驗和服務運行效果。

關于作者

曾祥瑞,轉轉搜索推薦研發工程師

銳意進取,勇于試驗,與時俱進。

責任編輯:武曉燕 來源: 轉轉技術
相關推薦

2021-09-28 10:37:50

LayUI JDK

2024-08-26 09:36:06

2024-01-26 08:33:14

JDK17JDK11版本

2014-03-13 10:14:59

離線集群Hadoop 2.0淘寶

2023-02-08 09:42:30

策略方式容量

2012-11-06 09:47:34

Windows 8升級

2012-10-26 16:45:13

聯想Windows 8升級

2017-10-14 14:11:44

iOS 11iOS 10升級

2024-09-11 19:36:24

2012-07-06 08:58:53

微軟Windows 8

2009-06-23 08:35:12

微軟Windows 7操作系統

2009-08-16 09:25:55

Windows 7系統升級

2009-06-05 08:55:16

2012-05-11 09:54:23

微軟Windows 8

2010-05-06 09:57:45

RHEL 5.5升級

2012-12-12 09:53:50

Windows 8

2009-09-17 08:39:52

Windows 7系統升級

2013-10-18 16:09:30

Windows8.1聯想

2013-10-18 12:14:34

聯想服務Windows8.1升級

2024-09-19 22:22:41

多任務學習
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲在线免费 | 午夜影院中文字幕 | 国产精品久久久久久吹潮 | 国产电影一区二区 | 欧美日韩在线一区二区三区 | 日韩av在线中文字幕 | 极品电影院 | 国产欧美精品在线观看 | 国产精品视频一二三区 | 天天操人人干 | 欧美一区二区三区在线观看视频 | 一区二区三区四区国产 | 激情的网站 | 青青激情网 | 青青草视频网站 | 中文字幕在线观看第一页 | 久久欧美高清二区三区 | 欧美日韩精品国产 | 亚洲精品456 | 欧美在线一二三 | 久久久av| www.久久| 亚洲一区成人 | 在线欧美小视频 | 91精品国产综合久久精品 | 国产精品麻 | 四虎网站在线观看 | 久久的色 | 亚洲欧美一区二区三区视频 | www成人啪啪18 | 日本激情视频网 | 成人福利网站 | 亚洲国产成人精品一区二区 | 久久久精品久久 | 91在线精品视频 | 天堂男人av | 91久久久久久 | 中文字幕亚洲精品 | 欧美精品久久久久 | 成人高清网站 | 久久久国产精品 |