国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

淺析JVM invokedynamic指令和Java Lambda語法

開發 前端
Lambda表達式語言特性引入Java語言后,賦予了Java語言更便捷的函數式編程魔力(相對匿名內部類),同時也讓其更簡潔,畢竟Java代碼寫起來啰嗦這點一直被開發者們廣泛詬病。

一、導語

盡管近年來JDK的版本發布愈發敏捷,當前最新版本號已經20+,但是日常使用中,JDK8還是占據了統治地位。

圖片圖片

你發任你發,我用Java8:【Jetbrains】2023 開發者生態系統現狀 - https://www.jetbrains.com/zh-cn/lp/devecosystem-2023/java/

JDK8如此旺盛的生命力,與其優異的兼容性、穩定性和足夠日常開發使用的語言特性有極大的關系,這其中最引人矚目的語言特性莫過于Lambda表達式。

Lambda表達式語言特性引入Java語言后,賦予了Java語言更便捷的函數式編程魔力(相對匿名內部類),同時也讓其更簡潔,畢竟Java代碼寫起來啰嗦這點一直被開發者們廣泛詬病。

本文將從JVM和Java兩個層面著手,和大家一起深入解析Lambda表達式。

二、Java和JVM的關系

JVM是HLLVM(高級語言虛擬機),其參考物理計算機體系架構,設計、實現了一套特定領域虛擬指令集,即:字節碼指令。利用上述虛擬指令集作為中間層,將上層高級語言和底層體系架構解耦以規避繁瑣、復雜的平臺兼容性問題,以實現【一次編譯,處處運行】。

Java是基于JVM提供的虛擬指令集,設計、實現的一種供開發者使用的高級語言。通過配套的編譯器和標準庫,將文本格式的Java代碼編譯成符合JVM指令集規范的二進制文件,交付到JVM執行。

Java是一種運行在JVM平臺上的高級語言,但是JVM平臺絕不是只能運行Java語言。任何人都可以設計自己的語言語法,只要能按JVM規范編譯成合法的JVM字節碼,即可在JVM上運行(用Java命令)。

計算機科學領域的任何問題,都可以通過增加一個中間層來解決。

圖片圖片

沒有無源之水,Java語言層面的特性,除非是純語法糖,不然一定離不開特定JVM特性的支撐。Lambda是Java8語言特性,那支撐它的便是JVM invokedynamic指令。

三、JVM指令:invokedynamic

在Java7之前,JVM提供了如下4種【方法調用】指令:

圖片圖片

上述4種字節碼指令各自有不同的使用場景,但是有一個共同的特點:目標方法一定需要在【編譯期】確定。如下圖,編譯后4種指令的參數都指定了目標方法所在的類和簽名以供運行時鏈接、動態分派。

圖片圖片

圖片圖片

這個特點一方面保證了JVM語言類型安全,另一方面也限制了JVM平臺對動態類型高級語言的支持。比如想讓JavaScript、Python等動態語言代碼編譯成JVM字節碼運行在JVM平臺上的開銷會比較大,性能也會比較差。

為了解決上述問題, Java7引入了一條新的虛擬機指令:invokedynamic。這是自JVM 1.0以來第一次引入新的虛擬機指令,invokedynamic與其他 invoke*指令不同的是它允許由應用級的代碼來決定方法解析(鏈接、分派)。

所謂的【應用級的代碼來決定方法解析】需要對照之前的invoke*指令來理解。之前的4種invoke*指令,在編譯期就必須要明確目標方法并hardcode到字節碼中,JVM在運行時直接解析、鏈接、動態分派硬編碼指定的目標方法。而invokedynamic指令通過回調機制來獲取需要調用的目標方法。即先調用業務自定義回調方法做方法決策(解析、鏈接),再調用其返回的目標方法。筆者稱之為【兩階段調用】。

偽代碼對比如下:

圖片圖片

MethdoHandle為示意,后文有詳述。

偽字節碼偽字節碼

invokevirtual指令直接調用目標方法,invokedynamic直接調用回調方法,再調用回調方法返回的方法句柄。

傳統的invoke*指令直接調用字節碼中指定的目標方法,如Son.testMethod1,invokedynamic指令在調用時,先調用字節碼中指定的回調方法,如Son.dynamicMethodCallback,然后再調用回調方法(hook)返回的方法引用。

而上述dynamicMethodCallback即為【應用級的代碼或者我們常說的業務代碼】,可以在不影響性能的前提下,靈活的干預JVM方法解析、鏈接的過程。

