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

ThreadLocal源碼解讀:內存泄露問題分析

開發 前端
ThreadLocal 優勢是無鎖化提升并發性能和簡化變量的傳遞邏輯。在實際業務中使用 ThreadLocal 類時應該在恰當位置調用 remove 方法顯式移除值。盡可能的避免觸發 ThreadLocal 清理過時 Entry 的邏輯,從而提高 ThreadLocal 性能。

引言

大家好,我們又見面了。今天依舊是結合源碼為大家分享個人對于 ThreadLocal 的一些理解。今天是第二期,將著重分析 ThreadLocal 內存泄露問題,文章后半篇含重點源碼精講,不容錯過。廢話不多說,坐穩發車咯!

上期回顧

在上一期,我通過閱讀源碼的方式帶大家學習了 ThreadLocal 常用的 API,并在這個過程中深度剖析了 ThreadLocal 的存儲結構。

下面通過我剛剛繪制的一張圖來為大家回顧一下上一節所闡述的存儲結構。

圖片圖片

如果大家對這個存儲結構有所疑惑,可以回看第一期《ThreadLocal 源碼解讀:初識 ThreadLocal》。

引用類型

在 Java 中有四種常用的引用類型,依照引用的強弱排序依次是:強引用、軟引用、弱引用、幻引用(虛引用)。

其中強引用就是我們通常所說的引用,所以這里 Java 并沒有單獨定義一個引用類來表示,并且強引用存在時被引用對象一定不會被垃圾回收器回收。

軟引用在 Java 中使用 SoftReference 類表示,被軟引用單獨引用的對象當系統內存不足的時候會被垃圾回收器所回收,也就是說在發生 OOM 前將會回收軟引用對象,試圖避免 OOM 的發生。

弱引用在 Java 中使用 WeakReference 類表示,被弱引用單獨引用的對象在發生任意垃圾回收時,無論內存是否充足都將會被回收。

幻引用在 Java 中使用 PhantomReference 類表示,是最弱的引用類型,主要用于跟蹤對象是否被垃圾回收,并且幻引用的 get 方法永遠返回 null。

上述三種引用類均繼承 Reference 類,Reference 類通過泛型成員變量 referent 存儲引用對象,并提供了 get 方法用于獲取引用對象,提供 clear 方法用于清理引用對象。

圖片圖片

內存泄露問題剖析

拋出觀點

在探究 ThreadLocal 內存泄漏問題之前,我們首先要明確一下,什么是內存泄露?

這里我們直接引用百度百科提供的答案。


內存泄漏(Memory Leak)是指程序中已動態分配的堆內存由于某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重后果。

那么 ThreadLocal 在使用過程中存在泄露問題嗎?答案是肯定的,但是要糾正一點,ThreadLocal 的內存泄露問題與 ThreadLocal 對象的弱引用并無關系!這一點在網上可能存在著誤導信息,下面將會為大家論證我的觀點。

推理驗證

首先我們來看一下 Entry 類的定義。

圖片圖片

可以看到 Entry 類繼承了 WeakReference 類,并且將弱引用的 ThreadLocal 對象作為了 ThreadLocalMap 的鍵。

查閱過 ThreadLocal 相關博客的小伙伴可能看過下面種說法。

--start--

ThreadLocal 變量如果未被正確清理,可能會導致內存泄露。因為 ThreadLocalMap 的鍵是 ThreadLocal 對象的弱引用,值是強引用。

當 ThreadLocal 對象不再被外部引用時,ThreadLocalMap 中的鍵會被垃圾回收,但值仍然存在,導致無法被垃圾回收,從而引發內存泄露。

--end--

在這個過程中的確存在內存泄露問題,但這和 ThreadLocalMap 的 key 設計并無關系,這是編寫程序的不嚴謹導致的問題,在使用完 ThreadLocal 后,沒有調用 remove 方法顯式移除值。

任何一個 Java 對象都可能因為使用不當導致內存泄漏,比如聲明了一個類的對象用作成員變量,但是卻從未在代碼里使用過這個成員變量(如下圖),這也是內存泄漏。

圖片圖片

