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

深入理解 Netty FastThreadLocal

開發
本文以線上詭異問題為切入點,通過對比JDK ThreadLocal和Netty FastThreadLocal實現邏輯以及優缺點,并深入解讀源碼,由淺入深理解Netty FastThreadLocal。

一、前言

最近在學習Netty相關的知識,在看到Netty FastThreadLocal章節中,回想起一起線上詭異問題。

問題描述:外銷業務獲取用戶信息判斷是否支持https場景下,獲取的用戶信息有時候竟然是錯亂的。

問題分析:使用ThreadLocal保存用戶信息時,未能及時進行remove()操作,而Tomcat工作線程是基于線程池的,會出現線程重用情況,所以獲取的用戶信息可能是之前線程遺留下來的。

問題修復:ThreadLocal使用完之后及時remove()、ThreadLocal使用之前也進行remove()雙重保險操作。

接下來,我們繼續深入了解下JDK ThreadLocal和Netty FastThreadLocal吧。

二、JDK ThreadLocal介紹

ThreadLocal是JDK提供的一個方便對象在本線程內不同方法中傳遞、獲取的類。用它定義的變量,僅在本線程中可見,不受其他線程的影響,與其他線程相互隔離

那具體是如何實現的呢?如圖1所示,每個線程都會有個ThreadLocalMap實例變量,其采用懶加載的方式進行創建,當線程第一次訪問此變量時才會去創建。

ThreadLocalMap使用線性探測法存儲ThreadLocal對象及其維護的數據,具體操作邏輯如下:

假設有一個新的ThreadLocal對象,通過hash計算它應存儲的位置下標為x。

此時發現下標x對應位置已經存儲了其他的ThreadLocal對象,則它會往后尋找,步長為1,下標變更為x+1。

接下來發現下標x+1對應位置也已經存儲了其他的ThreadLocal對象,同理則它會繼續往后尋找,下標變更為x+2。

直到尋找到下標為x+3時發現是空閑的,然后將該ThreadLocal對象及其維護的數據構建一個entry對象存儲在x+3位置。

在ThreadLocalMap中數據很多的情況下,很容易出現hash沖突,解決沖突需要不斷的向下遍歷,該操作的時間復雜度為O(n),效率較低

圖1

從下面的代碼中可以看出:

Entry 的 key 是弱引用,value 是強引用。在 JVM 垃圾回收時,只要發現弱引用的對象,不管內存是否充足,都會被回收。

但是當 ThreadLocal 不再使用被 GC 回收后,ThreadLocalMap 中可能出現 Entry 的 key 為 NULL,那么 Entry 的 value 一直會強引用數據而得不到釋放,只能等待線程銷毀,從而造成內存泄漏

static class ThreadLocalMap {
    // 弱引用,在資源緊張的時候可以回收部分不再引用的ThreadLocal變量
    static class Entry extends WeakReference<ThreadLocal<?>> {
        // 當前ThreadLocal對象所維護的數據
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    // 省略其他代碼
}

綜上所述,既然JDK提供的ThreadLocal可能存在效率較低和內存泄漏的問題,為啥不做相應的優化和改造呢?

1.從ThreadLocal類注釋看,它是JDK1.2版本引入的,早期可能不太關注程序的性能。

2.大部分多線程場景下,線程中的ThreadLocal變量較少,因此出現hash沖突的概率相對較小,及時偶爾出現了hash沖突,對程序的性能影響也相對較小。

3.對于內存泄漏問題,ThreadLocal本身已經做了一定的保護措施。作為使用者,在線程中某個ThreadLocal對象不再使用或出現異常時,立即調用 remove() 方法刪除 Entry 對象,養成良好的編碼習慣。

三、Netty FastThreadLocal介紹

FastThreadLocal是Netty中對JDK提供的ThreadLocal優化改造版本,從名稱上來看,它應該比ThreadLocal更快了,以應對Netty處理并發量大、數據吞吐量大的場景。

那具體是如何實現的呢?如圖2所示,每個線程都會有個InternalThreadLocalMap實例變量。

每個FastThreadLocal實例創建時,都會采用AtomicInteger保證順序遞增生成一個不重復的下標index,它是該FastThreadLocal對象維護的數據應該存儲的位置。

讀寫數據的時候通過FastThreadLocal的下標 index 直接定位到該FastThreadLocal的位置,時間復雜度為 O(1),效率較高。

如果該下標index遞增到特別大,InternalThreadLocalMap維護的數組也會特別大,所以FastThreadLocal是通過空間換時間來提升讀寫性能的。

圖2

四、Netty FastThreadLocal源碼分析

4.1 構造方法

public class FastThreadLocal<V> {
    // FastThreadLocal中的index是記錄了該它維護的數據應該存儲的位置
    // InternalThreadLocalMap數組中的下標, 它是在構造函數中確定的
    private final int index;
    public InternalThreadLocal() {
        index = InternalThreadLocalMap.nextVariableIndex();
    }
    // 省略其他代碼
}
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
    // 自增索引, ?于計算下次存儲到Object數組中的位置
    private static final AtomicInteger nextIndex = new AtomicInteger();
    private static final int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8;
    public static int nextVariableIndex() {
        int index = nextIndex.getAndIncrement();
        if (index >= ARRAY_LIST_CAPACITY_MAX_SIZE || index < 0) {
            nextIndex.set(ARRAY_LIST_CAPACITY_MAX_SIZE);
            throw new IllegalStateException("too many thread-local indexed variables");
        }
        return index;
    }
    // 省略其他代碼
}