總結來說,所謂應用級的代碼其實也是一個方法,在這里這個方法被稱為引導方法(Bootstrap Method),簡稱 BSM。invokedynamic執行時,BSM先被調用并返回一個 CallSite(調用點)對象,這個對象就和 invokedynamic鏈接在一起。以后再執行這條invokedynamic指令都不會創建新的 CallSite 對象。CallSite就是一個 MethodHandle(方法句柄)的holder,方法句柄指向一個調用點真正執行的方法。

一階段:調用引導方法確定并緩存CallSite(MethodHandle)

二階段:調用CallSite(MethodHandle)

字節碼指令比較low level,除字節碼業務插樁場景外,字節碼指令序列的構造、編排一般都由【高級語言編譯器】來根據語言語法規則自動完成,如javac。

某種意義上有點類似Java【動態代理】機制,都是通過調用橫切來動態橋接、靈活決策目標方法。

四、方法句柄:MethodHandle

前面我們知道invokedynamic指令支持通過業務層面自定義的BSM來靈活的決策被調用的目標方法,也就是上述的【一階段】。BSM方法的返回值就是【二階段】調用的方法。

但是和C、Python等語言不同,Java中方法/函數不是一等公民,也就是在Java中無法將【方法變量】作為方法返回值。

為了解決這個問題,Java標準庫提供了一個新的類型MethodHandle,用于實現類似C語言中的方法指針、JavaScript/Python中方法變量的能力。該API和反射API呈現的能力相似,但是性能更好。

圖片圖片

上述為MethodHandle API的基本使用,該課題展開又是一篇長文。總之,我們可以用MethodHandle來作為【方法變量】,變相的將【Java方法】提升為【一等公民】,從而可以在BSM中用Java代碼實現動態編排、決策,返回合適的方法指針。這也是上述invokedynamic+BSM機制能夠成立的一個基礎。

