一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - JVM FULL GC 生產(chǎn)問題筆記

JVM FULL GC 生產(chǎn)問題筆記

2021-04-12 23:58今日頭條老馬嘯西風(fēng) Java教程

造成 full gc 的原因一般都是內(nèi)存泄漏。GC 日志真的很重要,遇到問題一定要記得添加上,這樣才能更好的分析解決問題。

故事的開始

早晨 8 點(diǎn)多,同事給我發(fā)了一條消息。

“跑批程序很慢,負(fù)載過高,上午幫忙看一下。”

我一邊走路,一遍回復(fù)好的,整個人都是懵的,一方面是因?yàn)闆]睡飽,另一方面是因?yàn)閷ν碌某绦蛞粺o所知。

而這,就是今天整個故事的開始。

JVM FULL GC 生產(chǎn)問題筆記

問題的定位

 

到了公司,簡單了解情況之后,開始登陸機(jī)器,查看日志。

一看好家伙,最簡單的一個請求 10S+,換做實(shí)時鏈路估計(jì)直接炸鍋了。

于是想到兩種可能:

(1)數(shù)據(jù)庫有慢 SQL,歸檔等嚴(yán)重影響性能的操作

(2)應(yīng)用 FULL GC

于是讓 DBA 幫忙定位是否有第一種情況的問題,自己登陸機(jī)器看是否有 FULL GC。

初步的解決

 

十幾分鐘后,DBA 告訴我確實(shí)有慢 SQL,已經(jīng) kill 掉了。

GC 日志

不過查看 GC 日志的道路卻一點(diǎn)都不順利。

(1)發(fā)現(xiàn)應(yīng)用本身沒打印 gc log

(2)想使用 jstat 發(fā)現(xiàn) docker 用戶沒權(quán)限,醉了。

于是讓配管幫忙重新配置 jvm 參數(shù)加上 gc 日志,幸運(yùn)的是,這個程序?qū)儆谂芘绦颍梢噪S時發(fā)布。

剩下的就等同事來了,下午驗(yàn)證一下即可。

FULL-GC 的源頭

 

慢的源頭

有了 GC 日志之后,很快就定位到慢是因?yàn)橐恢痹诎l(fā)生 full gc 導(dǎo)致的。

那么為什么會一直有 full gc 呢?

jvm 配置的調(diào)整

一開始大家都以為是 jvm 的新生代配置的太小了,于是重新調(diào)整了 jvm 的參數(shù)配置。

結(jié)果很不幸,執(zhí)行不久之后還是會觸發(fā) full gc。

要定位 full gc 的源頭,只有開始看代碼了。

JVM FULL GC 生產(chǎn)問題筆記

代碼與需求

 

需求

首先說一下應(yīng)用內(nèi)需要解決的問題還是比較簡單的。

把數(shù)據(jù)庫里的數(shù)據(jù)全部查出來,依次執(zhí)行處理,不過有兩點(diǎn)需要注意:

(1)數(shù)據(jù)量相對較大,百萬級

(2)單條數(shù)據(jù)處理比較慢,希望處理的盡可能快。

業(yè)務(wù)簡化

為了便于大家理解,我們這里簡化所有的業(yè)務(wù),使用最簡單的 User 類來模擬業(yè)務(wù)。