上面這兩段代碼在Netty FastThreadLocal介紹中已經講解過,這邊就不再重復介紹了。

4.2 get 方法

public class FastThreadLocal<V> {
    // FastThreadLocal中的index是記錄了該它維護的數據應該存儲的位置
    private final int index;
    public final V get() {
        // 獲取當前線程的InternalThreadLocalMap
        InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
        // 根據當前線程的index從InternalThreadLocalMap中獲取其綁定的數據
        Object v = threadLocalMap.indexedVariable(index);
        // 如果獲取當前線程綁定的數據不為缺省值UNSET,則直接返回;否則進行初始化
        if (v != InternalThreadLocalMap.UNSET) {
            return (V) v;
        }
        return initialize(threadLocalMap);
    }
    // 省略其他代碼
}
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
    private static final int INDEXED_VARIABLE_TABLE_INITIAL_SIZE = 32;
    // 未賦值的Object變量(缺省值),當?個與線程綁定的值被刪除之后,會被設置為UNSET
    public static final Object UNSET = new Object();
    // 存儲綁定到當前線程的數據的數組
    private Object[] indexedVariables;
    // slowThreadLocalMap為JDK ThreadLocal存儲InternalThreadLocalMap
    private static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap =
            new ThreadLocal<InternalThreadLocalMap>();
    // 從綁定到當前線程的數據的數組中取出index位置的元素
    public Object indexedVariable(int index) {
        Object[] lookup = indexedVariables;
        return index < lookup.length? lookup[index] : UNSET;
    }
    public static InternalThreadLocalMap get() {
        Thread thread = Thread.currentThread();
        // 判斷當前線程是否是FastThreadLocalThread類型
        if (thread instanceof FastThreadLocalThread) {
            return fastGet((FastThreadLocalThread) thread);
        } else {
            return slowGet();
        }
    }
    private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
        // 直接獲取當前線程的InternalThreadLocalMap
        InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
        // 如果當前線程的InternalThreadLocalMap還未創建,則創建并賦值
        if (threadLocalMap == null) {
            thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
        }
        return threadLocalMap;
    }
    private static InternalThreadLocalMap slowGet() {
        // 使用JDK ThreadLocal獲取InternalThreadLocalMap
        InternalThreadLocalMap ret = slowThreadLocalMap.get();
        if (ret == null) {
            ret = new InternalThreadLocalMap();
            slowThreadLocalMap.set(ret);
        }
        return ret;
    }
    private InternalThreadLocalMap() {
        indexedVariables = newIndexedVariableTable();
    }
    // 初始化一個32位長度的Object數組,并將其元素全部設置為缺省值UNSET
    private static Object[] newIndexedVariableTable() {
        Object[] array = new Object[INDEXED_VARIABLE_TABLE_INITIAL_SIZE];
        Arrays.fill(array, UNSET);
        return array;
    }
    // 省略其他代碼
}

源碼中 get() 方法主要分為下面3個步驟處理:

通過InternalThreadLocalMap.get()方法獲取當前線程的InternalThreadLocalMap。
根據當前線程的index 從InternalThreadLocalMap中獲取其綁定的數據。
如果不是缺省值UNSET,直接返回;如果是缺省值,則執行initialize方法進行初始化。

下面我們繼續分析一下

InternalThreadLocalMap.get()方法的實現邏輯。

首先判斷當前線程是否是FastThreadLocalThread類型,如果是FastThreadLocalThread
類型則直接使用fastGet方法獲取InternalThreadLocalMap,如果不是FastThreadLocalThread類型則使用slowGet方法獲取InternalThreadLocalMap兜底處理。
兜底處理中的slowGet方法會退化成JDK原生的ThreadLocal獲取InternalThreadLocalMap。
獲取InternalThreadLocalMap時,如果為null,則會直接創建一個InternalThreadLocalMap返回。其創建過過程中初始化一個32位長度的Object數組,并將其元素全部設置為缺省值UNSET。

4.3 set 方法

public class FastThreadLocal<V> {
    // FastThreadLocal初始化時variablesToRemoveIndex被賦值為0
    private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
    public final void set(V value) {
        // 判斷value值是否是未賦值的Object變量(缺省值)
        if (value != InternalThreadLocalMap.UNSET) {
            // 獲取當前線程對應的InternalThreadLocalMap
            InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
            // 將InternalThreadLocalMap中數據替換為新的value
            // 并將FastThreadLocal對象保存到待清理的Set中
            setKnownNotUnset(threadLocalMap, value);
        } else {
            remove();
        }
    }
    private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
        // 將InternalThreadLocalMap中數據替換為新的value
        if (threadLocalMap.setIndexedVariable(index, value)) {
            // 并將當前的FastThreadLocal對象保存到待清理的Set中
            addToVariablesToRemove(threadLocalMap, this);
        }
    }
    private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
        // 取下標index為0的數據,用于存儲待清理的FastThreadLocal對象Set集合中
        Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
        Set<FastThreadLocal<?>> variablesToRemove;
        if (v == InternalThreadLocalMap.UNSET || v == null) {
            // 下標index為0的數據為空,則創建FastThreadLocal對象Set集合
            variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
            // 將InternalThreadLocalMap中下標為0的數據,設置成FastThreadLocal對象Set集合
            threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);
        } else {
            variablesToRemove = (Set<FastThreadLocal<?>>) v;
        }
        // 將FastThreadLocal對象保存到待清理的Set中
        variablesToRemove.add(variable);
    }
    // 省略其他代碼
}
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
    // 未賦值的Object變量(缺省值),當?個與線程綁定的值被刪除之后,會被設置為UNSET
    public static final Object UNSET = new Object();
    // 存儲綁定到當前線程的數據的數組
    private Object[] indexedVariables;
    // 綁定到當前線程的數據的數組能再次采用x2擴容的最大量
    private static final int ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD = 1 << 30;
    private static final int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8;
    // 將InternalThreadLocalMap中數據替換為新的value
    public boolean setIndexedVariable(int index, Object value) {
        Object[] lookup = indexedVariables;
        if (index < lookup.length) {
            Object oldValue = lookup[index];
            // 直接將數組 index 位置設置為 value,時間復雜度為 O(1)
            lookup[index] = value;
            return oldValue == UNSET;
        } else { // 綁定到當前線程的數據的數組需要擴容,則擴容數組并數組設置新value
            expandIndexedVariableTableAndSet(index, value);
            return true;
        }
    }
    private void expandIndexedVariableTableAndSet(int index, Object value) {
        Object[] oldArray = indexedVariables;
        final int oldCapacity = oldArray.length;
        int newCapacity;
        // 判斷可進行x2方式進行擴容
        if (index < ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD) {
            newCapacity = index;
            // 位操作,提升擴容效率
            newCapacity |= newCapacity >>>  1;
            newCapacity |= newCapacity >>>  2;
            newCapacity |= newCapacity >>>  4;
            newCapacity |= newCapacity >>>  8;
            newCapacity |= newCapacity >>> 16;
            newCapacity ++;
        } else { // 不支持x2方式擴容,則設置綁定到當前線程的數據的數組容量為最大值
            newCapacity = ARRAY_LIST_CAPACITY_MAX_SIZE;
        }
        // 按擴容后的大小創建新數組,并將老數組數據copy到新數組
        Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
        // 新數組擴容后的部分賦UNSET缺省值
        Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
        // 新數組的index位置替換成新的value
        newArray[index] = value;
        // 綁定到當前線程的數據的數組用新數組替換
        indexedVariables = newArray;
    }
    // 省略其他代碼
}