所以并不是因為 ThreadLocalMap 的 key 的弱引用設計,才導致的內存泄露問題。恰恰相反,ThreadLocalMap 的 key 的弱引用設計一定程度上減少了內存泄露的損失。

首先當 ThreadLocalMap 的 key 不再被外部所引用時,ThreadLocal 對象以及通過 ThreadLocal 存儲在 ThreadLocalMap 中的值已經無法在其他地方被獲取,已經發生了內存泄漏。那么這時候垃圾回收器回收掉 ThreadLocalMap 的 key,恰恰為我們釋放了一部分已經泄露的內存。

這時候有人可能會有疑問,那 value 就不管了嗎?當然不是!雖然這是開發者 API 使用不當留下的坑,但是設計者也為我們填了這個坑。

注意看 Entry 類的注釋,這里我直接為大家翻譯出來。

圖片圖片

可以看到官方將 key 為 null 的 Entry 對象稱之為“陳舊條目”,也就是我上一期文章所說的過時 Entry,并且官方指出這些過時 Entry 可以從 ThreadLocalMap 中刪除。

那么不難猜到,ThreadLocal 在設計時一定在某些時機對這些過時 Entry 進行了清理,盡可能的釋放泄露的內存。

這里先給出大家結論,然后我們再去論證:ThreadLocal在調用set(),get(),remove()方法的時候,都可能觸發清理過時Entry的邏輯。。

清理方法源碼剖析

expungeStaleEntry 方法

在討論到 ThreadLocalMap 過時 Entry 清理的問題,就繞不開 ThreadLocalMap 的 expungeStaleEntry 這個方法,見名之意這個方法用于刪除過時 Entry。

下面我將采用在源碼中添加注釋的方式剖析這個方法。

/**
 * Expunge a stale entry by rehashing any possibly colliding entries
 * lying between staleSlot and the next null slot.  This also expunges
 * any other stale entries encountered before the trailing null.  See
 * Knuth, Section 6.4
 *
 * @param staleSlot index of slot known to have null key
 * @return the index of the next null slot after staleSlot
 * (all between staleSlot and this slot will have been checked
 * for expunging).
 */
private int expungeStaleEntry(int staleSlot) {
    // 入參 staleSlot: 待清理位置下標
    
    // 獲取 ThreadLocalMap 中的 Entry 數組。
    ThreadLocal.ThreadLocalMap.Entry[] tab = table;
    // len 為當前 Entry 數組容量。
    int len = tab.length;

    // expunge entry at staleSlot
    // 清除當前 staleSlot 位置的過時 Entry。
    tab[staleSlot].value = null;
    tab[staleSlot] = null;
    // 元素數量減一。
    size--;

    // Rehash until we encounter null
    // 因為 ThreadLocalMap 解決哈希沖突采用的是線性探測法,如將當前下標位置賦值為 null ,但不對后續 Entry
    // 元素進行 rehash 操作,就可能導致存在哈希沖突的后置元素無法被探測到。所以將當前元素清理后需要
    // 對后續元素進行 rehash 操作,直到遇到下一個為 null 的元素。
    ThreadLocal.ThreadLocalMap.Entry e;
    int i;
    // nextIndex 用于向后遞增索引 ((i + 1 < len) ? i + 1 : 0)
    for (i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        ThreadLocal<?> k = e.get();
        if (k == null) {
            // 此時 Entry 不為 null,key 為 null,Entry 為過時 Entry 需清理掉。
            e.value = null;
            tab[i] = null;
            size--;
        } else {
            // 此時為有效 Entry,需要進行 rehash 操作重新定位。
            int h = k.threadLocalHashCode & (len - 1);
            if (h != i) {
                // 進入到這個分支說明 rehash 后,新的下標與原來下標不等。
                // 將當前下標位置清空。
                tab[i] = null;

                // Unlike Knuth 6.4 Algorithm R, we must scan until
                // null because multiple entries could have been stale.
                // 從 h 位置開始遍歷,直到遇到為 null 的元素,并將 rehash 后的元素插入到該位置。
                while (tab[h] != null)
                    h = nextIndex(h, len);
                tab[h] = e;
            }
        }
    }
    
    // i 為 staleSlot 后的第一個 null 元素的位置下標。
    return i;
}

