精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

国产精品对白刺激久久久| 精品欧美一区二区久久| 亚洲成人a**址| 国产精品久久影视| 你懂的网址国产 欧美| 手机看片1024国产| 欧洲三级视频| 欧美丰满少妇xxxbbb| www.黄色网址.com| 污视频网站在线播放| 日韩极品在线观看| 久久九九国产精品怡红院| 四虎永久免费观看| 17videosex性欧美| 中文在线免费一区三区高中清不卡| 亚洲精品720p| 丰满的少妇愉情hd高清果冻传媒| wwwav国产| 日韩高清成人在线| 色94色欧美sute亚洲线路二| 日韩一区二区三区资源| 国产免费黄色录像| 国产欧美另类| 精品激情国产视频| 日本不卡视频一区| 成人在线不卡| 欧美日韩精品在线| 欧美一区二区三区综合| 麻豆影视在线| 国产高清不卡一区| 国产精品精品视频一区二区三区| 少妇欧美激情一区二区三区| av日韩中文| 中文字幕五月欧美| 欧美理论一区二区| 少妇一级淫片免费看| 黄网站免费久久| 国产成人午夜视频网址 | 羞羞视频在线观看欧美| 久久手机精品视频| 天堂网av2018| 欧美丝袜丝交足nylons172| 亚洲裸体xxxx| 久久午夜夜伦鲁鲁片| 成人精品毛片| 欧美一区二区三区视频免费播放 | 欧美精品乱码久久久久久按摩 | 浮生影视网在线观看免费| 日韩—二三区免费观看av| 欧美激情影音先锋| 亚洲AV成人无码精电影在线| 日韩精品首页| 色综合亚洲精品激情狠狠| 91网站免费视频| 6080亚洲理论片在线观看| 欧美日韩一区二区三区免费看| 狠狠色综合欧美激情| 99久久婷婷国产一区二区三区| 成人免费在线观看av| 亚洲免费视频观看| 亚洲欧美色图视频| 激情av综合| 日韩高清中文字幕| 亚洲第一导航| 日韩欧美在线观看一区二区| 91丝袜呻吟高潮美腿白嫩在线观看| 欧美精品videosex极品1| 特黄一区二区三区| 婷婷综合五月| 裸体女人亚洲精品一区| 国产乱国产乱老熟300| 欧美午夜久久| 欧美激情网站在线观看| 久久久久久久九九九九| 亚洲一区二区三区高清| 国产va免费精品高清在线| 亚洲天堂男人av| 蜜臀av性久久久久蜜臀aⅴ流畅| 综合激情国产一区| 国产又粗又猛又爽又黄的视频小说| 伊人久久大香| 欧美一级片在线| 国偷自产av一区二区三区麻豆| 九色porny丨首页入口在线| 亚洲欧美偷拍卡通变态| 欧美国产日韩激情| 国产三级伦理在线| 亚洲成a人在线观看| www.浪潮av.com| 日韩精品第二页| 精品成人一区二区三区四区| 成人午夜福利一区二区| 色一区二区三区四区| 久久精品91久久久久久再现| 久久久久久福利| 日韩一区精品字幕| 99九九视频| 国产三级在线免费| 亚洲国产毛片aaaaa无费看| 99免费视频观看| 视频二区欧美毛片免费观看| 国产小视频国产精品| 老女人性淫交视频| 蜜桃视频在线一区| 国产亚洲欧美一区二区| 日本美女在线中文版| 婷婷久久综合九色国产成人| 不用播放器的免费av| 午夜先锋成人动漫在线| 美女福利精品视频| 中文字幕你懂的| youjizz国产精品| 一区二区三区一级片| 成人免费看视频网站| 日韩美女一区二区三区四区| 免费看的黄色录像| 亚洲永久在线| 国产精品美女诱惑| 中文字幕中文字幕在线十八区| 亚洲激情自拍视频| 国产视频一区二区三区在线播放| gogo亚洲高清大胆美女人体| 日韩欧美一级精品久久| 久久久免费看片| 午夜影院日韩| 精品一区二区久久久久久久网站| 色播色播色播色播色播在线 | 最新黄网在线观看| 欧美亚洲尤物久久| 亚洲 小说 欧美 激情 另类| 一本久道久久综合狠狠爱| 91精品视频播放| 成年人在线视频| 在线免费亚洲电影| 日韩 中文字幕| 亚洲黄色精品| 国产一区二区三区四区五区在线 | 91黄色小网站| 久久夜色电影| 欧美激情xxxx性bbbb| 99热这里只有精品在线观看| 中文字幕一区在线| 成人综合久久网| 久久日文中文字幕乱码| 国产精品嫩草影院久久久| 国产黄色免费在线观看| 色噜噜久久综合| 美女被到爽高潮视频| 久久精品官网| 日本亚洲导航| 秋霞国产精品| 尤物tv国产一区| 中文字幕视频二区| 中文字幕一区二区三区四区 | 国产精品一卡二卡在线观看| 中文字幕av日韩精品| 亚洲日日夜夜| 欧美男插女视频| 丰满人妻一区二区三区无码av| 久久女同互慰一区二区三区| 欧美黄色免费影院| 不卡一区综合视频| 91精品久久久久| www久久日com| 亚洲精品一区二区精华| 日本va欧美va国产激情| 久久久久国产精品人| 亚洲免费看av| 欧美三级午夜理伦三级中文幕| 国产91精品最新在线播放| 国产三级在线免费观看| 欧美美女直播网站| 国产在线视频你懂的| 91在线观看高清| 亚洲一区在线不卡| 91精品国产福利在线观看麻豆| 欧美综合国产精品久久丁香| 番号在线播放| 日韩女同互慰一区二区| 在线观看中文字幕视频| 中文字幕精品一区二区精品绿巨人| 麻豆tv在线播放| 精品亚洲成人| 999日本视频| 亚洲精品福利电影| 色偷偷av一区二区三区乱| 亚洲AV无码精品国产| 懂色av影视一区二区三区| 黄色国产在线播放| 成人国产精品免费观看视频| 妺妺窝人体色www在线观看| 亚洲色图国产| 欧美一卡2卡3卡4卡无卡免费观看水多多| av免费不卡国产观看| 在线日韩av观看| 午夜久久久久久噜噜噜噜| 91福利国产精品| 青青草成人免费| 国产欧美日韩在线视频| 人妻激情偷乱频一区二区三区| 一区二区三区国产精华| 久久综合婷婷综合| 秋霞影院一区| 国产99视频精品免视看7| 欧美14一18处毛片| 色综久久综合桃花网| 午夜视频在线免费播放| 91精品国产麻豆国产自产在线| 一区二区三区四区五区| 97se亚洲国产综合在线| 亚洲免费成人在线视频| 久久电影一区| 成人免费性视频| 小小影院久久| 香蕉久久夜色| 蜜桃国内精品久久久久软件9| 日韩美女在线看| 直接在线观看的三级网址| 中文国产亚洲喷潮| 韩国中文免费在线视频| 亚洲精品国产精品国产自| 精品久久久久中文慕人妻| 欧美日韩精品综合在线| 高清乱码免费看污| 欧美日韩在线一区| 日韩欧美亚洲国产| 亚洲一区二区三区在线播放| 无码黑人精品一区二区| 国产精品成人免费| 免费看的黄色录像| 中文成人综合网| 调教驯服丰满美艳麻麻在线视频 | 午夜视频在线观看一区二区 | 国产成人在线免费| 天天综合天天添夜夜添狠狠添| 伊人久久大香线| 中文字幕日韩一区二区三区| 日韩国产一区| 亚洲在线视频一区二区| 日本午夜一区| 先锋影音一区二区三区| 国产区精品区| 日韩av影视| jizzjizz欧美69巨大| 日韩免费av一区二区三区| 九色精品91| 日韩jizzz| 久久亚洲成人| 最新欧美日韩亚洲| 在线中文字幕第一区| 特级西西444| 激情视频一区二区三区| www.99热这里只有精品| 99综合精品| 毛片av免费在线观看| 奇米综合一区二区三区精品视频| 久久久国内精品| 韩国一区二区三区在线观看| 国产91沈先生在线播放| 亚洲精品系列| 久久精品免费一区二区| 日韩精品亚洲专区| 中日韩av在线播放| 国产凹凸在线观看一区二区| 成人在线视频免费播放| 久久你懂得1024| 免费黄色激情视频| 亚洲一区二区三区中文字幕| 国产精品免费精品一区| 欧美视频第二页| 精品国产乱码一区二区三| 欧美精品一区二区在线播放| 欧美偷拍视频| 中文在线不卡视频| 肉体视频在线| 日产精品久久久一区二区福利| sm在线观看| 国产91九色视频| 日韩视频一二区| 免费毛片一区二区三区久久久| 91大神精品| 奇米精品在线| 欧美激情偷拍| 国产一级片黄色| 国产精品 日产精品 欧美精品| 久久99爱视频| 成人av在线电影| 欧美巨胸大乳hitomi| 亚洲综合久久av| 在线观看亚洲黄色| 精品国产乱码久久久久久夜甘婷婷 | 亚洲综合99| 久久久福利影院| 久久免费精品国产久精品久久久久| 无码人妻精品一区二区三| 国产日韩欧美a| 久久免费在线观看视频| 欧美色涩在线第一页| 欧美视频xxx| 在线观看国产精品日韩av| eeuss鲁一区二区三区| 国产主播在线一区| 国产ts一区| 午夜一区二区三区| 亚洲美洲欧洲综合国产一区| 欧美又黄又嫩大片a级| 91在线你懂得| 欧美黄色一区二区三区| 欧美性感一区二区三区| 亚洲 另类 春色 国产| 久久夜精品香蕉| 欧美××××黑人××性爽| 成人一区二区三区四区| 五月精品视频| 91看片在线免费观看| 91片黄在线观看| 激情综合网五月婷婷| 欧美高清精品3d| a中文在线播放| 日韩免费av一区二区| 久久精品凹凸全集| 欧美精品卡一卡二| 国产成人精品1024| 国产真实乱在线更新| 欧美日韩高清一区二区不卡| 欧美白人做受xxxx视频| 欧美一级淫片videoshd| 国产精品一区二区中文字幕| av日韩在线看| 国产成人免费视频网站高清观看视频| 性欧美18—19sex性高清| 亚洲乱码中文字幕综合| 国产又黄又爽视频| 最新的欧美黄色| 国产精品99精品一区二区三区∴| 96精品久久久久中文字幕| 日韩精品第一区| 青青在线免费观看视频| 国产日韩欧美麻豆| 天堂а√在线中文在线新版| 日韩av一区二区在线| 嗯啊主人调教在线播放视频 | 亚洲黄色av网址| 国产亚洲一区二区在线观看| 亚洲欧美精品一区二区三区| 精品丝袜一区二区三区| 成人片免费看| 日韩欧美在线观看强乱免费| 免费一级片91| 手机看片国产日韩| 欧美精品aⅴ在线视频| 国产日产一区二区| 91亚洲精品久久久| 欧美在线三区| 国产人妻黑人一区二区三区| 红桃视频成人在线观看| 欧美日韩激情视频一区二区三区| 欧美成人在线网站| 一区中文字幕| 成人中文字幕在线播放| 国产偷v国产偷v亚洲高清| 中文字幕日本人妻久久久免费| 日韩av有码在线| 偷拍自拍在线看| 日韩av电影免费在线| 精品制服美女久久| 久久久久香蕉视频| 日韩av中文字幕在线| 欧美xnxx| www国产免费| 99久久免费精品| 精品国产www| 欧美裸体xxxx极品少妇| 亚洲免费专区| 91女神在线观看| 亚洲国产成人av网| 国产免费a∨片在线观看不卡| 91tv亚洲精品香蕉国产一区7ujn| 国产精品白丝久久av网站| 免费一级淫片aaa片毛片a级| av中文字幕在线不卡| 欧美男人天堂网| 欧美高清视频一区二区| 夜夜春成人影院| 天天爽夜夜爽视频| 色激情天天射综合网| 毛片激情在线观看| 久久国产精品一区二区三区| 久久电影网电视剧免费观看| 午夜免费福利在线| 欧美日韩精品在线一区| 超碰人人草人人| 五月婷婷综合在线| 尤物网址在线观看| 国产伦精品一区二区三区| 日韩av高清在线观看| 九九九在线视频| 国产一区av在线| 国产成人av毛片| 中文字幕av不卡在线|