精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Java程序中的潛在危機: 深入探討NullPointerException

開發 前端
在深入了解虛擬機如何處理NullPointerException之后,我們可以發現,表面上看似簡單的異常處理背后,實際上蘊藏著大量復雜的邏輯思考和設計上的平衡。

一、前言

在Java語言的世界里,處理錯誤和異常是每位開發者必須面對的重要課題。其中,NullPointerException無疑是最常見且令人頭痛的錯誤之一。它的出現往往讓我們措手不及,同時大概率會導致程序行為異常。盡管從最早的版本這個異常就貫穿在我們的編碼世界里,但它背后卻隱藏著深刻的歷史和設計哲學。 

二、一則趣聞

在討論今天的主題之前,讓我們先介紹一位計算機科學界的杰出人物:Tony Hoare。他在業界享有極高的聲譽,成就斐然,重要事跡和頭銜足以讓人頂禮膜拜:

  • 發明了廣為人知的快速排序算法
  • 1980年榮獲圖靈獎
  • 被選為美國國家工程院外籍院士、英國皇家工程院院士、牛津大學名譽教授

然而,Tony Hoare被大多數人所熟知的,還是他與空引用的故事。

1965年,Tony Hoare在設計ALGOL W語言時,引入了空引用Null Reference這一概念。他認為,空引用可以方便地表示無值或未知值。其設計初衷是借助編譯器的自動檢測機制,確保所有引用的使用都是絕對安全的。此外,這種設計思路在實現上相對簡單,大大減少了開發者的工作量。因此,受到Tony Hoare的影響,隨后幾十年中,許多編程語言,包括1991年誕生的Java(前身為Oak語言),也紛紛被這一設計思路所影響。

然而,隨著時間的推移,Hoare對自己當年引入空引用的決策進行了深刻的反思。在2009年,他坦言:

“我將我之前發明的空引用的處理稱為十億美元的錯誤。1965年,我在為一種面向對象的語言(ALGOL W)設計第一個全面的引用類型系統時,目標是確保所有引用的使用都應該是絕對安全的,由編譯器自動進行檢查。但我無法抵擋引入空引用的誘惑,因為這實在是太容易實現了。這導致了無數錯誤、漏洞和系統崩潰,可能在過去四十年里造成了十億美元的損失和痛苦。”

但從今天的軟件系統發展來看,空引用對業界的影響遠不止這一數字。它不僅改變了程序設計的方式,也引發了對異常處理、內存管理等眾多領域的深入思考。

三、空引用檢查

空引用識別

我們先來想一個問題:虛擬機是如何識別到空引用的呢?

  • JDK底層封裝識別
  • 字節碼層面識別
  • 機器碼層面識別
  • 類型檢查
  • 內存數據分析

在不考慮實現復雜度的情況下,我們很快可以列舉出上述可能的識別方向,但Java虛擬機這邊給出了一種意料之外的解決方案:不主動識別。

這可能會讓很多研發人大跌眼鏡。大家可能會想,Java作為一門風靡全球的語言,應該有細致且周全的檢查空引用的邏輯,但實際卻和大家想的恰恰相反。

public static int getSize(List first, List second, List third, List fourth) {
    return first.size() + second.size() + third.size() + fourth.size();
}

上述代碼累加了多個列表的大小,理論上每個列表對象都可能是個空值。如果按照我們預想的對于每個對象引用做空是否為空的檢查,那么對于每個列表對象都會做一次檢查,這次檢查會至少涉及到一條機器碼比較指令。這個成本對于當下的Java應用程序來說是巨大且不可接受的。

所以權衡之后虛擬機的開發者們采用了一種類似于Try-Catch的解決方案,白話一點的意思就是:我們并不實時去檢查是否可能有空的引用,因為絕大多數情況下空引用都是少數情況,但是如果真的發生了我們保證一定會處理(拋出NullPointerException)。

檢查細節