了解了 expungeStaleEntry 方法的內部實現細節之后就可以把這個方法當做一個黑盒,作用是清理傳入下標位置的過時 Entry,入參為一個過時 Entry 的下標。

cleanSomeSlots 方法

有了 expungeStaleEntry 方法的基礎我們就可以攻克下一個和清理過時 Entry 相關的方法:cleanSomeSlots。見名之意,這個方法的作用是清除一些過時 Entry。

同樣采用在源碼中添加注釋的方式剖析這個方法。

/**
 * Heuristically scan some cells looking for stale entries.
 * This is invoked when either a new element is added, or
 * another stale one has been expunged. It performs a
 * logarithmic number of scans, as a balance between no
 * scanning (fast but retains garbage) and a number of scans
 * proportional to number of elements, that would find all
 * garbage but would cause some insertions to take O(n) time.
 *
 * @param i a position known NOT to hold a stale entry. The
 * scan starts at the element after i.
 *
 * @param n scan control: {@code log2(n)} cells are scanned,
 * unless a stale entry is found, in which case
 * {@code log2(table.length)-1} additional cells are scanned.
 * When called from insertions, this parameter is the number
 * of elements, but when from replaceStaleEntry, it is the
 * table length. (Note: all this could be changed to be either
 * more or less aggressive by weighting n instead of just
 * using straight log n. But this version is simple, fast, and
 * seems to work well.)
 *
 * @return true if any stale entries have been removed.
 */
private boolean cleanSomeSlots(int i, int n) {
    // 入參 i: 一個已知不為過時 Entry 的下標。掃描從 i 之后的位置開始。
    // 入參 n: 掃描次數控制值
    
    // 是否清理了任意過時 Entry 標志,
    // 為 false 代表本次方法調用未能清理任何過時 Entry,為 true 代表本次方法調用至少清理了一個過時 Entry。
    boolean removed = false;
    ThreadLocal.ThreadLocalMap.Entry[] tab = table;
    int len = tab.length;
    // doWhile 循環,至少執行一次。
    do {
        i = nextIndex(i, len);
        ThreadLocal.ThreadLocalMap.Entry e = tab[i];
        if (e != null && e.get() == null) {
            // 進入到當前分支說明當前 Entry 為過時 Entry。
            // 將 n 置為數組容量,將當前循環遍歷次數進行追增(n 變大了)。
            n = len;
            // 將標志置為 true,證明本次方法調用并不是無功而返。
            removed = true;
            // 調用清理過時 Entry 方法,并將 expungeStaleEntry 方法返回的 null 元素的下標賦值給 i,
            // 在這之間的下標都在 expungeStaleEntry 方法中進行了清理,所以這里直接跳過避免重復操作。
            i = expungeStaleEntry(i);
        }
        // >>>= 無符號右移并賦值,相當于除以 2 操作。
    } while ( (n >>>= 1) != 0);
    // 返回本次是否至少清理了一個過時 Entry。
    return removed;
}

cleanSomeSlots 方法在 set 和 remove 方法調用中會被調用到,這個方法在完全不掃描以及全量掃描中做了一個平衡,采用以對數的方式進行掃描,并且如果發現了過時 Entry 則會再追增對數次掃描,使得在保證 set 方法和 remove 方法的執行效率的情況下一定程度上清理了過時 Entry。

replaceStaleEntry 方法

下面我們來看一下最后一個與清理過時 key 有關的方法:replaceStaleEntry,通過方法名我們可以推測出這個方法的作用是替換過時條目,那么用什么替換呢,是 set 方法傳過來的 Entry。

同樣采用在源碼中添加注釋的方式剖析這個方法,這個方法有些許難度,如果大家不理解,可以多閱讀幾遍。

