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

色av成人天堂桃色av| 波多野结衣亚洲一区| 中文字幕亚洲欧美一区二区三区| 一区二区三区视频在线观看免费| fc2在线中文字幕| 日韩国产欧美三级| 日韩一区二区av| 日本黄色一级网站| 新版的欧美在线视频| 国产调教视频一区| 亚洲精品日韩av| 精品在线免费观看视频| 中文字幕亚洲影视| 91精品国产一区二区| 中国丰满熟妇xxxx性| 青青草观看免费视频在线 | 成人综合婷婷国产精品久久免费| 韩国福利视频一区| 人与动物性xxxx| 99精品在免费线中文字幕网站一区| 欧美激情va永久在线播放| 色呦色呦色精品| 免费网站在线观看人| 2024国产精品视频| 成人日韩在线电影| 国产一级精品视频| 99久久99久久精品国产片果冰| 日韩美一区二区三区| 日韩 欧美 高清| 最新国产在线拍揄自揄视频| 91网上在线视频| 国产欧美精品一区二区| 中文字幕一区二区三区精品| 日韩综合一区| 亚洲国模精品一区| www.偷拍.com| 99久久综合国产精品二区| 亚洲一区二区三区不卡国产欧美 | crdy在线观看欧美| 色综合久久中文综合久久97| 日韩不卡视频一区二区| 巨骚激情综合| 99国产精品99久久久久久| 亚洲tv在线观看| 久久久国产免费| 日韩视频一区| 欧美高清激情视频| 波多野结衣喷潮| 国产亚洲一区二区三区不卡| 亚洲аv电影天堂网| 一级淫片在线观看| 国产精品久久乐| 色狠狠一区二区| 97视频在线免费播放| 黄色18在线观看| 亚洲中国最大av网站| av磁力番号网| 精品自拍一区| 日韩一区日韩二区| 三年中文高清在线观看第6集| 九色网友自拍视频手机在线| 91天堂素人约啪| 精品国产乱码久久久久软件| 亚洲精品成人电影| 国产a精品视频| 999在线免费观看视频| 国产精品久久久久久久免费| 久热精品在线| 国产精品91久久| 精品国产乱子伦| 久久久噜噜噜| 国产精品日韩av| 中文字幕av免费观看| 日本欧美久久久久免费播放网| 青草热久免费精品视频| 国产性生活视频| 日韩国产欧美三级| 国产日韩精品电影| 国产情侣激情自拍| 懂色av一区二区三区免费看| 国产精品久久久一区二区三区| 丰满熟妇人妻中文字幕| 99热99精品| 欧美精品成人一区二区在线观看| 欧美视频综合| 中文字幕第一区第二区| 一区二区三区精品国产| av网站导航在线观看免费| 一区二区视频在线| 久在线观看视频| 欧美专区福利免费| 欧美情侣在线播放| 免费啪视频在线观看| 先锋影音国产精品| 综合久久五月天| 欧美卡一卡二卡三| 一本久久知道综合久久| 国产成人精品优优av| 亚洲中文字幕一区二区| 国产成人免费高清| 欧洲精品码一区二区三区免费看| 888av在线| 亚洲综合清纯丝袜自拍| 成人在线看视频| 亚洲午夜剧场| 亚洲精品久久久久久久久久久久| 精品欧美一区二区久久久| 围产精品久久久久久久| 国内偷自视频区视频综合| 高潮毛片又色又爽免费| 国产一区不卡视频| 欧美日韩一区二区视频在线| 国产理论在线观看| 欧美日韩一区二区免费视频| 免费成年人高清视频| 红杏一区二区三区| 日韩在线观看av| 精品美女久久久久| 国产在线看一区| 看欧美日韩国产| 在线三级中文| 欧美色图12p| 精品中文字幕在线播放| 97精品一区二区| 欧美亚洲一级片| 国产福利第一视频| 国产精品网站导航| 免费看国产一级片| 欧美做受高潮1| 亚洲调教欧美在线| 日韩欧美视频在线播放| 久久免费在线观看| 亚洲一区精品在线观看| 99国产欧美久久久精品| 一二三在线视频| 中文字幕一区二区三区人妻在线视频| 一区二区三区国产好| 亚洲男人7777| 久操视频免费在线观看| 久久精品国产免费| 欧美日韩国产一二| 华人av在线| 天天天综合网| 欧美日韩xxxxx| 久久久999久久久| av高清久久久| 成年在线观看视频| 亚洲青青一区| 日日骚久久av| 中文天堂在线视频| 久久久久久久久蜜桃| 精品国产一二三四区| 综合成人在线| 色综合导航网站| 污视频网址在线观看| 超碰成人免费| 理论片在线不卡免费观看| 天天综合久久综合| 国产亚洲一区二区三区四区| 亚洲美免无码中文字幕在线| 福利欧美精品在线| 欧美极品少妇xxxxx| jlzzjlzz亚洲女人18| 亚洲人精品午夜| 日本一二三四区视频| 亚洲成人一区| 91视频最新| 四虎影院观看视频在线观看| 欧美一区二区国产| 久久久久无码国产精品不卡| 国产 欧美在线| 国产成人永久免费视频| 亚洲国产高清在线观看| 欧美精品videos性欧美| 少妇高潮一区二区三区99小说| 亚洲午夜私人影院| 先锋资源av在线| 校园激情久久| 少妇特黄a一区二区三区 | 中日韩免费视频中文字幕| 热久久精品免费视频| 欧美日韩性在线观看| 国产精品久久久久aaaa九色| 97视频在线观看网站| 欧美剧在线免费观看网站| 中文字幕av播放| 国产成人免费av在线| 成人在线播放网址| 亚洲日产av中文字幕| 国产精品视频自在线| 国产一二区在线观看| 亚洲精品在线三区| 无码人妻丰满熟妇区bbbbxxxx| 国产欧美日韩另类一区| 日韩av.com| 激情久久中文字幕| 日韩精品电影网站| 国产美女精品视频免费播放软件| 欧美国产日韩在线| 欧美色综合一区二区三区| 欧美日韩国产免费| 日本一区二区不卡在线| 国产调教视频一区| 精品国产午夜福利在线观看| 国产情侣一区| 亚洲欧洲精品一区二区三区波多野1战4 | h视频网站在线观看| 宅男在线国产精品| 国产无遮挡呻吟娇喘视频| 国产精品久久久久久久久久久免费看| 一级全黄裸体片| 日韩中文欧美在线| 国产精品无码免费专区午夜| 欧美日韩色图| 国产精品久久久对白| 成人国产一区| 国产69久久精品成人| 黄网站免费在线观看| 日韩精品在线私人| wwwav在线播放| 在线欧美小视频| 欧美一级高潮片| 综合欧美一区二区三区| 无码人妻aⅴ一区二区三区69岛| 国产精品一区二区久激情瑜伽 | 日韩一区欧美二区| 福利视频免费在线观看| 外国成人激情视频| 丝袜美腿玉足3d专区一区| 国内精品国产成人国产三级粉色| 国产精品主播视频| 精品91久久| 久久久久久久久久久亚洲| 日本视频在线| 亚洲午夜av久久乱码| 日本美女一级片| 8v天堂国产在线一区二区| 嫩草影院一区二区三区| 狠狠躁天天躁日日躁欧美| 九九九在线视频| 中文字幕综合网| 色欲狠狠躁天天躁无码中文字幕| av网站免费线看精品| 免费看三级黄色片| 久久成人免费网| 香蕉视频禁止18| 久热国产精品| 欧洲av无码放荡人妇网站| 最新成人av网站| 欧美一级欧美一级| 激情偷拍久久| 无码熟妇人妻av在线电影| 伊人成综合网| 400部精品国偷自产在线观看 | 亚洲精美色品网站| 成人免费一级视频| 日韩精品在线看片z| www.桃色av嫩草.com| 日韩视频免费直播| 精品人妻一区二区三区日产乱码 | 久久视频精品在线| 九色porny丨首页在线| 麻豆成人在线看| www视频在线看| 两个人的视频www国产精品| 97caopron在线视频| 欧美精品中文字幕一区| 在线中文免费视频| 久久久久久久影院| 三级在线看中文字幕完整版| 2024亚洲男人天堂| 欧美成人精品一区二区男人小说| 日产日韩在线亚洲欧美| 唐人社导航福利精品| 国产精品毛片a∨一区二区三区|国| 国产超碰精品| 国产精品中文字幕在线观看| 亚洲色图图片| 波多野结衣精品久久| 激情小说一区| 色中色综合成人| 天天做天天爱天天综合网| 欧美日韩视频免费| 国产精品亚洲综合色区韩国| 搡女人真爽免费午夜网站| 麻豆精品一区二区av白丝在线| 樱花草www在线| 成人网页在线观看| 免费在线观看你懂的| 国产精品久久久久影院亚瑟 | 2022亚洲天堂| 日韩影院免费视频| 无码人妻少妇色欲av一区二区| 成人黄色在线看| 国产jk精品白丝av在线观看| 国产精品久线观看视频| 国产精品成人久久| 欧美在线观看18| 精品国产乱码久久久久久蜜臀网站| 亚洲激情在线视频| 日本欧美在线视频免费观看| 国产+人+亚洲| 国产精品原创视频| 国产一区二区三区四区五区在线| 欧州一区二区| 国产在线xxxx| 免费在线看成人av| caopor在线| 国产农村妇女毛片精品久久麻豆| 欧美日韩综合一区二区| 色老头久久综合| 性欧美8khd高清极品| 一区二区欧美久久| 成人超碰在线| 国产剧情日韩欧美| 香蕉一区二区| 日韩精品福利片午夜免费观看| 美女久久网站| 国产大学生视频| 亚洲欧洲中文日韩久久av乱码| 69成人免费视频| 日韩欧美在线一区二区三区| 高清毛片在线看| 98视频在线噜噜噜国产| 在线成人免费| 日韩成人av网站| 国产精品亚洲产品| 在线中文字日产幕| 最新国产精品久久精品| 亚洲永久精品一区| 亚洲欧美国产高清va在线播| 久草在线视频资源| 成人一区二区电影| 第一会所sis001亚洲| 九色在线视频观看| 成人美女在线视频| 九九热精彩视频| 欧美二区三区91| 成人全视频高清免费观看| 欧美壮男野外gaytube| 成午夜精品一区二区三区软件| 日本黄色a视频| 秋霞电影一区二区| 波多野结衣av在线观看| 欧美日韩亚洲精品内裤| 乱色精品无码一区二区国产盗| 美女福利精品视频| 亚洲综合资源| 中文字幕制服丝袜在线| 久久精品二区亚洲w码| 性猛交娇小69hd| 色噜噜久久综合| 免费在线稳定资源站| 51视频国产精品一区二区| 久久午夜影院| 亚洲 欧美 日韩 国产综合 在线| 成人国产精品免费网站| 国产中文字字幕乱码无限| 欧美mv日韩mv国产网站app| 特级毛片在线| 国产精品国产精品国产专区不卡| 欧美精品1区| 亚洲精品久久久久久| 一区二区在线观看免费视频播放| 国产免费一区二区三区免费视频| 日韩中文字幕欧美| 国产免费av国片精品草莓男男| 麻豆映画在线观看| 成人精品免费看| 国产成人在线播放视频| 日韩精品视频在线播放| 欧美舌奴丨vk视频| 亚洲春色在线| 激情五月激情综合网| 久久久www成人免费毛片| 精品福利一二区| 五月天国产在线| 香蕉久久免费影视| 国产综合成人久久大片91| 欧美日韩免费做爰视频| 精品99久久久久久| 国产伦精品一区二区三区视频金莲| 欧美精品国产精品久久久 | 亚洲日本免费电影| www.亚洲成人网| 91看片淫黄大片一级在线观看| 波多野结衣绝顶大高潮| 精品国产视频在线| 成人h动漫精品一区二区器材| 五十路熟女丰满大屁股| 久久精品男人的天堂| 国产精品伊人久久| 91av在线视频观看| 97精品视频| 日本人添下边视频免费| 91国产丝袜在线播放| 伊人在我在线看导航| 欧美三日本三级少妇三99| 极品美女销魂一区二区三区| 国产无码精品在线观看|