下面代碼是JDK8的虛擬機內部判別是否需要檢查空引用的實現,調用鏈路依次如圖中所示。入口處的注釋This platform only uses signal-based null checks. The Label is not needed就已經告訴我們了足夠多的信息,意思是在x86環境下,使用了基于signal的方式來完成了空的檢查,至于什么是signal我們先按下不表。

進一步的由于offset使用默認值,needs_explicit_null_check函數(是否需要顯式的進行空引用檢查)會返回false。這會導致最終函數null_check里什么也不做,僅有一行注釋nothing to do, (later) access of M[reg + offset] will provoke OS NULL exception if reg = NULL。這里的代碼注釋已經足夠直白,告訴我們如果空引用的情況下,訪問內存的時候會觸發操作系統層面的異常。

==================== c1_MacroAssembler_x86.hpp ====================
// This platform only uses signal-based null checks. The Label is not needed.
void null_check(Register r, Label *Lnull = NULL) { MacroAssembler::null_check(r); }




==================== macroAssembler_x86.cpp ====================
void MacroAssembler::null_check(Register reg, int offset) {
  if (needs_explicit_null_check(offset)) {
    // provoke OS NULL exception if reg = NULL by
    // accessing M[reg] w/o changing any (non-CC) registers
    // NOTE: cmpl is plenty here to provoke a segv
    cmpptr(rax, Address(reg, 0));
    // Note: should probably use testl(rax, Address(reg, 0));
    //       may be shorter code (however, this version of
    //       testl needs to be implemented first)
  } else {
    // nothing to do, (later) access of M[reg + offset]
    // will provoke OS NULL exception if reg = NULL
  }
}


==================== assembler.cpp ====================
bool MacroAssembler::needs_explicit_null_check(intptr_t offset) {
  // Exception handler checks the nmethod's implicit null checks table
  // only when this method returns false.
#ifdef _LP64
  if (UseCompressedOops && Universe::narrow_oop_base() != NULL) {
    assert (Universe::heap() != NULL, "java heap should be initialized");
    // The first page after heap_base is unmapped and
    // the 'offset' is equal to [heap_base + offset] for
    // narrow oop implicit null checks.
    uintptr_t base = (uintptr_t)Universe::narrow_oop_base();
    if ((uintptr_t)offset >= base) {
      // Normalize offset for the next check.
      offset = (intptr_t)(pointer_delta((void*)offset, (void*)base, 1));
    }
  }
#endif
  return offset < 0 || os::vm_page_size() <= offset;
}

四、空引用操作系統處理

我們回過頭再看上面代碼中的注釋:

nothing to do, (later) access of M[reg + offset] will provoke OS NULL exception if reg = NULL

它明確的告訴了我們觸發的細節,也就是當真的碰到了空引用,此時的流程應該是這樣:

  • 空引用時寄存器里的地址也為空
  • 基于寄存器內的空地址從內存讀取會觸發操作系統層面的Exception

那這個操作系統的層面到底是什么呢?

初見SIGSEGV

Linux下把信號分為了兩大類:可靠信號與不可靠信號。不可靠信號有可能丟失、順序問題等特點。其中我們日常遇見的信號基本都在不可靠信號這個區間內。這里列舉一些場景的信號:

圖片圖片

而尤以SIGSEGV這個信號尤為重要和常見。它意味著此時發生了無效的內存訪問,而虛擬機對于NullPointerException的識別便是依靠著SIGSEGV才能完成。 

SIGSEGV捕獲

操作系統對于所有的信號都有其默認行為。對于大部分不可靠信號來說,它的默認行為都是終止當前進程,有些場景下會同時生成核心轉儲文件。這意味著如果進程收到SIGSEGV信號其實是一件非常嚴重的事情,但操作系統層面同時也考慮到了擴展性: 雖然默認行為是終止進程,但是如果開發者確認這是個正常行為,那么可以嘗試攔截這樣的情況別忽略。所以操作系統在這里提供了回調方法的注冊,開發可以自行注冊回調來識別正常行為的信號。