源碼中 set() 方法主要分為下面3個步驟處理:

判斷value是否是缺省值UNSET,如果value不等于缺省值,則會通過InternalThreadLocalMap.get()方法獲取當前線程的InternalThreadLocalMap,具體實現3.2小節中get()方法已做講解。
通過FastThreadLocal中的setKnownNotUnset()方法將InternalThreadLocalMap中數據替換為新的value,并將當前的FastThreadLocal對象保存到待清理的Set中。
如果等于缺省值UNSET或null(else的邏輯),會調用remove()方法,remove()具體見后面的代碼分析。

接下來我們看下

InternalThreadLocalMap.setIndexedVariable方法的實現邏輯。

判斷index是否超出存儲綁定到當前線程的數據的數組indexedVariables的長度,如果沒有超出,則獲取index位置的數據,并將該數組index位置數據設置新value。

如果超出了,綁定到當前線程的數據的數組需要擴容,則擴容該數組并將它index位置的數據設置新value。

擴容數組以index 為基準進行擴容,將數組擴容后的容量向上取整為 2 的次冪。然后將原數組內容拷貝到新的數組中,空余部分填充缺省值UNSET,最終把新數組賦值給 indexedVariables。

下面我們再繼續看下

FastThreadLocal.addToVariablesToRemove方法的實現邏輯。

1.取下標index為0的數據(用于存儲待清理的FastThreadLocal對象Set集合中),如果該數據是缺省值UNSET或null,則會創建FastThreadLocal對象Set集合,并將該Set集合填充到下標index為0的數組位置。

2.如果該數據不是缺省值UNSET,說明Set集合已金被填充,直接強轉獲取該Set集合。

3.最后將FastThreadLocal對象保存到待清理的Set集合中。

4.4 remove、removeAll方法

public class FastThreadLocal<V> {
    // FastThreadLocal初始化時variablesToRemoveIndex被賦值為0
    private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
    public final void remove() {
        // 獲取當前線程的InternalThreadLocalMap
        // 刪除當前的FastThreadLocal對象及其維護的數據
        remove(InternalThreadLocalMap.getIfSet());
    }
    public final void remove(InternalThreadLocalMap threadLocalMap) {
        if (threadLocalMap == null) {
            return;
        }
        // 根據當前線程的index,并將該數組下標index位置對應的值設置為缺省值UNSET
        Object v = threadLocalMap.removeIndexedVariable(index);
        // 存儲待清理的FastThreadLocal對象Set集合中刪除當前FastThreadLocal對象
        removeFromVariablesToRemove(threadLocalMap, this);
        if (v != InternalThreadLocalMap.UNSET) {
            try {
                // 空方法,用戶可以繼承實現
                onRemoval((V) v);
            } catch (Exception e) {
                PlatformDependent.throwException(e);
            }
        }
    }
    public static void removeAll() {
        InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
        if (threadLocalMap == null) {
            return;
        }
        try {
            // 取下標index為0的數據,用于存儲待清理的FastThreadLocal對象Set集合中
            Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
            if (v != null && v != InternalThreadLocalMap.UNSET) {
                @SuppressWarnings("unchecked")
                Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
                // 遍歷所有的FastThreadLocal對象并刪除它們以及它們維護的數據
                FastThreadLocal<?>[] variablesToRemoveArray =
                        variablesToRemove.toArray(new FastThreadLocal[0]);
                for (FastThreadLocal<?> tlv: variablesToRemoveArray) {
                    tlv.remove(threadLocalMap);
                }
            }
        } finally {
            // 刪除InternalThreadLocalMap中threadLocalMap和slowThreadLocalMap數據
            InternalThreadLocalMap.remove();
        }
    }
    private static void removeFromVariablesToRemove(
            InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
        // 取下標index為0的數據,用于存儲待清理的FastThreadLocal對象Set集合中
        Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
 
        if (v == InternalThreadLocalMap.UNSET || v == null) {
            return;
        }
        @SuppressWarnings("unchecked")
        // 存儲待清理的FastThreadLocal對象Set集合中刪除該FastThreadLocal對象
        Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
        variablesToRemove.remove(variable);
    }
 