詳見:秒懂Java之方法句柄(MethodHandle) (https://blog.csdn.net/ShuSheng0007/article/details/107066856)

上述【一階段】調用的本質就是得到一個特定的MethodHandle(方法指針/方法引用),【二階段】調用就是調用這個MethodHandle。

五、Lambda表達式簡介

Java的Lambda表達式,是傳統的【匿名內部類】特性在特定場景下的平替特性。所謂的特定場景,即我們熟知的FunctionalInterface。

當【匿名內部類】匿名實現的是一個FunctionalInterface時,可以用Lambda表達式平替。

示例如下:

圖片圖片

函數式接口(Functional Interface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。

Java 不會強制要求你使用 @FunctionalInterface 注解來標記你的接口是函數式接口,然而,作為API作者,你可能傾向使用@FunctionalInterface指明特定的接口為函數式接口,這只是一個設計上的考慮,可以讓用戶很明顯的知道一個接口是函數式接口。

Java Lambda表達式在語法層面有兩種形式:行內代碼塊、方法引用。

圖片圖片

但是在編譯產物中,行內Lambda最終會被提取到獨立的靜態方法中。也就是說,在字節碼層面只有【方法引用】一種Lambda形式。

圖片圖片

圖片圖片

如上圖反編譯結果,兩個行內Lambda中的代碼在編譯后被提取到兩個自動生成的方法lambda$main$0、lambda$main$1,后續Lambda表達式的處理流程都可以收斂,無需區分對待。

六、Lambda表達式實現

Lambda表達式具體的實現涉及類文件結構、字節碼指令結構、標準庫等多個方面的內容,千頭萬緒。也想不出來什么通俗易懂的敘述角度,只能是枯燥的對照著字節碼分析了。

圖片圖片

如上圖,mian方法中聲明了3個Lambda表達式,反編譯字節碼可以看到字節碼指令流如下:

圖片圖片

0 iconst_3
 1 istore_1
 2 iconst_3
 3 newarray 10 (int)
 5 dup
 6 iconst_0
 7 iconst_1
 8 iastore
 9 dup
10 iconst_1
11 iconst_2
12 iastore
13 dup
14 iconst_2
15 iconst_3
16 iastore
17 invokestatic #2 <java/util/stream/IntStream.of : ([I)Ljava/util/stream/IntStream;>
20 invokedynamic #3 <applyAsInt, BootstrapMethods #0>
25 invokeinterface #4 <java/util/stream/IntStream.map : (Ljava/util/function/IntUnaryOperator;)Ljava/util/stream/IntStream;> count 2
30 iload_1
31 invokedynamic #5 <applyAsInt, BootstrapMethods #1>
36 invokeinterface #4 <java/util/stream/IntStream.map : (Ljava/util/function/IntUnaryOperator;)Ljava/util/stream/IntStream;> count 2
41 invokedynamic #6 <applyAsInt, BootstrapMethods #2>
46 invokeinterface #4 <java/util/stream/IntStream.map : (Ljava/util/function/IntUnaryOperator;)Ljava/util/stream/IntStream;> count 2
51 invokeinterface #7 <java/util/stream/IntStream.sum : ()I> count 1
56 istore_2
57 return

3個lambda表達式對應3條invokedynamic指令:

圖片圖片

第一個lambda表達式比較簡單且典型,后續我們以其為抓手展開分析。

invokedynamic指令參數

invokedynamic指令參數結構如下:

圖片圖片

jvms-6.5.invokedynamic (https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokedynamic)

invokedynamic指令需要指定其期待BSM返回的方法特征(出入參類型)和BSM方法引用。該參數以CONSTANT_InvokeDynamic_info結構存放在類文件的常量池結構中,invokedynamic用兩個byte寬度的常量池索引號指定。

CONSTANT_InvokeDynamic_info {
    u1 tag;
    u2 bootstrap_method_attr_index;
    u2 name_and_type_index;
}

圖片圖片

對照字節碼我們可知,Lambda1相關的invokedynamic指定的CONSTANT_InvokeDynamic_info序號為3,得到如下內容:

圖片圖片

圖片圖片

期望的方法名稱和描述符

該invokedynamic指令期望BSM0方法返回一個如下特征的方法引用:

IntUnaryOperator anyName();

沒有入參,返回值類型為IntUnaryOperator的MethodHandle。

為什么是返回IntUnaryOperator類型呢?因為IntStream的map方法需要的參數是IntUnaryOperator類型。

圖片圖片

換句話說,該invokedynamic指令希望相應的BSM返回一個IntUnaryOperator的工廠方法句柄,然后invokedynamic指令再調用這個方法句柄,創建出一個map方法需要的IntUnaryOperator類型的參數。

BSM方法序號

BSM方法序號指定了當前invokedynamic指令使用的BSM方法在BSM方法表中的索引。

通俗來說,類文件中有一個數組,數組名稱叫BootstrapMethods。其結構如下:

BootstrapMethods_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 num_bootstrap_methods;
    {   u2 bootstrap_method_ref;
        u2 num_bootstrap_arguments;
        u2 bootstrap_arguments[num_bootstrap_arguments];
    } bootstrap_methods[num_bootstrap_methods];
}

圖片圖片

圖片圖片

圖片圖片

該invokedynamic指令指定的BSM為BSM數組中的第一個BSM。

圖片圖片

BSM方法

圖片圖片

圖片圖片

BSM方法參數

該BSM數據結構指定了3個編譯期固定的、靜態的BSM方法參數:

圖片圖片

第一、第三個參數指定了預期的函數式接口(FunctionInterface)的特征:入參為int、出參為int。即上述IntUnaryOperator。

圖片圖片

第二個參數是一個靜態方法引用。如上述,Lambda表達式在編譯時會被提取到一個自動生成的方法中。

圖片圖片

圖片圖片

至此,invokedynamic指令具有的發起【一階段調用】的上下文如下:

  1. 具體的一階段調用的BSM方法:java.lang.invoke.LambdaMetafactory#metafactory
  2. IntStream.map方法需要的參數類型:IntUnaryOperator
  3. 編譯器(javac)編譯產生的包含Lambda表達式代碼內容的靜態方法:lambda$main$0(I)I

接下來就是調用java.lang.invoke.LambdaMetafactory#metafactory方法,傳遞上述必要的上下文參數,接受metafactory方法返回的IntUnaryOperator applyAsInt()類型的MethodHandle并調用該MethodHandle,繼而得到IntStream.map方法需要的參數:IntUnaryOperator。

LambdaMetafactory#metafactory

圖片圖片

如上述,invokedynamic指令調用上述metafactory方法,對照字節碼信息,可以得到如下具體參數表格:

圖片圖片

LambdaMetafactory根據上述上下文,使用ASM庫,動態生成了一個如下所示的IntUnaryOperator適配類,用于橋接Lambda表達式代碼塊到IntUnaryOperator類型。

添加-Djdk.internal.lambda.dumpProxyClasses=.啟動參數,JDK會將生成的適配函數式接口的類源碼輸出到工作目錄中。

構造CallSite

圖片圖片

java.lang.invoke.InnerClassLambdaMetafactory#buildCallSite

生成FunctionalInterface適配類后,基于適配類創建MethodHandle。該MethodHandle體現的代碼邏輯類似如下Java代碼:

圖片圖片

至此,invokedynamic【一階段】調用已經完成,invokedynamic指令獲取到了由LambdaMetafactory#metafactory作為BSM動態決策、動態生成的IntUnaryOperator適配類的【工廠方法】(以CallSite包裝的MethodHandle的形式)。

二階段調用

【一階段調用】已經完成,返回了動態決策產生的CallSite對象,getTarget方法可以獲取上述的IntUnaryOperator適配類的【工廠方法】。

圖片圖片

至此,invokedynamic指令可以通過如下偽代碼,創建IntStream.map方法需要的IntUnaryOperator實例。

IntUnaryOperator intUnaryOperator = (IntUnaryOperator)callSite.getTarget().invoke()

Lambda1的整個運行時解析、鏈接流程完成。

七、Lambda表達式性能

圖片圖片

經過上述分析我們可以知道,Lambda1這種無狀態的、沒有捕獲外部變量(閉包)的Lambda表達式的開銷是很小的,只會在第一次調用時動態生成橋接的適配類,實例化后就通過ConstantCallSite緩存。后續所有的調用都不會再重新生成適配類、實例化適配類。

但是,Lambda2則不同,因為Lambda捕獲、依賴了(閉包)外部變量num,那么這個表達式就是有狀態的。雖然同樣只是會在第一次調用時動態生成橋接的適配類,但是每一次調用都會使用num變量重新實例化一個新的適配類實例。這種場景下,其在性能和形式上就已經和傳統的【匿名內部類】沒有太大差別了。

Lambda3本質上和Lambda1一樣,只不過不需要Java編譯器在編譯時將Lambda代碼語句抽取成獨立的方法。

八、Lambda表達式和final變量

圖片圖片

當Lambda表達式閉包捕獲的局部變量num在方法內可變時,編譯器會提示編譯錯誤。這不是JVM的限制,而是Java語言層面的限制。筆者認為,這種限制沒有技術上的原因,而是Java語言設計者刻意的借助編譯器在阻止你犯錯。

假設沒有這個限制,那么Lambda表達式就變成了重構不友好的【位置相關】的代碼塊。

換句話說,下面兩種代碼執行結果是不一樣的:

圖片圖片

Lambda捕獲的num的值為5;

圖片圖片

Lambda捕獲的num的值為3;

如果沒有類似的編譯約束,當我們有心或無意的在復雜的業務邏輯中進行了類似的代碼調整時,極易出錯且難以排查。

九、總結

提筆的時候立意高遠,想著要盡可能通俗詳盡的寫清楚所有涉及的技術點,但是越寫越覺得事情不簡單,最后只能是把博客標題從【深入剖析】修改為【淺析】。這塊內容牽涉的面太廣,筆者沒有能力也沒有精力介紹到事無巨細、面面俱到,只能為大家拋磚引玉,大家可以配合后文【參考資料】多梳理、多實驗,同時在評論區批評指正。

  1. invokedynamic指令不是業務開發者使用的。invokedynamic指令可以用來實現Lambda語法,但是它不是只能用來實現Lambda語法。這個指令對于JVM語言開發者比如Kotlin、Groovy、JRuby、Jython等會比較重要。
  2. 沒有捕獲外部變量(閉包)的Lambda表達式性能和直接調用沒有差別。
  3. 捕獲外部變量(閉包)的Lambda表達式性能理論上和【匿名內部類】范式一樣,每次調用都會創建一個對象(最壞情況)。

本文使用的反編譯工具為:jclasslib Bytecode Viewer

(https://plugins.jetbrains.com/plugin/9248-jclasslib-bytecode-viewer)

十、附錄

自動生成的Lambda2適配類

// $FF: synthetic class
final class LambdaTest$$Lambda$2 implements IntUnaryOperator {
    private final int arg$1;


    private LambdaTest$$Lambda$2(int var1) {
        this.arg$1 = var1;
    }


    private static IntUnaryOperator get$Lambda(int var0) {
        return new LambdaTest$$Lambda$2(var0);
    }


    @Hidden
    public int applyAsInt(int var1) {
        return LambdaTest.lambda$main$1(this.arg$1, var1);
    }
}

自動生成的Lambda3適配類

// $FF: synthetic class
final class LambdaTest$$Lambda$3 implements IntUnaryOperator {
    private LambdaTest$$Lambda$3() {
    }


    @Hidden
    public int applyAsInt(int var1) {
        return LambdaTest.add(var1);
    }
}

參考:

  • Oracle-Java虛擬機規范(JDK8)--https://docs.oracle.com/javase/specs/jvms/se8/html/
  • Oracle-Java語言規范(JDK8)-https://docs.oracle.com/javase/specs/jls/se8/html/index.html
  • JVM系列之:JVM是怎么實現invokedynamic的? | HeapDump性能社區-https://heapdump.cn/article/3573623
  • Java 虛擬機:JVM是怎么實現invokedynamic的?(上)-https://cloud.tencent.com/developer/article/1787369
  • Java 虛擬機:JVM是怎么實現invokedynamic的?(下)-https://cloud.tencent.com/developer/article/1787371
  • 【stackoverflow】What is a bootstrap method?-https://stackoverflow.com/questions/30733557/what-is-a-bootstrap-method
  • Java中普通lambda表達式和方法引用本質上有什么區別?-https://www.zhihu.com/question/51491241/answer/126232275
  • 理解 invokedynamic-https://juejin.cn/post/6844903503236710414
  • https://www.cnblogs.com/wade-luffy/p/6058087.html
  • 09 | JVM是怎么實現invokedynamic的?(下)-深入拆解Java虛擬機-極客時間-https://time.geekbang.org/column/article/12574
責任編輯:武曉燕 來源: 得物技術
相關推薦

2023-02-15 19:07:30

Java字節碼指令

2009-09-17 09:09:50

Lambda表達式Linq查詢

2021-06-27 06:25:14

代碼優化技巧Java

2016-09-07 20:56:24

2009-09-14 13:44:14

Lambda ExprC# Lambda

2013-03-29 11:09:17

JVM內存

2009-08-27 11:43:31

C#語法

2009-08-14 00:30:09

C#條件編譯指令

2010-09-30 15:19:33

2009-08-18 12:52:33

C#枚舉類型

2020-05-14 10:47:13

Android Java 8

2015-04-09 10:18:21

網卡配置

2023-09-01 08:59:57

2013-01-05 02:19:50

JavaLambda表達式JVM

2009-07-06 12:49:33

JSP編譯器

2010-09-07 10:33:04

CSS

2009-09-17 09:20:45

C#操作XML

2009-07-02 11:34:42

JSP指令JSP開發

2009-07-27 13:34:15

ASP.NET編程

2010-01-21 09:34:57

C++語法
點贊
收藏

51CTO技術棧公眾號

久久精彩免费视频| 在线午夜精品自拍| av资源一区二区| 亚洲精品mv| 午夜精品视频一区| 深夜福利国产精品| 精品二区视频| 性生活免费观看视频| av成人毛片| 九九精品在线观看| 日本中文字幕视频在线| 日韩成人网免费视频| 国产激情在线播放| 欧美性xxxxxxxx| 色婷婷亚洲十月十月色天| 激情综合视频| 日韩av在线免费观看| 亚洲中文字幕无码中文字| 香蕉久久夜色精品国产| 欧美性猛交xxxx黑人| 国产又粗又大又爽的视频| 成人国产在线| 国产精品福利av| 亚洲欧美精品在线观看| 成人亚洲综合| 日韩免费视频一区二区| 在线免费观看高清视频色| 久久久精品免费免费| 8x8x视频在线| 一本一道久久久a久久久精品91| 三级在线免费观看| 日韩电影av| 国产激情视频一区二区在线观看| 日韩视频在线永久播放| 欧美另类videos| 成人免费精品视频| 欧美污视频网站| 国产精品国产自产拍高清av王其 | 亚洲国产高清不卡| 2019av中文字幕| 邻居大乳一区二区三区| 亚洲人成精品久久久久久| 国产va亚洲va在线va| 精品176极品一区| 日韩欧美色综合网站| 伪装者在线观看完整版免费| 奇米色一区二区三区四区| 中文字幕精品视频| 678在线观看视频| 在线观看亚洲精品| 亚洲精品.com| 欧洲亚洲免费在线| 激情视频一区| 激情网站五月天| 噜噜噜在线观看播放视频| 久久久久亚洲精品| 久久久久99精品国产片| 国产第一页在线| 一级做a爰片久久| 欧美日韩日本视频| 久久精品高清| 四色成人av永久网址| 久久久久久这里只有精品| 国产精品一区免费视频| 国产成人l区| 亚洲自拍中文字幕| 日韩国产欧美在线观看| 啊啊啊好爽视频| 中文字幕亚洲一区二区三区| 国产精品五区| 男女av在线| 国产成人精品在线视频| 蜜臀av性久久久久蜜臀aⅴ| 黄网站色大毛片| 亚洲五码中文字幕| 欧美激情影院| 亚洲人成网站免费播放| 国产伦精品一区二区三区免费优势 | 久久人人爽人人爽| 在线看免费av| 全亚洲最色的网站在线观看| 国产精品自拍一区| av在线女优影院| 国产精品久久国产愉拍| 午夜精品一区二区在线观看| 91免费视频大全| 国产传媒在线观看| www.久久撸.com| 欧美大胆的人体xxxx| 久久国产精品高清一区二区三区| 亚洲国产精品中文| 欧美xxav| www.99在线| 日韩毛片在线观看| 国产精品分类| www99热| 亚洲男人天堂网| 一本一道久久综合狠狠老精东影业| 成人免费看黄网址| 色老头一区二区三区在线观看| 国产精品vip| 永久www成人看片| 免费av在线一区| 国产电影一区二区三区| 性xxxxfjsxxxxx欧美| av免费精品一区二区三区| 国产精品一区二区三区四区在线观看 | 九色porny自拍视频在线观看| 国产在线播放不卡| 国产精品入口麻豆原神| 欧美片第1页| 污视频在线免费观看一区二区三区| 欧美日韩一区二区三区在线免费观看 | 午夜剧场成人观在线视频免费观看| 久草精品在线观看| 午夜视频成人| 国产欧美一区二区三区四区| 综合婷婷亚洲小说| 亚洲日本va| 黄色大片在线免费看| 亚洲欧美日韩图片| 国内精品伊人久久久久av一坑| 黄网站免费在线观看| 国产精品伊人日日| 欧美色图在线观看| 亚洲一级电影| 日本中文在线观看| 欧美另类高清视频在线| 91精品国产免费| 久久人人超碰| 91超碰在线| 午夜啪啪福利视频| 亚洲天堂av在线免费观看| 国产成人亚洲综合a∨婷婷图片 | 欧美色欧美亚洲高清在线视频| 精品一区不卡| 最新中文字幕在线观看| 成人在线免费观看视视频| 欧美日韩中文在线观看| 99久久99久久精品国产片桃花| 在线观看入口黄最新永久免费国产| 国产精品无码专区在线观看| 欧美日韩亚洲激情| 好吊一区二区三区| 韩国av网站在线| 成年人免费观看的视频| 亚洲色图美腿丝袜| 久久久久久免费网| 国产99精品一区| 久草视频视频在线播放| 精品久久sese| 日韩电影大片中文字幕| av影院午夜一区| 97久久亚洲| 伊人网在线视频| 91精品网站| 精品少妇一区二区| www.视频一区| 欧美精品一区二区性色a+v| 色狠狠久久aa北条麻妃| 亚洲免费在线观看视频| 一区二区三区在线电影| 24小时免费看片在线观看| 精品99在线视频| 成人黄色av网| 亚洲国产精品字幕| 国产欧美精品国产国产专区| 在线看片不卡| 成人影院入口| 五月天婷婷综合社区| 欧洲精品码一区二区三区免费看| 国产亚洲精品久久久久动| 中文字幕一区二区在线播放| 欧美精选一区| 欧美电影在线观看网站| 你懂的好爽在线观看| 大桥未久一区二区三区| 国产成人精品视频| 日韩精品在线网站| 亚洲欧洲日韩在线| 美女日韩在线中文字幕| 国产主播性色av福利精品一区| 一广人看www在线观看免费视频| 国产美女在线一区| 91日本视频在线| 中文字幕亚洲欧美日韩2019| 欧美视频裸体精品| 成人午夜伦理影院| 午夜日韩视频| 一区二区在线免费播放| 国产激情视频在线| free亚洲| 午夜啪啪免费视频| 日韩av观看网址| 日韩精品在线电影| 色综合久久中文综合久久牛| 久久精品在这里| 九色综合国产一区二区三区| 久久久久久影院| 欧美巨大xxxx|