如下是OpenJDK9中虛擬機的代碼,3個方法主要做了三件事情:

  • install_signal_handlers(): 虛擬機啟動時注冊信號,這里完成了SIGSEGV的捕獲注冊
  • set_signal_handler(): 設置回調函數為signalHandler
  • signalHandler(): 進一步調用抽象的JNI函數JVM_handle_linux_signal

這里需要說明的是函數JVM_handle_linux_signal,它定義在os_linux.cpp下,但由于Linux平臺下還有更細的架構劃分,如x86、aarch64、arm、ppc、s390、sparc等,在不同的架構下有不同的實現,所以這里要抽象出統一的函數模型。

==================== os_linux.cpp ====================
void os::Linux::install_signal_handlers() {
    ...
    set_signal_handler(SIGSEGV, true);
    set_signal_handler(SIGPIPE, true);
    set_signal_handler(SIGBUS, true);
    set_signal_handler(SIGILL, true);
    set_signal_handler(SIGFPE, true);
    ...
}


void os::Linux::set_signal_handler(int sig, bool set_installed) {
    ...  
    if (!set_installed) {
        sigAct.sa_flags = SA_SIGINFO|SA_RESTART;
    } else {
        sigAct.sa_sigaction = signalHandler;
        sigAct.sa_flags = SA_SIGINFO|SA_RESTART;
    }
    ...
}


void signalHandler(int sig, siginfo_t* info, void* uc) {
  assert(info != NULL && uc != NULL, "it must be old kernel");
  int orig_errno = errno;  // Preserve errno value over signal handler.
  JVM_handle_linux_signal(sig, info, uc, true);
  errno = orig_errno;
}


==================== os_linux_x86.cpp ==================== 
extern "C" JNIEXPORT int
JVM_handle_linux_signal(int sig,
                        siginfo_t* info,
                        void* ucVoid,
                        int abort_if_unrecognized) {
    ...                        
}

SIGSEGV捕獲后的行為

由于我們當前生產環境多為x86架構,所以這里我們只用關注os_linux_x86.cpp下的實現即可。這里可以看到一下的細節:

  • NullPointerException下的SIGSEGV處理:設置攔截后的跳轉代碼,這里是SharedRuntime::continuation_for_implicit_exception,該函數負責拋出Java層面的NullPointerException。
  • ucontext_set_pc: 重置PC寄存器,更改代碼執行行為,直接執行continuation_for_implicit_exception,這樣接下來就會拋出NullPointerException
  • VMError::report_and_die等同于信號的默認語義,直接終止進程。

到此,NullPointerException從產生到拋出的全過程我們都有了了解。如下方注釋所說,當虛擬機收到操作系統回調時,如果發現是SIGSEGV信號且對應的內存offset為0,會主動返回并拋出NullPointerException,系統也并不會崩潰。

==================== os_linux_x86.cpp ==================== 


extern "C" JNIEXPORT int
JVM_handle_linux_signal(int sig,
                        siginfo_t* info,
                        void* ucVoid,
                        int abort_if_unrecognized) {     
    ......
   
    // 這里處理NullPointerException的情況
    if (sig == SIGSEGV &&
               !MacroAssembler::needs_explicit_null_check((intptr_t)info->si_addr)) {
          // Determination of interpreter/vtable stub/compiled code null exception
          stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL);
      }
    }   
  }
 
  ...


  // StackoverflowError和NullPointerException會主動返回并被記錄, 系統不掛
  if (stub != NULL) {
    // save all thread context in case we need to restore it
    if (thread != NULL) thread->set_saved_exception_pc(pc);


    os::Linux::ucontext_set_pc(uc, stub);
    return true;
  }
 
  ...
  
  // 虛擬機不主動處理的信號到達這里會觸發系統掛掉 
  VMError::report_and_die(t, sig, pc, info, ucVoid);


  ShouldNotReachHere();
  return true; // Mute compiler
}

五、使用信號量的隱含風險 

頻繁的空引用