/**
 * Replace a stale entry encountered during a set operation
 * with an entry for the specified key.  The value passed in
 * the value parameter is stored in the entry, whether or not
 * an entry already exists for the specified key.
 *
 * As a side effect, this method expunges all stale entries in the
 * "run" containing the stale entry.  (A run is a sequence of entries
 * between two null slots.)
 *
 * @param  key the key
 * @param  value the value to be associated with key
 * @param  staleSlot index of the first stale entry encountered while
 *         searching for key.
 */
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                               int staleSlot) {
    // 入參 key: set 操作傳過來的 key
    // 入參 value: set 操作傳過來的 value,與參數 key 相關聯
    // 入參 staleSlot: 待替換的過時 Entry 的下標
    
    ThreadLocal.ThreadLocalMap.Entry[] tab = table;
    int len = tab.length;
    ThreadLocal.ThreadLocalMap.Entry e;

    // Back up to check for prior stale entry in current run.
    // We clean out whole runs at a time to avoid continual
    // incremental rehashing due to garbage collector freeing
    // up refs in bunches (i.e., whenever the collector runs).
    // slotToExpunge 變量目標是存儲當前區間段(兩個 null 元素之間),第一個過時 Entry 的下標。
    // 將入參的過時 Entry 下標賦值給 slotToExpunge。
    int slotToExpunge = staleSlot;
    // 這里需要格外注意一下,這里并不是遞增下標,而是對下標進行遞減。
    // prevIndex ((i - 1 >= 0) ? i - 1 : len - 1)。
    // 向前進行遍歷直到遇到為 null 的元素。
    for (int i = prevIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = prevIndex(i, len))
        if (e.get() == null)
            // 將遍歷過程中過時 Entry 的下標賦值給 slotToExpunge 變量。
            // 經過當前遍歷邏輯,slotToExpunge 將存儲兩個 null 元素之間第一個過時 key 的下標。
            slotToExpunge = i;

    // Find either the key or trailing null slot of run, whichever
    // occurs first
    // 這里是由入參的過時 Entry 下標開始向后遍歷,直到遇到 null 元素。
    for (int i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        ThreadLocal<?> k = e.get();

        // If we find key, then we need to swap it
        // with the stale entry to maintain hash table order.
        // The newly stale slot, or any other stale slot
        // encountered above it, can then be sent to expungeStaleEntry
        // to remove or rehash all of the other entries in run.
        if (k == key) {
            // 進入當前分支表示在遍歷的過程中找到了被 set 的 Entry 對象的本體。
            // 將和 key 關聯的新 value 值賦值給本體。
            e.value = value;
            // 操作一
            // 將 Entry 對象本體和入參 staleSlot 位置的過時 Entry 進行交換,
            // 結果是set操作的 key 與 value,無論之前本體存儲在哪里,
            // 最終都會存儲在入參的 staleSlot 下標,符合方法名中的 replace 含義。
            tab[i] = tab[staleSlot];
            tab[staleSlot] = e;

            // Start expunge at preceding stale entry if it exists
            if (slotToExpunge == staleSlot)
                // 如果當前區間段第一個過時 Entry 下標仍是 staleSlot 下標,
                // 那么需要將當前 i 下標賦值給 slotToExpunge ,因為 staleSlot 下標已經存儲了 set 操作的 Entry 對象,
                // 導致當前 i 下標變成了第一個過時 Entry 的下標。
                slotToExpunge = i;
            // 先調用 expungeStaleEntry 方法清除 slotToExpunge 下標的過時 Entry,
            // 再從 expungeStaleEntry 方法返回的 null 元素的下標開始執行 cleanSomeSlots 方法。
            cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
            // 已經完成替換過時條目操作,退出當前方法。
            return;
        }

        // If we didn't find stale entry on backward scan, the
        // first stale entry seen while scanning for key is the
        // first still present in the run.
        if (k == null && slotToExpunge == staleSlot)
            // 進入當前分支說明當前 Entry 是過時 Entry。
            // 如果當前區間段第一個過時 Entry 下標仍是入參的 staleSlot 下標,
            // 則需要將當前位置下標賦值給 slotToExpunge,因為最終當前位置的過時 Entry 將是
            // 當前區間段的第一個過時 Entry。因為 staleSlot 下標位置的過時 Entry 在之后的邏輯
            // 里要么被交換到當前下標之后(上文操作一),要么被新的 set 傳入的 Entry 覆蓋掉(下文操作二)。
            slotToExpunge = i;
    }

    // If key not found, put new entry in stale slot
    // 操作二
    // 代碼執行到當前位置說明 set 的 key 與 value 是一個新的 Entry,在之前并不存在。
    // 以 set 方法傳入的 key 和 value 值 new 一個新的 Entry 對象,并覆蓋在入參的 staleSlot 下標處。
    tab[staleSlot].value = null;
    tab[staleSlot] = new ThreadLocal.ThreadLocalMap.Entry(key, value);

    // If there are any other stale entries in run, expunge them
    if (slotToExpunge != staleSlot)
        // slotToExpunge 與 staleSlot 相等則說明當前區間段只有入參 staleSlot 位置有過時 Entry,
        // 并且該過時 Entry 已被覆蓋,所以無需清理,無需進入當前分支。
        
        // 進入當前分支說明當前區間段,除了被覆蓋的過時 Entry,至少還存在一個過時 Entry,
        // slotToExpunge 下標為第一個過時 Entry 的下標。
        // 先調用 expungeStaleEntry 方法清除 slotToExpunge 下標的過時 Entry,
        // 再從 expungeStaleEntry 方法返回的 null 元素的下標開始執行 cleanSomeSlots 方法。
        cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}