基本的數(shù)據(jù)庫實(shí)體。

  1. /** 
  2.  * 用戶信息 
  3.  * @author binbin.hou 
  4.  * @since 1.0.0 
  5.  */ 
  6. public class User { 
  7.  
  8.     private Integer id; 
  9.  
  10.     public Integer getId() { 
  11.         return id; 
  12.     } 
  13.  
  14.     public void setId(Integer id) { 
  15.         this.id = id; 
  16.     } 
  17.  
  18.     @Override 
  19.     public String toString() { 
  20.         return "User{" + 
  21.                 "id=" + id + 
  22.                 '}'
  23.     } 
  24.  
  •  UserMapper.java

模擬數(shù)據(jù)庫查詢操作。

  1. public class UserMapper { 
  2.  
  3.     // 總數(shù),可以根據(jù)實(shí)際調(diào)整為 100W+ 
  4.     private static final int TOTAL = 100; 
  5.  
  6.     public int count() { 
  7.         return TOTAL; 
  8.     } 
  9.  
  10.     public List<User> selectAll() { 
  11.         return selectList(1, TOTAL); 
  12.     } 
  13.  
  14.     public List<User> selectList(int pageNum, int pageSize) { 
  15.         List<User> list = new ArrayList<User>(pageSize); 
  16.  
  17.         int start = (pageNum - 1) * pageSize; 
  18.         for (int i = start; i < start + pageSize; i++) { 
  19.             User user = new User(); 
  20.             user.setId(i); 
  21.             list.add(user); 
  22.         } 
  23.  
  24.         return list; 
  25.     } 
  26.  
  27.     /** 
  28.      * 模擬用戶處理 
  29.      * 
  30.      * @param user 用戶 
  31.      */ 
  32.     public void handle(User user) { 
  33.         try { 
  34.             // 模擬不同的耗時 
  35.             int id = user.getId(); 
  36.             if(id % 2 == 0) { 
  37.                 Thread.sleep(100); 
  38.             } else { 
  39.                 Thread.sleep(200); 
  40.             } 
  41.         } catch (InterruptedException e) { 
  42.             e.printStackTrace(); 
  43.         } 
  44.         System.out.println(System.currentTimeMillis() + " " + Thread.currentThread().getName() + " " + user); 
  45.     } 
  46.  

這里提供了幾個簡單的方法,這里為了演示方便,將總數(shù)固定為 100。

  • UserService.java

定義需要處理所有實(shí)體的一個接口。

  1. /** 
  2.  * 用戶服務(wù)接口 
  3.  * @author binbin.hou 
  4.  * @since 1.0.0 
  5.  */ 
  6. public interface UserService { 
  7.  
  8.  
  9.     /** 
  10.      * 處理所有的用戶 
  11.      */ 
  12.     void handleAllUser(); 
  13.  

 v1-全部加載到內(nèi)存

 

最簡單粗暴的方式,就是把所有數(shù)據(jù)直接加載到內(nèi)存。

  1. public class UserServiceAll implements UserService { 
  2.  
  3.  
  4.     /** 
  5.      * 處理所有的用戶 
  6.      */ 
  7.     public void handleAllUser() { 
  8.         UserMapper userMapper = new UserMapper(); 
  9.         // 全部加載到內(nèi)存 
  10.  
  11.         List<User> userList = userMapper.selectAll(); 
  12.         for(User user : userList) { 
  13.             // 處理單個用戶 
  14.             userMapper.handle(user); 
  15.         } 
  16.     } 
  17.  

這種方式非常的簡單,容易理解。

不過缺點(diǎn)也比較大,數(shù)據(jù)量較大的時候會直接把內(nèi)存打爆。

我也嘗試了一下這種方式,應(yīng)用直接假死,所以不可行。

v2-分頁加載到內(nèi)存

 

既然不能一把加載,那我很自然的就想到分頁。

  1. /** 
  2.  * 分頁查詢 
  3.  * @author binbin.hou 
  4.  * @since 1.0.0 
  5.  */ 
  6. public class UserServicePage implements UserService { 
  7.  
  8.     /** 
  9.      * 處理所有的用戶 
  10.      */ 
  11.     public void handleAllUser() { 
  12.         UserMapper userMapper = new UserMapper(); 
  13.         // 分頁查詢 
  14.         int total = userMapper.count(); 
  15.         int pageSize = 10; 
  16.  
  17.         int totalPage = total / pageSize; 
  18.         for(int i = 1; i <= totalPage; i++) { 
  19.             System.out.println("第" + i + " 頁查詢開始"); 
  20.             List<User> userList = userMapper.selectList(i, pageSize); 
  21.  
  22.             for(User user : userList) { 
  23.                 // 處理單個用戶 
  24.                 userMapper.handle(user); 
  25.             } 
  26.         } 
  27.     } 
  28.  

一般這樣處理也就夠了,不過因?yàn)橄胱非蟾斓奶幚硭俣龋率褂昧硕嗑€程,大概實(shí)現(xiàn)如下。

v3-分頁多線程

 

這里使用 Executor 線程池進(jìn)行單個數(shù)據(jù)的消費(fèi)處理。

主要注意點(diǎn)有兩個地方:

(1)使用 sublist 控制每一個線程處理的數(shù)據(jù)范圍

(2)使用 CountDownLatch 保證當(dāng)前頁處理完成后,才進(jìn)行到下一次分頁的查詢和處理。

  1. import com.github.houbb.thread.demo.dal.entity.User
  2. import com.github.houbb.thread.demo.dal.mapper.UserMapper; 
  3. import com.github.houbb.thread.demo.service.UserService; 
  4.  
  5. import java.util.List; 
  6. import java.util.concurrent.CountDownLatch; 
  7. import java.util.concurrent.Executor; 
  8. import java.util.concurrent.Executors; 
  9.  
  10. /** 
  11.  * 分頁查詢多線程 
  12.  * @author binbin.hou 
  13.  * @since 1.0.0 
  14.  */ 
  15. public class UserServicePageExecutor implements UserService { 
  16.  
  17.     private static final int THREAD_NUM = 5; 
  18.  
  19.     private static final Executor EXECUTOR = Executors.newFixedThreadPool(THREAD_NUM); 
  20.  
  21.     /** 
  22.      * 處理所有的用戶 
  23.      */ 
  24.     public void handleAllUser() { 
  25.         UserMapper userMapper = new UserMapper(); 
  26.         // 分頁查詢 
  27.         int total = userMapper.count(); 
  28.         int pageSize = 10; 
  29.  
  30.         int totalPage = total / pageSize; 
  31.         for(int i = 1; i <= totalPage; i++) { 
  32.             System.out.println("第 " + i + " 頁查詢開始"); 
  33.             List<User> userList = userMapper.selectList(i, pageSize); 
  34.  
  35.             // 使用多線程處理 
  36.             int count = userList.size(); 
  37.             int countPerThread = count / THREAD_NUM; 
  38.  
  39.             // 通過 CountDownLatch 保證當(dāng)前分頁執(zhí)行完成,才繼續(xù)下一個分頁的處理。 
  40.             CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM); 
  41.             for(int j = 0; j < THREAD_NUM; j++) { 
  42.                 int startIndex = j * countPerThread; 
  43.                 int endIndex = startIndex + countPerThread; 
  44.                 // 最后一個 
  45.                 if(j == THREAD_NUM - 1) { 
  46.                     endIndex = count
  47.                 } 
  48.  
  49.                 final int finalStartIndex = startIndex; 
  50.                 final int finalEndIndex = endIndex; 
  51.                 EXECUTOR.execute(()->{ 
  52.                     List<User> subList = userList.subList(finalStartIndex, finalEndIndex); 
  53.                     handleList(subList); 
  54.  
  55.                     // countdown 
  56.                     countDownLatch.countDown(); 
  57.                 }); 
  58.             } 
  59.  
  60.  
  61.             try { 
  62.                 countDownLatch.await(); 
  63.  
  64.                 System.out.println("第 " + i + " 頁查詢?nèi)客瓿?quot;); 
  65.             } catch (InterruptedException e) { 
  66.                 e.printStackTrace(); 
  67.             } 
  68.         } 
  69.     } 
  70.  
  71.     private void handleList(List<User> userList) { 
  72.         UserMapper userMapper = new UserMapper(); 
  73.  
  74.         // 處理 
  75.         for(User user : userList) { 
  76.             // 處理單個用戶 
  77.             userMapper.handle(user); 
  78.         } 
  79.     } 
  80.  

這個實(shí)現(xiàn)是有一點(diǎn)復(fù)雜,但是第一感覺還是沒啥問題。

為什么就 full gc 了呢?

sublist 的坑

 

這里使用了 sublist 方法,性能很好,也達(dá)到了分割范圍的作用。

不過一開始,我卻懷疑這里導(dǎo)致了內(nèi)存泄漏。

SubList 的源碼:

  1. private class SubList extends AbstractList<E> implements RandomAccess { 
  2.         private final AbstractList<E> parent; 
  3.         private final int parentOffset; 
  4.         private final int offset; 
  5.         int size
  6.  
  7.         SubList(AbstractList<E> parent, 
  8.                 int offset, int fromIndex, int toIndex) { 
  9.             this.parent = parent; 
  10.             this.parentOffset = fromIndex; 
  11.             this.offset = offset + fromIndex; 
  12.             this.size = toIndex - fromIndex; 
  13.             this.modCount = ArrayList.this.modCount; 
  14.         } 

可以看出SubList原理:

  1. 保存父ArrayList的引用;
  2. 通過計(jì)算offset和size表示subList在原始list的范圍;

由此可知,這種方式的subList保存對原始list的引用,而且是強(qiáng)引用,導(dǎo)致GC不能回收,故而導(dǎo)致內(nèi)存泄漏,當(dāng)程序運(yùn)行一段時間后,程序無法再申請內(nèi)存,拋出內(nèi)存溢出錯誤。

解決思路是使用工具類替代掉 sublist 方法,缺點(diǎn)是內(nèi)存占用會變多,比如:

  1. /** 
  2.  * @author binbin.hou 
  3.  * @since 1.0.0 
  4.  */ 
  5. public class ListUtils { 
  6.  
  7.     @SuppressWarnings("all"
  8.     public static List copyList(List list, int start, int end) { 
  9.         List results = new ArrayList(); 
  10.         for(int i = start; i < end; i++) { 
  11.             results.add(list.get(i)); 
  12.         } 
  13.         return results; 
  14.     } 
  15.  

經(jīng)過實(shí)測,發(fā)現(xiàn)并不是這個原因?qū)е碌摹rz

lambda 的坑

 

因?yàn)槭褂玫?jdk8,所以大家也就習(xí)慣性的使用 lambda 表達(dá)式。

  1. EXECUTOR.execute(()->{ 
  2.     //... 
  3. }); 

這里實(shí)際上是一個語法糖,會導(dǎo)致 executor 引用 sublist。

因?yàn)?executor 的生命周期是非常長的,從而會讓 sublist 一直得不到釋放。

后來把代碼調(diào)整了如下,full gc 也確認(rèn)解決了。

v4-分頁多線程 Task

 

我們使用 Task,讓 sublist 放在 task 中去處理。

  1. public class UserServicePageExecutorTask implements UserService { 
  2.  
  3.     private static final int THREAD_NUM = 5; 
  4.  
  5.     private static final Executor EXECUTOR = Executors.newFixedThreadPool(THREAD_NUM); 
  6.  
  7.     /** 
  8.      * 處理所有的用戶 
  9.      */ 
  10.     public void handleAllUser() { 
  11.         UserMapper userMapper = new UserMapper(); 
  12.         // 分頁查詢 
  13.         int total = userMapper.count(); 
  14.         int pageSize = 10; 
  15.  
  16.         int totalPage = total / pageSize; 
  17.         for(int i = 1; i <= totalPage; i++) { 
  18.             System.out.println("第 " + i + " 頁查詢開始"); 
  19.             List<User> userList = userMapper.selectList(i, pageSize); 
  20.  
  21.             // 使用多線程處理 
  22.             int count = userList.size(); 
  23.             int countPerThread = count / THREAD_NUM; 
  24.  
  25.             // 通過 CountDownLatch 保證當(dāng)前分頁執(zhí)行完成,才繼續(xù)下一個分頁的處理。 
  26.             CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM); 
  27.             for(int j = 0; j < THREAD_NUM; j++) { 
  28.                 int startIndex = j * countPerThread; 
  29.                 int endIndex = startIndex + countPerThread; 
  30.                 // 最后一個 
  31.                 if(j == THREAD_NUM - 1) { 
  32.                     endIndex = count
  33.                 } 
  34.  
  35.                 Task task = new Task(countDownLatch, userList, startIndex, endIndex); 
  36.                 EXECUTOR.execute(task); 
  37.             } 
  38.  
  39.             try { 
  40.                 countDownLatch.await(); 
  41.  
  42.                 System.out.println("第 " + i + " 頁查詢?nèi)客瓿?quot;); 
  43.             } catch (InterruptedException e) { 
  44.                 e.printStackTrace(); 
  45.             } 
  46.         } 
  47.     } 
  48.  
  49.     private void handleList(List<User> userList) { 
  50.         UserMapper userMapper = new UserMapper(); 
  51.  
  52.         // 處理 
  53.         for(User user : userList) { 
  54.             // 處理單個用戶 
  55.             userMapper.handle(user); 
  56.         } 
  57.     } 
  58.  
  59.     private class Task implements Runnable { 
  60.  
  61.         private final CountDownLatch countDownLatch; 
  62.  
  63.         private final List<User> allList; 
  64.  
  65.         private final int startIndex; 
  66.  
  67.         private final int endIndex; 
  68.  
  69.         private Task(CountDownLatch countDownLatch, List<User> allList, int startIndex, int endIndex) { 
  70.             this.countDownLatch = countDownLatch; 
  71.             this.allList = allList; 
  72.             this.startIndex = startIndex; 
  73.             this.endIndex = endIndex; 
  74.         } 
  75.  
  76.         @Override 
  77.         public void run() { 
  78.             try { 
  79.                 List<User> subList = allList.subList(startIndex, endIndex); 
  80.                 handleList(subList); 
  81.             } catch (Exception exception) { 
  82.                 exception.printStackTrace(); 
  83.             } finally { 
  84.                 countDownLatch.countDown(); 
  85.             } 
  86.         } 
  87.     } 
  88.  

我們這里做了一點(diǎn)上面沒有考慮到的點(diǎn),countDownLatch 可能無法被執(zhí)行,導(dǎo)致線程被卡主。

于是我們把 countDownLatch.countDown(); 放在 finally 中去執(zhí)行。

辛苦搞了大半天,按理說到這里故事應(yīng)該就結(jié)束了,不過現(xiàn)實(shí)比理論更加夢幻。

實(shí)際執(zhí)行的時候,這個程序總是會卡主一段時間,導(dǎo)致整體的效果很差,還沒有不適用多線程的效果好。

和其他同事溝通了一下,還是建議使用 生產(chǎn)-消費(fèi)者 模式去實(shí)現(xiàn)比較好,原因如下:

(1)實(shí)現(xiàn)相對簡單,不會產(chǎn)生奇奇怪怪的 BUG

(2)相對于 countDownLatch 的強(qiáng)制等待,生產(chǎn)-消費(fèi)者模式可以做到基本無鎖,性能更好。

于是,我晚上就花時間寫了一個簡單的 demo。

JVM FULL GC 生產(chǎn)問題筆記

 v5-生產(chǎn)消費(fèi)者模式

 

這里我們使用 ArrayBlockingQueue 作為阻塞隊(duì)列,也就是消息的存儲媒介。

當(dāng)然,你也可以使用公司的 mq 中間件來實(shí)現(xiàn)類似的效果。

  1. import com.github.houbb.thread.demo.dal.entity.User
  2. import com.github.houbb.thread.demo.dal.mapper.UserMapper; 
  3. import com.github.houbb.thread.demo.service.UserService; 
  4.  
  5. import java.util.List; 
  6. import java.util.concurrent.*; 
  7.  
  8. /** 
  9.  * 分頁查詢-生產(chǎn)消費(fèi) 
  10.  * @author binbin.hou 
  11.  * @since 1.0.0 
  12.  */ 
  13. public class UserServicePageQueue implements UserService { 
  14.  
  15.     // 分頁大小 
  16.     private final int pageSize = 10; 
  17.  
  18.     private static final int THREAD_NUM = 5; 
  19.  
  20.     private final Executor executor = Executors.newFixedThreadPool(THREAD_NUM); 
  21.  
  22.     private final ArrayBlockingQueue<User> queue = new ArrayBlockingQueue<>(2 * pageSize, true); 
  23.  
  24.     // 模擬注入 
  25.     private UserMapper userMapper = new UserMapper(); 
  26.  
  27.     // 消費(fèi)線程任務(wù) 
  28.     public class ConsumerTask implements Runnable { 
  29.  
  30.         @Override 
  31.         public void run() { 
  32.             while (true) { 
  33.                 try { 
  34.                     // 會阻塞直到獲取到元素 
  35.                     User user = queue.take(); 
  36.                     userMapper.handle(user); 
  37.                 } catch (InterruptedException e) { 
  38.                     e.printStackTrace(); 
  39.                 } 
  40.             } 
  41.         } 
  42.     } 
  43.  
  44.     // 初始化消費(fèi)者進(jìn)程 
  45.     // 啟動五個進(jìn)程去處理 
  46.     private void startConsumer() { 
  47.         for(int i = 0; i < THREAD_NUM; i++) { 
  48.             ConsumerTask task = new ConsumerTask(); 
  49.             executor.execute(task); 
  50.         } 
  51.     } 
  52.  
  53.     /** 
  54.      * 處理所有的用戶 
  55.      */ 
  56.     public void handleAllUser() { 
  57.         // 啟動消費(fèi)者 
  58.         startConsumer(); 
  59.  
  60.         // 分頁查詢 
  61.         int total = userMapper.count(); 
  62.         int pageSize = 10; 
  63.  
  64.         int totalPage = total / pageSize; 
  65.         for(int i = 1; i <= totalPage; i++) { 
  66.             // 等待消費(fèi)者處理已有的信息 
  67.             awaitQueue(pageSize); 
  68.  
  69.             System.out.println("第 " + i + " 頁查詢開始"); 
  70.             List<User> userList = userMapper.selectList(i, pageSize); 
  71.  
  72.             // 直接往隊(duì)列里面扔 
  73.             queue.addAll(userList); 
  74.  
  75.             System.out.println("第 " + i + " 頁查詢?nèi)客瓿?quot;); 
  76.         } 
  77.     } 
  78.  
  79.     /** 
  80.      * 等待,直到 queue 的小于等于 limit,才進(jìn)行生產(chǎn)處理 
  81.      * 
  82.      * 首先判斷隊(duì)列的大小,可以調(diào)整為0的時候,才查詢。 
  83.      * 不過因?yàn)椴樵円脖容^耗時,所以可以調(diào)整為小于 pageSize 的時候就可以準(zhǔn)備查詢 
  84.      * 從而保障消費(fèi)者不會等待太久 
  85.      * @param limit 限制 
  86.      */ 
  87.     private void awaitQueue(int limit) { 
  88.         while (true) { 
  89.             // 獲取阻塞隊(duì)列的大小 
  90.             int size = queue.size(); 
  91.  
  92.             if(size >= limit) { 
  93.                 try { 
  94.                     System.out.println("當(dāng)前大小:" + size + ", 限制大小: " + limit); 
  95.                     // 根據(jù)實(shí)際的情況進(jìn)行調(diào)整 
  96.                     Thread.sleep(100); 
  97.                 } catch (InterruptedException e) { 
  98.                     e.printStackTrace(); 
  99.                 } 
  100.             } else { 
  101.                 break; 
  102.             } 
  103.         } 
  104.     } 

整體的實(shí)現(xiàn)確實(shí)簡單很多,因?yàn)椴樵儽忍幚硪话阋欤酝?duì)列中添加元素時,這里進(jìn)行了等待。

當(dāng)然可以根據(jù)你的實(shí)際業(yè)務(wù)進(jìn)行調(diào)整等待時間等。

這里保證小于等于 pageSize 時才插入新的元素,保證不超過隊(duì)列的總長度,同時盡可能的讓消費(fèi)者不會進(jìn)入空閑等待狀態(tài)。

小結(jié)

 

總的來說,造成 full gc 的原因一般都是內(nèi)存泄漏。

GC 日志真的很重要,遇到問題一定要記得添加上,這樣才能更好的分析解決問題。

很多技術(shù)知識,我們以為熟悉了,往往還是存在不少坑。

要永遠(yuǎn)記得如無必要,勿增實(shí)體。

原文地址:https://www.toutiao.com/i6948809403172651534/

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: jk制服白丝超短裙流白浆 | 久久强奷乱码老熟女 | 黄网在线观看免费网站台湾swag | 国产悠悠视频在线播放 | 欧美精品日韩一区二区三区 | 国产偷窥 | 午夜香蕉 | 色综合天天娱乐综合网 | 亚洲欧美成人中文在线网站 | 无码骚夜夜精品 | 精品国产欧美一区二区 | 香蕉久久一区二区三区啪啪 | 丰满大屁股美女一级毛片 | 亚洲第一人黄所 | 色天天综合色天天碰 | 99久久国产亚洲综合精品 | 3d欧美人禽交 | 日韩在线免费播放 | 久久国产综合精品欧美 | 我的年轻漂亮继坶三级 | 欧美性f| 成人女人天堂午夜视频 | 青青草一区二区免费精品 | 特黄特级毛片免费视 | 日本妻子迷妹网 | 四虎免费在线观看 | 红楼影视h38bar在线线播放 | 欧美日韩亚洲一区二区三区在线观看 | 色婷婷久 | 成年性午夜免费视频网站不卡 | 免费av在线视频 | 99久久99热久久精品免费看 | 美女露奶奶 | 国产精品免费久久久久影院小说 | 日本久久影视 | 色婷婷狠狠 | sao虎在线精品永久在线 | 麻豆最新 | 99成人国产精品视频 | 欧美一级专区免费大片 | bt天堂在线最新版在线 |