JVM規范只是規定了當遇見空引用需要拋出空指針異常,但在具體實現的細節上,NullPointerException的監測和拋出多少有點超出了我們的想象,但從結果看它確實是符合JVM規范的行為。同時當前方案的好處也顯而易見,它將本來需要顯式的檢查一個引用是否為空的代碼轉換為了隱式的檢查(可以理解為和虛擬機核心邏輯處理流程解耦了),算是很精妙的設計。

那么到這里可能就有人會問了,如果我們代碼寫的很爛到處都是空引用呢?這樣的話NullPointerException要通過發信號、信號處理、跳轉到空指針檢查的后續處理代碼的路徑,比起直接生成顯式檢查的路徑要長得多也慢得多,豈不是得不償失?實際上也確實是這樣,但虛擬機的開發者就是在做一種假設:一個正常健康運行的系統就不應該會有這么多的空指針異常,如果真出現大量異常,開發者應該先去檢查自身代碼的健壯性。 

信號量資源共享

在程序開發里一個非常重要的細節就是,你一定要管控好你的程序的作用域。如果在管控域之外的行為需要多加留意。回到這個問題本身,由于JVM采用了操作系統級別的信號量來同步NullPointerException信息,這在JVM本身內部并無問題,但由于JVM可以加載JNI代碼,如果加載的第三方JNI中也捕獲了SIGSEGV信號,這便會導致虛擬機自身的捕獲失效,屆時面對一個普通的NullPointerException都會導致系統崩潰。

下面是一個簡單的例子,大家可以在Linux環境嘗試:

// NPETest.java
import java.util.UUID;
public class NPETest {
    public static void main(String[] args) throws Exception {
        System.loadLibrary("NPETest");
        UUID.fromString(null);
    }
}


// NPETest.c
#include <signal.h>
#include <jni.h>


JNIEXPORT
jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
    signal(SIGSEGV, SIG_DFL);
    return JNI_VERSION_1_8;
}

我們可以將這個例子打包成一個shell腳本來執行:

gcc -Wall -shared -fPIC NPETest.c -o libNPETest.so -I$JAVA_HOME/include -I$JAVA_HOME/include/linux
javac NPETest.java
java -Xcomp -Djava.library.path=. -cp . NPETest

如上是一個簡單的例子,當加載的JNI代碼中存在手工捕獲了SIGSEGV之后,面對NullPointerException虛擬機只能無奈以崩潰告終,并生成堆轉儲文件。

圖片圖片

如果我們將JNI中的信號量捕獲代碼signal(SIGSEGV, SIG_DFL);注釋掉,即可看到正常的異常拋出:

圖片圖片

六、JDK的改進

Optional

Optional是JDK8引入的一個容器類,旨在提供一種更安全且清晰的方式來處理可能為空的值,從而減少 NullPointerException的發生。通過使用Optional,開發者可以明確地表示某個值可能缺失,這種設計促使開發者在代碼中顯式處理缺失值的情況,增強了代碼的健壯性和可讀性。Optional類提供了一系列便捷的方法,如isPresent()來檢查值是否存在、ifPresent()以避免空值的直接處理、orElse()用于提供默認值,以及map()和flatMap()方法以支持函數式編程風格的鏈式操作。這些特性不僅使代碼更簡潔,而且幫助開發者以更直觀的方式處理空值,提高了代碼的可維護性和可理解性。

需要指出的是,Optional最早是由Google Guava庫開發的。這一設計旨在提供一種更安全的方式來處理可能為空的值,減少空指針異常的發生。2014年發布的JDK8 中引入的Optional類,實際上是基于Guava的設計思想進行了改進和擴展。JDK8的Optional不僅保持了Guava的核心理念,還增加了一些新的方法和特性,使得開發者能夠以更簡潔和直觀的方式處理缺失值,從而提高代碼的可讀性和可維護性。

異常提示細化