    // 省略其他代碼
}
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
    // 根據當前線程獲取InternalThreadLocalMap
       public static InternalThreadLocalMap getIfSet() {
        Thread thread = Thread.currentThread();
        if (thread instanceof FastThreadLocalThread) {
            return ((FastThreadLocalThread) thread).threadLocalMap();
        }
        return slowThreadLocalMap.get();
    }
    // 數組下標index位置對應的值設置為缺省值UNSET
    public Object removeIndexedVariable(int index) {
        Object[] lookup = indexedVariables;
        if (index < lookup.length) {
            Object v = lookup[index];
            lookup[index] = UNSET;
            return v;
        } else {
            return UNSET;
        }
    }
    // 刪除threadLocalMap和slowThreadLocalMap數據
    public static void remove() {
        Thread thread = Thread.currentThread();
        if (thread instanceof FastThreadLocalThread) {
            ((FastThreadLocalThread) thread).setThreadLocalMap(null);
        } else {
            slowThreadLocalMap.remove();
        }
    }
    // 省略其他代碼
}

源碼中 remove() 方法主要分為下面2個步驟處理:

通過InternalThreadLocalMap.getIfSet()獲取當前線程的InternalThreadLocalMap。具體和3.2小節get()方法里面獲取當前線程的InternalThreadLocalMap相似,這里就不再重復介紹了。
刪除當前的FastThreadLocal對象及其維護的數據。

源碼中 removeAll() 方法主要分為下面3個步驟處理:

通過InternalThreadLocalMap.getIfSet()獲取當前線程的InternalThreadLocalMap。
取下標index為0的數據(用于存儲待清理的FastThreadLocal對象Set集合),然后遍歷所有的FastThreadLocal對象并刪除它們以及它們維護的數據。
最后會將InternalThreadLocalMap本身從線程中移除。

五、總結

那么使用ThreadLocal時最佳實踐又如何呢?

 每次使用完ThreadLocal實例,在線程運行結束之前的finally代碼塊中主動調用它的remove()方法,清除Entry中的數據,避免操作不當導致的內存泄漏。

使?Netty的FastThreadLocal一定比JDK原生的ThreadLocal更快嗎?

不?定。當線程是FastThreadLocalThread,則添加、獲取FastThreadLocal所維護數據的時間復雜度是 O(1),?使?ThreadLocal可能存在哈希沖突,相對來說使?FastThreadLocal更?效。但如果是普通線程則可能更慢。

使?FastThreadLocal有哪些優點?

正如文章開頭介紹JDK原生ThreadLocal存在的缺點,FastThreadLocal全部優化了,它更?效、而且如果使?的是FastThreadLocal,它會在任務執?完成后主動調?removeAll?法清除數據,避免潛在的內存泄露。

責任編輯:龐桂玉 來源: vivo互聯網技術
相關推薦

2016-12-08 15:36:59

HashMap數據結構hash函數

2010-06-01 15:25:27

JavaCLASSPATH

2020-07-21 08:26:08

SpringSecurity過濾器

2009-09-25 09:14:35

Hibernate日志

2021-02-17 11:25:33

前端JavaScriptthis

2013-09-22 14:57:19

AtWood

2019-06-25 10:32:19

UDP編程通信

2017-08-15 13:05:58

Serverless架構開發運維

2025-05-06 00:43:00

MySQL日志文件MIXED 3

2024-02-21 21:14:20

編程語言開發Golang

2017-01-10 08:48:21

2020-09-23 10:00:26

Redis數據庫命令

2025-06-05 05:51:33

2020-10-15 18:31:36

理解Netty編解碼

2022-11-04 09:43:05

Java線程

2022-09-05 08:39:04

kubernetesk8s

2017-01-13 22:42:15

iosswift

2021-04-20 23:25:16

執行函數變量

2024-03-12 00:00:00

Sora技術數據

2023-02-10 08:11:43

Linux系統調用
點贊
收藏

51CTO技術棧公眾號