在方法中 slotToExpunge 變量之所以始終要存儲當前區間段(兩個 null 元素之間)的第一個過時 Entry,是因為每當刪除一個過時 Entry 后都會對后續 Entry 進行 rehash 操作,如果清理的不是第一個過時 Entry,那么在后續其他邏輯觸發清理第一個過時 Entry 時還會將剛剛 rehash 過的元素再次 rehash 一遍,極大的影響效率。

至此在 ThreadLocalMap 中涉及清理過時Entry的三個方法都已剖析完畢,下面我們來羅列一下什么時候會觸發這三個方法。

清理方法調用梳理

為避免截圖過多影響閱讀體驗,這里將只粘出調用的起點,并給調用鏈路,大家后續可以自己在源碼中點一點。

get方法

調用鏈路:ThreadLocal#get->ThreadLocalMap#getEntry->ThreadLocalMap#getEntryAfterMiss->ThreadLocalMap#expungeStaleEntry

圖片圖片

set方法

調用鏈路 1:ThreadLocal#set->ThreadLocalMap#set->ThreadLocalMap#replaceStaleEntry

調用鏈路 2:ThreadLocal#set->ThreadLocalMap#set->ThreadLocalMap#cleanSomeSlots

調用鏈路 3:ThreadLocal#set->ThreadLocalMap#set->ThreadLocalMap#rehash->ThreadLocalMap#expungeStaleEntries->ThreadLocalMap#expungeStaleEntry

圖片圖片

remove方法

調用鏈路:ThreadLocal#remove->ThreadLocalMap#remove->ThreadLocalMap#expungeStaleEntry

圖片圖片

總結

通過兩期文章的深度剖析,大家應該對 ThreadLocal 的 API 使用以及內存泄露問題有了進一步的理解。

ThreadLocal 優勢是無鎖化提升并發性能和簡化變量的傳遞邏輯。

在實際業務中使用 ThreadLocal 類時應該在恰當位置調用 remove 方法顯式移除值。

盡可能的避免觸發 ThreadLocal 清理過時 Entry 的邏輯,從而提高 ThreadLocal 性能。

例如使用繼承的 ThreadLocal 類,并重寫 finalize 方法,確保 ThreadLocal 對象在被垃圾回收前,remove 方法會被調用。

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

2022-08-26 07:33:49

內存JVMEntry

2021-04-23 20:59:02

ThreadLocal內存

2024-10-28 08:15:32

2018-10-25 15:24:10

ThreadLocal內存泄漏Java

2022-10-18 08:38:16

內存泄漏線程

2017-01-11 14:02:32

JVM源碼內存

2024-06-24 08:11:37

2023-11-03 08:10:49

ThreadLoca內存泄露

2023-05-29 07:17:48

內存溢出場景

2013-12-23 09:25:21

2021-05-26 08:02:03

ThreadLocal多線程多線程并發安全

2023-09-22 17:34:37

內存remove方法

2021-05-10 11:55:57

ThreadLocal內存Java

2024-03-22 13:31:00

線程策略線程池

2010-10-25 10:10:27

ibmdwJava

2010-05-31 16:53:21

Java

2017-01-12 14:52:03

JVMFinalRefere源碼