隨著時間的推移,越來越多的開發者對于NullPointerException提出了更高的要求:

  • 開發者在調試時花費大量時間尋找導致NullPointerException的原因(特別是鏈式調用的場景)
  • 隨著編程語言的發展,許多現代語言已經提供了更好的空值處理和更有用的異常信息。但Java 作為一個成熟且廣泛使用的語言,卻沒有跟上這種趨勢

以下面代碼為例,研發就較難在第一時間決策出到底是代碼中的哪個返回是空才導致了NPE的發生:

System.out.println(earth.getAsian().getCountryList().size()); // NullPointerException

于是基于以上的訴求,Goetz Lindenmaier(在SAP負責JIT編譯器技術相關工作,是SAP的IA64移植的作者之一)發起了提案JEP 358: Helpful NullPointerExceptions, 核心主旨在于:通過準確指明哪個變量為 null,增強JVM生成的NullPointerExceptions的可用性。

對應該提案的內容在JDK14上正式生效。從這個版本開始,如果產生了NullPointerException,JVM可以給出詳細的信息告訴我們空對象到底是誰(需開啟-XX:+ShowCodeDetailsInExceptionMessages)。 

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "org.example.Main$Earth.getAsian()" because "earth" is null
        at org.example.Main.main(Main.java:8)

七、結語

在深入了解虛擬機如何處理NullPointerException之后,我們可以發現,表面上看似簡單的異常處理背后,實際上蘊藏著大量復雜的邏輯思考和設計上的平衡。這不僅涉及到如何有效捕獲和報告錯誤,還包括在性能、內存管理和用戶體驗之間進行權衡。Java虛擬機在設計時需要考慮到多種因素,例如如何迅速反饋給開發者,同時又不影響程序的整體性能和穩定性。通過深入分析這一過程,我們能夠更好地理解異常處理機制的內在原理,這不僅提升了我們的編程技能,也為我們在開發過程中處理類似問題提供了更深刻的視角和解決方案。希望本文能夠為你提供一些有價值的見解與幫助,激發你的進一步探索和思考。

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

2023-10-23 12:35:36

Golang追加操作

2009-12-23 16:13:00

WPF Attache

2011-02-25 09:23:00

Java類加載器

2015-09-02 08:57:56

JavaHashMap工作原理

2017-05-10 21:28:00

Java異常與錯誤處理

2010-03-31 14:58:03

云計算

2024-01-04 07:42:44

JavaCGLIBJDK

2009-12-07 16:07:03

PHP類的繼承

2010-11-22 14:18:32

MySQL鎖機制

2010-07-21 09:38:15

PHP緩存技術

2009-11-20 17:17:08

Oracle函數索引

2021-05-17 05:36:02

CSS 文字動畫技巧

2009-06-16 10:51:14

Java源碼

2024-01-24 08:31:13

extends?接口規范

2009-12-01 16:34:21

PHP表單

2009-10-16 18:20:07

綜合布線系統

2023-01-12 17:18:06

數據庫多云

2010-03-05 13:44:00

Python序列

2009-08-27 11:27:58

foreach語句C# foreach語

2010-03-15 16:31:34

Java多線程
點贊
收藏

51CTO技術棧公眾號