一区二区国产精品| 在线最新版中文在线| 国产精品一区久久久久| 久久久久久久久久久91| 亚洲黄色在线网站| 另类一区二区| 亚洲1区2区3区视频| 日本一区二区三区免费观看| 国产精品羞羞答答在线| 国产亚洲精品久久久久婷婷瑜伽| 在线精品91av| 女同性恋一区二区三区| 四虎在线精品| 欧美午夜精品久久久久久人妖 | 欧美aaaaaaaa牛牛影院| 欧洲生活片亚洲生活在线观看| 黄色网zhan| 欧美女优在线| 丁香五精品蜜臀久久久久99网站| 国产精品99久久久久久久久久久久| 激情小说中文字幕| 欧美午夜精品一区二区三区电影| 精品国产一区二区三区久久久蜜月| 亚洲一二三区av| 国产精品vvv| 亚洲欧美色图小说| 日韩中文一区二区三区| 免费国产羞羞网站视频| 韩国成人福利片在线播放| 欧美有码在线视频| 久久久久亚洲av成人片| 99精品视频在线| 亚洲午夜小视频| 欧美熟妇精品一区二区蜜桃视频| 亚洲一区二区av| 91福利社在线观看| 日本三级免费网站| 国产在线xxx| 亚洲六月丁香色婷婷综合久久| 日韩高清国产一区在线观看| 亚洲av片在线观看| 成人aa视频在线观看| 99理论电影网| www香蕉视频| 国产美女久久久久| 成人有码在线播放| 91肉色超薄丝袜脚交一区二区| 日产欧产美韩系列久久99| 91精品国产高清| 精品无码人妻一区二区三| 一个色综合网| 久久亚洲成人精品| 丝袜美腿小色网| 亚洲成人国产| 久热99视频在线观看| 欧美做爰啪啪xxxⅹ性| 欧美好骚综合网| 精品国内产的精品视频在线观看| 美女三级黄色片| 99久久99久久精品国产片果冰| 在线看日韩欧美| 精品国产国产综合精品| 午夜av一区| 欧美丰满老妇厨房牲生活 | 国产午夜视频在线观看| 久久九九久久九九| 亚洲国产一区在线| 国内外激情在线| 亚洲一区二区精品视频| 日本午夜激情视频| 欧美xoxoxo| 欧美视频中文字幕| 中国老熟女重囗味hdxx| 盗摄牛牛av影视一区二区| 精品国产91亚洲一区二区三区婷婷| 午夜免费福利影院| 亚洲宅男网av| 在线视频中文亚洲| 加勒比婷婷色综合久久| 亚洲国产高清一区| 国产成人精品电影久久久| 一本色道久久综合熟妇| 高清不卡一区二区| 欧美在线日韩精品| 顶级网黄在线播放| 婷婷一区二区三区| 久久婷婷综合色| 91综合精品国产丝袜长腿久久| 亚洲黄色成人网| 国产传媒在线看| 欧美精品一卡| 国产精欧美一区二区三区| 国产又大又黑又粗| 不卡的av在线播放| 亚洲一区二区免费视频软件合集| 欧美xxxxhdvideosex| 色综合久久中文字幕综合网| 最新天堂在线视频| 欧美久久香蕉| 久久不射电影网| www.欧美色| 国产乱码精品一区二区三区av| 精品91免费| 国产成人高清精品| 欧美小视频在线| 第四色婷婷基地| 色综合久久中文| 欧美成aaa人片在线观看蜜臀| 亚洲另类欧美日韩| 国产成人av影院| 亚洲精品乱码视频| 天堂а√在线最新版中文在线| 在线综合视频播放| 波多野吉衣中文字幕| 欧美区日韩区| 成人h片在线播放免费网站| 五月婷婷狠狠干| 夜夜亚洲天天久久| 日韩成人av免费| av一区二区在线观看| 91精品国产乱码久久久久久蜜臀| 国产丝袜视频在线观看| 国产日韩v精品一区二区| 欧美亚洲日本一区二区三区| **精品中文字幕一区二区三区| 亚洲性视频网站| 日产精品久久久| www.欧美日韩国产在线| www.69av| 精品国产亚洲一区二区三区在线 | 永久免费看片在线播放| 国产裸体歌舞团一区二区| 亚洲精品国产系列| 精品日韩视频| 亚洲午夜av久久乱码| 亚洲天堂男人av| www国产精品av| 国产精品一区二区免费在线观看| 精品国产一区二区三区不卡蜜臂 | 国产一区二区三区四区五区美女 | 亚洲精品在线观| 久久久久久久久久99| 国产精品夜夜嗨| 大陆极品少妇内射aaaaaa| 亚洲伊人精品酒店| 北条麻妃99精品青青久久| 亚洲一区在线观| 中文字幕在线观看一区二区| 五月花丁香婷婷| 一区二区三区四区在线观看国产日韩 | 秋霞国产精品| 少妇高潮久久77777| 伊人网免费视频| 中文字幕一区二区在线播放| 女人高潮一级片| 一区二区三区中文| 国产精品视频500部| 麻豆网站免费在线观看| 精品视频偷偷看在线观看| 国产无遮挡呻吟娇喘视频| 91小视频在线免费看| 日本成年人网址| 国产毛片一区二区三区| 国产日韩欧美在线观看| 国产福利在线播放麻豆| 亚洲а∨天堂久久精品喷水| 午夜影院在线看| 国产色综合久久| 亚洲三级在线观看视频| 国产精品s色| 久久精品国产第一区二区三区最新章节 | 国产精品午夜一区二区三区| 国产精品丝袜白浆摸在线| 日本在线观看www| 欧美不卡123| www毛片com| 国产精品电影院| 亚洲美女高潮久久久| 国产亚洲网站| 异国色恋浪漫潭| 国产精品久久久久av蜜臀| 日本精品免费一区二区三区| 9色在线视频| 欧美刺激午夜性久久久久久久| 草久视频在线观看| 一色桃子久久精品亚洲| 蜜臀视频在线观看| 视频一区中文字幕国产| 麻豆一区二区三区在线观看| 日韩高清在线免费观看| 国产精品稀缺呦系列在线| 色呦呦呦在线观看| 亚洲区免费影片| 精品区在线观看| 色噜噜狠狠色综合欧洲selulu| 裸体武打性艳史| 国产午夜精品一区二区三区嫩草 | 一级黄色特级片| 亚洲电影av| 中文字幕在线观看一区二区三区| 美女av一区| 2022国产精品| 狠狠久久伊人中文字幕| 97精品视频在线| aaa大片在线观看| 最好看的2019的中文字幕视频| 日韩一级片免费看| 337p亚洲精品色噜噜狠狠| 波多野结衣啪啪| 亚洲高清免费观看| 夫妻性生活毛片| 国产欧美一区二区精品仙草咪| 97精品人人妻人人| 久久成人av少妇免费| 男人亚洲天堂网| 亚洲成人直播| 91网站在线观看免费| 久久高清免费| 日韩理论片在线观看| 女同另类激情重口| 99久久精品免费看国产一区二区三区| 欧美一区二区三区婷婷| 国产成人一区二区三区| 99riav视频在线观看| 久久中文字幕在线| 免费观看久久久久| 在线观看国产欧美| 精品无人乱码| 亚洲伦理中文字幕| 性xxxxbbbb| 亚洲国产一区自拍| 国产成人三级在线观看视频| 制服丝袜亚洲色图| 亚洲视频久久久| 欧美日韩五月天| 在线观看日批视频| 欧美性大战久久久久久久蜜臀| 久久久久久不卡| 色婷婷狠狠综合| 波多野结衣在线电影| 日韩欧美亚洲综合| 国产乱国产乱老熟| 色哟哟精品一区| 亚洲第一网站在线观看| 91福利国产精品| 亚洲 小说区 图片区| 精品视频1区2区| 中文字幕第一页在线播放| 欧美亚洲国产一区二区三区va| 天天爱天天做天天爽| 欧美在线观看18| 亚洲在线免费观看视频| 91麻豆精品国产无毒不卡在线观看 | 欧美一级夜夜爽| 欧美 日韩 国产 在线| 亚洲激情在线视频| 免费在线黄色影片| 一区二区三区国产视频| 麻豆网站在线| 欧美俄罗斯性视频| 麻豆国产在线| 国产精品狼人色视频一区| 欧美综合社区国产| 97久久夜色精品国产九色| 国产精品超碰| 日韩免费三级| 91精品蜜臀一区二区三区在线| 毛片av在线播放| 国产精品美女| 中文字幕66页| 成人一级视频在线观看| 国产精品边吃奶边做爽| 欧美国产精品中文字幕| 中日韩一级黄色片| 亚洲午夜激情网页| 久久久久女人精品毛片九一| 欧美人xxxx| 人人妻人人澡人人爽久久av| 国产亚洲精品成人av久久ww| av网站在线免费看推荐| 性欧美办公室18xxxxhd| 欧美国产日韩电影| 99re6热在线精品视频播放速度| 欧美成人专区| 波多野结衣激情| 亚洲伊人观看| 香蕉视频xxxx| 国产日韩欧美综合一区| 久久久久久国产精品视频 | 91国内在线视频| 在线成人免费| 欧美精品免费观看二区| 91精品国产自产在线观看永久∴| 男人日女人下面视频| 狠狠色综合色综合网络| 欧美深性狂猛ⅹxxx深喉| 国产精品久久久久久户外露出| 国产性xxxx高清| 日韩情涩欧美日韩视频| 二区在线观看| 6080yy精品一区二区三区| 国产精品一区二区美女视频免费看| 久久亚洲国产精品日日av夜夜| 亚洲天堂一区二区三区四区| 男人亚洲天堂网| 成人aaaa免费全部观看| 2021亚洲天堂| 欧美日韩国产中文| 看电影就来5566av视频在线播放| 欧美高清自拍一区| 日韩免费在线电影| 欧美色欧美亚洲另类七区| 狠狠干综合网| 北条麻妃亚洲一区| 欧美激情综合网| 日韩精品一区二区亚洲av| 精品成人一区二区三区四区| dy888亚洲精品一区二区三区| 国产精品九九九| 自拍亚洲一区| 黄色av网址在线播放| 国产白丝精品91爽爽久久| 色哟哟一一国产精品| 欧美三级乱人伦电影| 成人精品一区二区三区校园激情| 2021久久精品国产99国产精品| 91欧美日韩在线| 大胆欧美熟妇xx| 国产成人99久久亚洲综合精品| 日韩欧美123区| 欧美一区二区视频观看视频| 欧美性天天影视| 国产一区二区香蕉| 日韩精品看片| 小泽玛利亚视频在线观看| 国产日韩欧美不卡在线| 无码aⅴ精品一区二区三区| 亚洲美女久久久| 六月婷婷综合| 人偷久久久久久久偷女厕| 老鸭窝毛片一区二区三区| 亚洲天堂视频一区| 欧美午夜视频一区二区| 欧美精品少妇| 国产精品成av人在线视午夜片| 国产成人一区| 精品日韩久久久| 中文字幕一区二区三区在线不卡| 中文字幕久久熟女蜜桃| 日韩中文字幕网| 国产精品视频一区视频二区| 国产午夜精品视频一区二区三区| 国产aⅴ精品一区二区三区色成熟| 国产一级在线观看视频| 亚洲精品美女在线观看| 成人免费短视频| 亚洲国产日韩欧美| 国产一区免费电影| 日韩va亚洲va欧美va清高| 精品剧情v国产在线观看在线| а√天堂中文在线资源8| 日本不卡一区| 六月丁香婷婷久久| 私库av在线播放| 亚洲国产欧美在线成人app | 夜夜嗨av一区二区三区网站四季av| 欧美 日本 国产| 欧美亚洲国产怡红院影院| 国产成人在线视频免费观看| 国产美女精品在线观看| 久久精品欧洲| 少妇aaaaa| 亚洲精品动漫久久久久| 欧美va在线观看| 国产小视频免费| 国产视频一区二区在线| 国产人妻精品一区二区三| 久久免费在线观看| 国产一区二区三区91| 欧美专区第二页| 日韩欧美极品在线观看| 国产三区视频在线观看| 国产精品三区四区| 美女视频黄频大全不卡视频在线播放| 一区二区在线观看免费视频| 亚洲免费av网址| 欧美成人精品午夜一区二区| 欧美视频免费播放| 亚洲美女偷拍久久| 蜜芽tv福利在线视频| 91嫩草国产在线观看| 水野朝阳av一区二区三区| 懂色av懂色av粉嫩av| 亚洲人成网站在线播| 日本在线成人| 牛夜精品久久久久久久| 午夜视频一区二区| 九七电影韩国女主播在线观看| 欧美日韩高清在线一区| 国产精品资源在线看|