2020-06-23 09:48:09

Python開發內存

2025-10-15 00:26:20

2011-08-16 09:34:34

Nginx
點贊
收藏

51CTO技術棧公眾號

成人福利在线| 91丨九色丨海角社区| 精品国产一区二区三区成人影院| 午夜精品福利在线| 天天爽天天狠久久久| 国产又黄又猛又爽| 亚洲美女黄网| 日日骚久久av| 中文成人无字幕乱码精品区| 精品视频在线一区二区在线| 一区二区三区日韩精品视频| 免费观看成人在线| 国产熟女一区二区三区四区| 亚洲一区二区毛片| 欧美日韩国产成人| 手机看片福利视频| 国产精品极品在线观看| 欧美日韩色综合| 老太脱裤让老头玩ⅹxxxx| 91在线免费看| 91在线播放网址| 91成人免费视频| 中文区中文字幕免费看| 一区二区高清| 久久综合九色九九| 18精品爽国产三级网站| 欧美色图五月天| 日韩一卡二卡三卡四卡| 污污的视频免费| 天然素人一区二区视频| 亚洲超碰97人人做人人爱| 手机看片日韩国产| 在线免费看黄网站| 国产性天天综合网| 欧美国产一二三区| 神马午夜精品95| 成人一区二区三区在线观看| 91精品国产自产在线老师啪| 国产裸体美女永久免费无遮挡| 在线一区免费观看| 欧美日本精品在线| 69av视频在线| 女人香蕉久久**毛片精品| 色综久久综合桃花网| 欧美黄色一级生活片| 久久91麻豆精品一区| 日韩电影中文字幕在线观看| 黄色性视频网站| 国产丝袜一区| 亚洲国产福利在线| 日本黄色免费观看| 欧美大胆视频| 亚洲精品在线看| 欧美亚一区二区三区| 少妇一区二区三区| 日韩精品福利网站| 久久久久亚洲av无码专区桃色| 久久aimee| 日韩国产精品视频| 91中文字幕永久在线| 中文字幕亚洲影视| 在线视频欧美日韩| 精品国产大片大片大片| 亚洲自拍偷拍网| 欧美国产精品人人做人人爱| 麻豆亚洲av成人无码久久精品| 韩国自拍一区| 欧美性在线视频| 天堂网视频在线| 免费观看成人鲁鲁鲁鲁鲁视频| 国产精品一区久久久| 一区二区三区www污污污网站| 九色|91porny| 成人自拍视频网站| 水莓100在线视频| 国产香蕉久久精品综合网| 亚洲精品在线免费| 中文字幕中文字幕在线中高清免费版| 亚洲综合色区另类av| 阿v天堂2018| 日韩高清成人| 91精品国产综合久久福利软件| 性xxxxxxxxx| 校花撩起jk露出白色内裤国产精品 | 精品国产乱码久久久久久蜜臀 | 欧美激情奇米色| 麻豆久久久久久久久久| 日本大胆欧美人术艺术动态| 国产精品视频一区二区高潮| 亚洲经典一区二区三区| 91麻豆精东视频| 91麻豆天美传媒在线| av剧情在线观看| 欧美午夜在线观看| 熟妇高潮一区二区| 日本不卡高清| 久久久久久成人精品| www.日韩一区| 成人午夜视频网站| 日韩在线三级| 美女精品导航| 欧美日韩久久不卡| 国产在线观看无码免费视频| 97久久夜色精品国产| 97在线视频精品| 国产精品国产av| 久久久久久久综合| 日韩极品视频在线观看| 久久精品 人人爱| 日韩电影中文字幕在线| 欧美成人手机视频| 蜜臀av国产精品久久久久| 国产日本一区二区三区| 欧美a免费在线| 色先锋aa成人| av天堂一区二区| 亚洲国产不卡| 国产精品久久久久久久久男| 日日躁夜夜躁白天躁晚上躁91| 中文字幕在线观看一区| 精品久久久久av| 欧美激情久久久久久久久久久| 久久综合五月天| 伊人久久成人网| 久久久久久久久久久99999| 青春草国产视频| 欧美午夜在线播放| 日韩资源在线观看| 人人妻人人爽人人澡人人精品| 成人涩涩免费视频| wwwjizzjizzcom| 96sao精品免费视频观看| 伊人青青综合网站| 成年人视频免费| 久久这里只有精品6| 大片在线观看网站免费收看| 伊人久久大香伊蕉在人线观看热v| 国产亚洲精品成人av久久ww| 四虎成人永久免费视频| 99久久免费精品| 青青草精品视频在线| 亚洲高清在线一区| 久久91亚洲精品中文字幕| 国产伦精品一区二区三区四区 | 亚洲缚视频在线观看| 免费在线黄色网| 国内久久精品视频| 成人免费看片视频在线观看| 九九99久久精品在免费线bt| 超碰日本道色综合久久综合| 99热这里只有精品3| 亚洲欧美日韩国产成人精品影院| 伊人免费视频二| 欧美理论在线| 国产另类第一区| 黄色激情在线播放| 亚洲美女视频网站| 羞羞色院91蜜桃| 国产精品久久久久影视| 免费成人黄色大片| 欧美一区影院| 国产精品加勒比| 97人人在线视频| 亚洲精品一区二区在线| 波多野结衣高清在线| 国产精品美女久久久久av爽李琼| 亚洲色图欧美自拍| 狠狠色综合网| 免费成人av网站| 久久伊人国产| 欧美日韩国产999| 日本免费不卡| 精品视频一区三区九区| 五月天丁香激情| 91亚洲大成网污www| 国产视频一区二区三区在线播放 | 一区二区三区在线播放欧美| 91国内精品视频| 亚洲免费伊人电影| 黄色片视频免费观看| 日一区二区三区| 在线观看成人免费| 人妖一区二区三区| 国产精品一区二区3区| 免费在线看污片| 亚洲美女视频网| 国产人妖在线播放| 欧美日韩视频在线| 二区三区四区视频| 99久久婷婷国产| 最新av免费在线观看| 亚洲久久一区| 自拍另类欧美| 最新亚洲精品| 97碰碰视频| 欧美精选视频一区二区| 久久久久久久久久久av| av中文字幕在线| 亚洲激情视频网| 91theporn国产在线观看| 精品露脸国产偷人在视频| 国精产品久拍自产在线网站| 99精品视频在线播放观看| 亚欧精品在线视频| 麻豆九一精品爱看视频在线观看免费| 欧美性受黑人性爽| 国产麻豆精品久久| 国产精品伊人日日| 亚洲色图图片| 国产成人精品视频在线| 福利在线导航136| 久久久99免费视频| 国产黄色免费在线观看| 亚洲国产成人在线视频| 国产日本精品视频| 欧美性xxxxxx少妇| 日日夜夜操视频| 亚州成人在线电影| 国产av无码专区亚洲av毛网站| 久久麻豆一区二区| 日韩无码精品一区二区| 韩国v欧美v日本v亚洲v| 欧美日韩一区二区三区69堂| 亚洲在线日韩| 亚洲国产精品无码观看久久| 一区二区三区国产精华| 一区二区日本| 日韩成人精品一区二区| 欧美日韩在线一二三| 欧美a级网站| 国产丝袜不卡| 91精品尤物| 999视频在线免费观看| 四虎精品在线观看| 国产精品99久久久久久久久久久久| av中文在线资源库| 欧美极品少妇xxxxⅹ喷水 | 精品久久综合| 欧美日韩高清在线一区| 宅男在线一区| 神马影院我不卡| 成人aaaa| 在线一区亚洲| 香蕉视频官网在线观看日本一区二区| 亚洲综合欧美日韩| 欧美wwwww| 四虎影院一区二区| 亚洲午夜精品一区二区国产 | 国产精品久久国产三级国电话系列| 中文成人激情娱乐网| 成人免费大片黄在线播放| 在线观看欧美| 亚洲最大av在线| 日本一区精品视频| 国产伦精品一区二区三区高清| 国产精品对白| 久久久一本精品99久久精品66| 外国成人在线视频| 日本成人三级电影网站| 日韩欧美中文| 一级特黄妇女高潮| 亚洲免费成人| 中文字幕欧美人妻精品一区| 奇米影视一区二区三区小说| 午夜精品中文字幕| 国产成人免费高清| 亚洲色图14p| 国产午夜精品一区二区三区视频| 91导航在线观看| 曰韩精品一区二区| 草久久免费视频| 欧美在线观看视频一区二区三区| 中文字幕在线观看国产| 欧美肥妇毛茸茸| 女人18毛片水真多18精品| 亚洲男人天堂九九视频| 在线观看av黄网站永久| 欧美高清视频一区二区| 一个人www视频在线免费观看| 国产精品狼人色视频一区| 久久久久久久久久久久电影| 久久精品美女| 天天影视欧美综合在线观看| 成人免费观看cn| 日本 国产 欧美色综合| 亚洲熟妇一区二区| 久久精品日产第一区二区三区高清版| 国产精品免费在线视频| 亚洲va欧美va国产va天堂影院| 国产午夜无码视频在线观看| 日韩一区二区三区四区| 精品乱码一区二区三四区视频| 色综合亚洲精品激情狠狠| 2018av在线| 国产欧美va欧美va香蕉在| 澳门久久精品| 亚洲二区三区四区| 激情五月***国产精品| 中文字幕第88页| hitomi一区二区三区精品| 男人的午夜天堂| 欧美日韩亚洲视频一区| japanese国产| 中文字幕精品一区二区精品| 久久久男人天堂| 97se亚洲综合在线| 欧美mv日韩| 国产精品人人妻人人爽人人牛| 成人av在线影院| 中文字幕电影av| 91久久免费观看| 少妇人妻精品一区二区三区| 成人444kkkk在线观看| yw.尤物在线精品视频| 久久久久久国产精品一区| 欧美暴力喷水在线| 91亚洲免费视频| 国产日产亚洲精品系列| 久久精品国产成人av| 欧美大胆人体bbbb| av在线app| 国产免费一区视频观看免费| 国产精品片aa在线观看| www黄色日本| 成人一区二区三区中文字幕| 久久久精品视频免费观看| 6080亚洲精品一区二区| 成人h小游戏| 国产成人在线一区| 中文精品一区二区| 日韩a在线播放| 91亚洲男人天堂| 久久国产黄色片| 亚洲免费精彩视频| 手机av在线| 久久久久国产精品视频| 国产一区白浆| 大地资源二中文在线影视观看 | 在这里有精品| 日本高清视频免费在线观看| 国模大尺度一区二区三区| 永久免费看mv网站入口| 欧美日本乱大交xxxxx| 天堂资源在线中文| 成人精品一区二区三区电影免费| 久久综合成人| 日韩av.com| 中文字幕一区二区三区在线播放| 亚洲最大成人av| 久久黄色av网站| 激情综合婷婷| 欧美中文字幕在线观看视频| 成人少妇影院yyyy| 中文字幕一区二区三区精品| 日韩久久午夜影院| gay欧美网站| 色一情一乱一伦一区二区三区 | 国产精品亚洲片夜色在线| 色喇叭免费久久综合| 亚洲最大天堂网| 亚洲另类中文字| 天堂在线资源8| 日韩av快播网址| 色天天久久综合婷婷女18| 欧美xxxxxbbbbb| 亚洲最新在线观看| 青青国产在线| 国产日韩欧美在线播放| 欧美成人久久| av无码一区二区三区| 91国偷自产一区二区使用方法| 婷婷视频在线| 国产精品永久入口久久久| 葵司免费一区二区三区四区五区| 国产日产在线观看| 精品国精品国产尤物美女| 中文在线中文资源| 在线观看免费91| aa级大片欧美| 亚洲视频在线免费播放| 欧美国产日韩免费| 免费看成人吃奶视频在线| 亚洲欧美日韩综合网| 亚洲一二三四久久| 国产美女视频一区二区三区| 亚洲va电影大全| 男人的天堂亚洲在线| 永久免费看mv网站入口| 日韩av在线直播| 色综合视频一区二区三区44| 欧美日韩一道本| 成人欧美一区二区三区白人| 日本天堂影院在线视频| 成人黄色av网| 久久av最新网址| 九九久久免费视频| 在线午夜精品自拍| 日韩理论电影中文字幕| 亚洲制服中文字幕| 欧美性猛xxx|