国外色69视频在线观看| 色婷婷亚洲综合| 亚洲国产二区| 制服丝袜成人动漫| 2018中文字幕第一页| 欧美一区二区在线观看视频| 亚洲一区二区动漫| 一本色道久久综合狠狠躁篇的优点| 无码内射中文字幕岛国片| 少妇人妻偷人精品一区二区| 三级在线观看一区二区| 久久福利视频导航| 97超碰在线免费观看| 在线视频成人| 精品久久久一区二区| 中文字幕成人一区| 亚洲欧洲精品视频| 国产精品一区不卡| 日韩美女在线播放| 国产亚洲精品成人| 日本a口亚洲| 亚洲第一精品久久忘忧草社区| 欧美日韩怡红院| 成人三级高清视频在线看| 国产嫩草影院久久久久| 成人区精品一区二区| 国产小视频在线免费观看| 一二三区不卡| 中文字幕精品av| avtt香蕉久久| 911亚洲精品| 69久久99精品久久久久婷婷| 国产极品美女高潮无套久久久| 先锋成人av| 最新成人av在线| 一区二区免费在线观看| 看电影就来5566av视频在线播放| 高清视频一区二区| 亚洲已满18点击进入在线看片| 成人小视频在线播放| 国产精品免费看| 韩国三级电影久久久久久| 波多野结衣亚洲色图| 久久精品国产99久久| 亚洲片在线资源| 亚洲熟妇无码av| 婷婷精品在线观看| 日韩成人中文字幕| 特级西西人体wwwww| 国产精品一区二区三区美女| 精品剧情v国产在线观看在线| 五月天六月丁香| 99精品美女视频在线观看热舞| 欧美亚洲国产一区在线观看网站| 欧美丰满熟妇xxxxx| 国产免费不卡| 欧美最新大片在线看| 国产一级不卡毛片| 电影亚洲精品噜噜在线观看| 色网站国产精品| 久久午夜夜伦鲁鲁一区二区| 在线一区视频观看| 欧美日韩一区二区在线观看| 免费看污黄网站| 免费一级欧美在线观看视频| 精品久久久久久国产91| 欧美日韩国产精品激情在线播放| 日本免费一区二区三区四区| 欧美伊人久久大香线蕉综合69| 手机看片福利盒子久久| 色综合一区二区日本韩国亚洲| 欧美日韩午夜在线| 伊人影院在线观看视频| 国产主播性色av福利精品一区| 欧美电影精品一区二区| 亚洲一区二区三区四区av| 欧美重口另类| 中文字幕亚洲一区二区三区| 成人免费毛片xxx| 激情综合激情| 国产精品7m视频| 国产美女精品视频国产| 成人av高清在线| 欧美人与物videos另类| 3p视频在线观看| 亚洲午夜成aⅴ人片| 日本网站免费在线观看| 精品美女一区| 亚洲高清福利视频| 性少妇xx生活| 亚洲美女一区| 成人www视频在线观看| 成人激情四射网| 国产亚洲一区字幕| 国产一级大片免费看| 中文在线8资源库| 51精品国自产在线| 一级黄色片大全| 欧美在线高清| 国产成人精品久久二区二区91| 国产人妖一区二区三区| 91麻豆免费视频| 男人j进女人j| 午夜精品成人av| 精品久久久网站| 少妇视频一区二区| 亚洲综合不卡| 97se亚洲综合| 91在线免费看| 精品久久久一区| 免费黄色在线播放| 91亚洲一区| 国产97在线|亚洲| 亚洲免费成人在线| 中文字幕五月欧美| 欧美一级片中文字幕| 日韩精品一区国产| 最新亚洲国产精品| 西西44rtwww国产精品| 国产麻豆精品久久一二三| 日韩国产伦理| 中文不卡1区2区3区| 欧美精品一区二区蜜臀亚洲| 97精品在线播放| 日本视频免费一区| 麻豆av一区二区三区久久| 青春草免费在线视频| 欧美日韩在线观看一区二区 | 欧美精品久久久久久久久46p| 国产美女诱惑一区二区| 国产91亚洲精品一区二区三区| 免费看a在线观看| 欧美在线免费视屏| 国精产品一区二区三区| 国产精品久久777777毛茸茸 | 性感美女视频一二三| 亚洲制服丝袜av| 亚洲综合中文网| 一本一道久久综合狠狠老| 国产欧美中文字幕| 亚洲视频tv| 欧美日韩亚洲综合| 国产农村妇女精品一区| 青青草成人在线观看| 日本高清久久一区二区三区| 92国产精品| 亚洲精品日韩丝袜精品| 69成人免费视频| 久久综合狠狠综合久久综合88| 91丨porny丨探花| 神马久久影院| 国产精品激情av在线播放| 国产精品一区二区三区四区色| 在线免费观看成人短视频| 这里只有久久精品| 蜜乳av一区二区| 综合操久久久| 日本成人精品| 欧美国产亚洲精品久久久8v| 国产18精品乱码免费看| 亚洲国产wwwccc36天堂| 国产激情视频网站| 老鸭窝91久久精品色噜噜导演| 欧美一区亚洲二区| 日韩欧美激情| 欧美激情国产精品| 五月婷婷六月丁香| 在线视频一区二区三区| 午夜影院黄色片| 国产一区二区免费在线| 国产精品videossex国产高清| 成人春色在线观看免费网站| 97国产在线视频| 搞黄视频免费在线观看| 欧美精品乱码久久久久久| 麻豆成人在线视频| 91在线观看高清| 欧美精品久久久久久久久25p| 欧美a级一区| 久久偷窥视频| 国产伊人久久| 久久久久久亚洲精品中文字幕| 青青草免费在线| 777欧美精品| 日韩av在线播| 国产精品视频一二三| 四虎国产精品免费| 老司机精品福利视频| 国产资源第一页| 久久综合欧美| 国产精成人品localhost| 偷拍精品精品一区二区三区| 欧美成人黄色小视频| 日本一区高清| 欧美一区二区三区小说| 亚洲影院在线播放| 亚洲欧美另类久久久精品 | 国产精品久久久久7777按摩| 91人人澡人人爽| 日本伊人色综合网| 黄色一级片黄色| 欧美韩国日本在线观看| 精品视频第一区| 天堂精品久久久久| 国产精品美女久久| 欧美freesex黑人又粗又大| 色偷偷88888欧美精品久久久| 少妇一级淫片免费看| 91精品欧美福利在线观看| 免费又黄又爽又猛大片午夜| 亚洲国产一区在线观看| 亚洲一级生活片| 中文字幕不卡在线| 草草地址线路①屁屁影院成人| 国产一区二区在线观看免费| 中文字幕在线观看第三页| 亚洲精品日韩久久| av中文字幕av| 久久精品国产大片免费观看| 日本不卡一二三区| 蜜臀av免费一区二区三区| 高清国产在线一区| 久久爱www.| 国产欧美日韩最新| 欧美日韩在线精品一区二区三区激情综合| 久久男人的天堂| 天堂va在线| 欧美成人免费全部| www.欧美日本韩国| 麻豆乱码国产一区二区三区 | 国产高清精品软件丝瓜软件| 欧美色大人视频| 狠狠狠狠狠狠狠| 欧美性xxxxxx| 亚洲日本视频在线观看| 精品久久久久久久久久| 中文字幕日韩一级| 午夜国产精品影院在线观看| 国产极品在线播放| 亚洲国产成人av好男人在线观看| 男人的天堂久久久| 亚洲精品日韩综合观看成人91| 日本黄色片免费观看| 综合久久一区二区三区| 国产一区二区三区视频播放| 国产精品日韩精品欧美在线| 亚洲一级片在线播放| 中文字幕精品一区| 国产黄色片在线| 亚洲欧洲在线观看av| 朝桐光av在线| 亚洲一二三区不卡| 国产精品一区二区6| 欧美性xxxxxxxxx| 中日韩av在线| 91麻豆精品国产91久久久久久久久| 国产精品人妻一区二区三区| 欧美一级日韩不卡播放免费| 精品人妻伦一二三区久久| 日韩欧美国产三级| 午夜视频免费在线| 亚洲欧美日韩在线一区| chinese偷拍一区二区三区| 日韩中文娱乐网| 日韩成人伦理| 亚洲2020天天堂在线观看| 国产精品av一区二区三区| 国产精品久久久久久影视| 91精品麻豆| 国产综合色一区二区三区| 西野翔中文久久精品国产| 日韩一区不卡| 综合久久婷婷| 日韩精品一区二区三区久久| 日韩av不卡一区二区| 黄色三级视频在线播放| av电影在线观看一区| 国产黄色录像视频| 亚洲一区日韩精品中文字幕| 国产精品21p| 91麻豆精品国产91久久久资源速度 | 日韩视频在线观看国产| 无码一区二区三区视频| 男人添女人下部高潮视频在观看 | 午夜激情视频网| caoporm超碰国产精品| 97在线观看免费视频| 有码一区二区三区| 四虎影院在线免费播放| 欧美一区在线视频| 青青草视频在线观看| 久久99热精品| 成人精品动漫| 久久大片网站| 中文字幕一区二区av| 欧美日韩中文在线视频| 国产精品 欧美精品| 精品亚洲aⅴ无码一区二区三区| 一区二区三区av电影 | 欧美精品一区二区三区一线天视频| 国产精品秘入口| 国内久久久精品| 免费一区二区三区四区| 国产一区再线| 影音先锋日韩精品| 精品久久久久久中文字幕2017| 国产成人在线色| 日韩av毛片在线观看| 欧美日韩亚洲一区二区| www.成人在线观看| 色综久久综合桃花网| 黄色成人免费网| 国产精品9999久久久久仙踪林| 日韩欧美在线中字| 国产女女做受ⅹxx高潮| 岛国精品一区二区| 永久免费看黄网站| 777亚洲妇女| 天堂资源在线中文| 国产不卡av在线| 免费成人蒂法| 日本一区午夜艳熟免费| 国产在线播放一区二区三区| 欧美成人久久久免费播放| 日本韩国一区二区三区| 性xxxx视频播放免费| 午夜免费在线观看精品视频| 91午夜精品| 人妻激情另类乱人伦人妻| 韩国视频一区二区| 国产高清视频免费在线观看| 91国模大尺度私拍在线视频| 欧洲亚洲精品视频| 欧美亚洲成人精品| 青青久久av| 97国产精东麻豆人妻电影| 福利一区福利二区| 久久这里只有精品免费| 精品欧美黑人一区二区三区| 永久免费网站在线| 成人在线观看91| 亚洲手机视频| 手机在线看片日韩| 欧美性猛交xxxx免费看| 天堂在线一二区| 日韩av电影在线播放| 精品国产一区二区三区久久久蜜臀| 国产精品人人妻人人爽人人牛| 欧美国产激情二区三区 | 另类综合日韩欧美亚洲| 亚洲成a人v欧美综合天堂下载 | 无码国产伦一区二区三区视频| 欧美日韩ab片| 久草在线综合| 欧美牲交a欧美牲交aⅴ免费下载| 久久欧美一区二区| 中文av免费观看| 日韩色av导航| 午夜日韩影院| 俄罗斯av网站| 国产肉丝袜一区二区| 又骚又黄的视频| 久久夜精品va视频免费观看| 91久久精品无嫩草影院| 97国产精东麻豆人妻电影| 国产人妖乱国产精品人妖| 91国内精品久久久| 欧美极品在线视频| 亚洲三级网址| 手机av在线免费| 亚洲午夜一区二区三区| 欧美孕妇性xxxⅹ精品hd| 国产精品成人免费电影| 午夜精品毛片| 喷水视频在线观看| 欧美中文字幕一区二区三区 | 日本黄色不卡视频| 日本伊人精品一区二区三区介绍| 久久精品国产www456c0m| 国产免费无码一区二区| 欧美天堂在线观看| 欧美r级在线| 精品欧美一区二区三区久久久 | 91精品国产综合久久久久久豆腐| 亚洲一区二区三区在线视频 | 亚洲伊人色欲综合网| 免费在线看v| 99久久久精品免费观看国产| 性色一区二区| 欧美爱爱小视频| 亚洲欧美在线免费观看| 国产精品视频一区二区三区| 欧美精品99久久| 亚洲欧美激情插| 国产在线色视频| 国产成人av一区二区三区| 日本v片在线高清不卡在线观看| 久久精品美女视频| 日韩专区中文字幕| 久久99久久人婷婷精品综合|