亚洲一级簧片_性 毛片_国产乱子视频_久久影城_强伦女教师视频_成人精品久久

電腦怎么看多大內存

發布時間: 2023-04-16 08:04 閱讀: 文章來源:轉載

1 內存條、總線與DMA

計算機組成中內存或者叫主存是非常重要的部件。內存因為地位太重要,所以和CPU直接相連,通過數據總線進行數據傳輸,并通過地址總線來進行物理地址的尋址。

除了數據總線、地址總線還有控制總線、IO總線等。IO總線是用來連接各種外設的,例如USB全稱就是通用串行總線。再比如PCIE是目前最常見的IO總線之一。這里放一張B站硬件茶談的一張圖。

圖1-1 硬件圖

圖中CPU和左側內存條直接連,并通過PCIE總線與下方的PCIE插槽連接,在PCIE插槽上可以插顯卡,網卡,聲卡,硬盤等等。PCIE帶寬是共享的,如果某個設備用了x1路帶寬,則能用的就少一路,因為本質上每一路都是串行的。南橋和CPU之間也有PCIE通道,主要是提供給一些帶寬占用很低的外設。

南橋芯片位于主板上,一般在右下角,有個被動散熱下面壓著。南橋中有個很重要的設備就是DMA控制器,或者叫DMAC。DMA直接內存訪問,意思就是DMAC能夠直接訪問內存。即一般進行IO的時候,cpu會把總線完全交給DMAC(DMAC和CPU會分時掌控總線),DMAC訪問設備如磁盤,將數據讀到內存中,因為此時接管了總線,所以可以寫內存。在這個過程中CPU可以進行其他的任務。這也是異步IO、非阻塞IO等理論的基礎。

計算機常考題:

圖1-2-1 題目1

圖1-2-2 題目2

2 操作系統內存管理與分類

2.1 虛擬內存(邏輯內存)

win32程序從程序上能操作的邏輯地址空間有4G這么大(雖然實際可能用不了那么多),4G的邏輯地址需要全部映射到物理內存上。映射的最小單位如果是字節的話,映射表將會非常大,且效率低下。提出page概念,即最小的映射單位是一個page,一頁一般是4K這樣的大小,我的機器是這樣的,所以下面程序demo中頁大小都是4K。

顯然邏輯空間可能比實際要大,但是只要程序沒有用那么多內存,就不需要去映射那么多page,且就算用了那么多內存,也可以映射到磁盤上。

邏輯頁是抽象的,需要映射到物理的頁上,才能完成對內存的操作。我們把邏輯頁叫頁(page)物理頁叫幀(page frame)。頁號-幀號的映射表叫頁表(page table)。

圖2-1 頁表映射

因為每個程序看到的邏輯地址空間都很大,所以程序變多了之后,程序使用的內存大于了物理內存,此時一般通過將部分"不著急使用"的頁映射到磁盤的方式來解決。所以頁表中映射項可能是磁盤。

圖2-2 頁表映射

同時每個進程都有自己的專屬頁表,如下:

圖2-3 多進程的頁表

一種實際情況,4G邏輯地址有32bit地址空間,假設pageSize=4K偏移量占12bit,因而頁表的邏輯頁號有20bit。再假設實際內存條只有256M 28bit地址空間 12bit偏移量 16bit頁號。

邏輯地址0x 00001 1a3,去映射的時候00001就是邏輯頁號,去查頁表發現映射到真實頁幀號00f3,然后偏移量不變還是1a3,最終就找到這個物理內存內容了。

圖2-4 頁表的映射過程

這個過程中,可能會出現映射的幀號是disk,即映射到了磁盤上。此時會觸發缺頁異常,進入內核態,內核從磁盤中讀取缺的這頁內容,將其加載到物理內存中。但是物理內存的幀有可能所有幀都滿了,此時就需要逐出不太"重要"的幀。

逐出的過程需要判斷當前物理頁(幀)是否是臟的(臟:與磁盤中內容不一致,即從磁盤加載到物理內存后被改過就是臟的),如果是臟的還需要更新磁盤中的內容保證一致。

逐出后就騰出了位置給從磁盤中讀到的這頁的數據,然后需要更新頁表的這一項的映射關系,將磁盤改為幀號,然后重新進行查頁表這一步。

邏輯層的作用:極大的降低了內存碎片;借助磁盤可以實現"無限的內存";各個進程間內存的安全性等。

一個地址中“住”的是一字節(8bit)的數據。

2.2 快表TLB、多級頁表

上面提到了邏輯-物理頁的映射,這就是頁表,但是上面的頁表其實除了簡單的頁號映射,還存儲了其他一些屬性:是否有效,讀寫權限,修改位,訪問位(淘汰算法和TLB中用),是否是臟(被修改過就是臟的,因為他和硬盤上的數據不一致),是否允許被高速緩存等等。

頁表存于主存中,每個進程都有自己的頁表。

上面可以看到基于頁表的尋址,需要兩次訪問主存(頁表是存在主存的),效率低下。為了提高速度,引入了快表,快表是頁表項的緩存,將最近一次的映射項存入快表,因為空間有限所以需要逐出最老的那一項。快表的設計是基于經驗:程序經常訪問的page一般就那幾個,不會經常頻繁的更換特別多的頁。

快表可能存于硬件MMU中(也可能是軟件TLB),一般只有8-256條,每個進程都有自己的快表。

另一個值得討論的話題是頁表占用空間太大,上面例子中(32位程序256M機器pageSize4K)頁號有20bit即2百萬個,所以需要有1百萬條,每條大小如果只算邏輯頁號(20bit)和物理頁號(16bit)的話:

36bit * 2^20 = 4.5MB

如果有64個這樣的程序在運行...后果可想而知。

一種很好的解決方法是多級頁表,第一級頁表用于尋找第二級頁表的編號。<20bit-16bit>的單級映射可以改成<10bit-10bit>和<10bit-6bit>兩級映射。此時占用內存為

20bit * 2^10 + 16bit * 2^20 = 2M

2.3 分段

嚴格意義的分段是,每一段的虛擬地址都是從0開始。然后頁表是段號+頁號來映射幀號的。但是這種形式已經被廢棄了,只有x86 32位的intel的cpu還保留了這種段頁結合的方式,即嚴格意義的分段已經用的很少。

那為什么還經常聽到段的概念?現在所說的段一般是程序在邏輯層面保留的概念,對邏輯地址有個粗略的劃分,便于程序編寫,但是并不影響os的內存管理(還是分頁管理)。

以32位程序為例,在邏輯空間中最高的0xc0000000 - 0xffffffff這1G的內存是給內核留出的,這部分是所有進程共享的。剩余3G內存從低到高分別是Text、Data、Heap、Lib、Stack。64位程序則遠大于這里的值。

Heap是從低往高增長,Stack是從高往低增長,且有個最大限制。Data存儲靜態變量Text存儲程序二進制碼,Lib存儲庫函數需要占用的內存,多個程序如果都使用了相同的庫,內存是共用的(共享內存)。各個部分的留有隨機的一段偏移量,可以保護程序,這也使得每次重新執行程序的時候變量所在的內存地址總是不同的。

圖2-5 32位系統下內存地址的組成

分段是邏輯空間上的,不影響分頁的內存管理方式,后面進行分頁,映射到物理內存上各部分跨多個頁其實并不連續。

2.4 cache

cpu的三級緩存扮演著緩存主存數據的作用,而cache在內存管理中的位置是怎樣的呢?

PIPT,物理級cache,cpu分析完映射關系,先到cache找有沒有該物理地址的cache。這樣會非常的慢,但是所有進程可以共享cache。

VIVT,邏輯級cache,cpu直接通過邏輯地址找cache,miss后再查TLB頁表這些。這樣很快,但是邏輯地址只能對當期進程使用,其他進程完全不能復用,尤其是庫函數這種共享的不能利用好cache。

VIPT,將兩者結合,用邏輯地址查找cache,cache中數據部分前面添加一個對應物理地址的tag。這樣拿到這個tag后到tlb、頁表中查看下這個對應關系是否正確,如果正確就直接讀cache。這樣速度和共享性都是折中的。

以上三種方式各有優劣,在不同的cpu中可能使用的不一樣。

2.5 內存地址大小

很多人想當然的會認為32位系統的虛擬地址是32位,這是沒錯的,但是64位系統下真正的可用的虛擬地址卻不到64位。

#include int main(){int x = 10;printf("%p",&x)}

圖2-6 C語言打印地址

明顯看到是48位,雖然這個指針大小是8byte,但是只有48bit是有效的地址位,前面是多個0。通過cat /proc/cpuinfo最后幾行能看到物理地址和虛擬地址的大小,這主要是cpu單方面定制的,我的這臺機器是13年買的intel 酷睿i5 3230的CPU。當然我的系統內存只有2G,其實物理地址不會有43位,只是cpu最多支持43位物理地址。

圖2-7 cpuinfo中的虛擬地址和物理地址

小細節:棧是僅次于內核的高位地址,參考圖2-5. 所以看到前面這個地址基本能推算出分給內核的虛擬空間應該是0xffff ffff ffff - 0x8000 0000 0000。

2.6 內存分類

在生活中我們經常看到各種內存的種類,比如在linux調用free -h的時候可以看到圖2-6的分類。

在linux中通過free -h可以看到當前系統的內存情況:

圖2-8 free指令下的內存分類

mem是物理內存,swap是交換分區,是用來將內存暫時放到磁盤上的。

total總內存大小,used用戶使用的內存大小,free空閑的內存大小,shared共享內存大小,buff/cache文件緩存大小,available可用內存大小是free和buff/cache加起來。

total = used(含shared) + free+ buff/cache

這里需要理解buff/cache,他們在老一些的內核中是分開顯示的分別是buffer cache和page cache,都是對磁盤的緩存。其中buffer cache是硬件層面,對磁盤塊中的數據進行緩存,緩存的單位當然也是塊。而page cache是文件系統層面,對文件進行緩存,緩存單位就是頁。buffer cache的提出非常的早,兩者并存時會遇到重復緩存了相同的內容的情況。

較新的內核已經將兩者合并,或者說將buffer cache合到了page cache。雖然也還是能緩存磁盤塊,但是存儲單位也是頁了。并且buffer使用前會先檢查page cahce是否已經緩存了對應內容,如果是則直接指過去。在機器維度查看內存的時候也能發現BufferCache都是0,因為都合到了pageCache,有Buffer的都是很老的內核的機器。

buff/cache占用大,會不會影響后續程序申請內存?

不會,一旦用戶程序需要申請內存,buff/cache就會釋放掉一部分。換句話說buff/cache是在內存比較空閑的時候,盡量利用一下來加速文件讀寫的。如果有大哥需要用內存,是會拱手讓出的。

如果想進一步了解兩者的演化,這篇文章從內核源碼的角度展示了,幾個理成本版本下buff cache 和 page cache的變化。

在windows任務管理器中又可以看到下圖的幾種狀態的內存叫法,而在Jprofile查看jvm內存的時候也有圖2-8的一些叫法。

圖2-9 windows任務管理器內存分類

圖2-10 jprofile內存分類

已提交的意思是已經向操作系統申請了這么多的內存,操作系統可以已經給了這么多內存了,但是也可能沒有給那么多。貼一張微軟自己的解釋如圖

圖2-11 幾種內存的解釋

提交的內存因為是虛擬內存,并不一定系統會立刻給這么多,所以可能提交遠超過物理內存上限的大小。我之前看過一個視頻,小哥用malloc申請了130000+GB的內存程序才退出,而如果在malloc后給申請的地址填寫值,事情就不那么順利了。感興趣可以去看下這個視頻。當然不了解C語言也沒關系我在本文后半段會用java的Unsafe同樣申請超過物理上限的內存大小做demo。

3 內存相關的系統調用

3.1 內核態和用戶態

內核態、用戶態、內核空間、用戶空間,是經常說起的概念。因為操作系統不允許用戶直接操作硬件,所以需要用戶程序通知內核,內核幫你下達指令給硬件。在進行讀文件的時候,就需要用到磁盤這個設備,所以需要進入內核態,將文件內容讀到內核buffer,然后拷貝到用戶buffer并從內核態切換為用戶態,程序才能真正拿到數據。

用戶態進內核態,一般有三種觸發條件,中斷、異常和系統調用,中斷和異常有時候界限比較模糊,例如缺頁中斷也有地方叫缺頁異常。這里我們引出了系統調用,大多數需要主動操作或讀寫硬件的都是通過系統調用。例如讀寫文件的open/read/write是系統調用,網絡傳輸常見的select/poll/epoll也是系統調用,申請內存的malloc底層也是通過brk或mmap這倆系統調用實現的。

系統調用伴隨了很多設計的優化,例如通過epoll等系統調用實現的IO多路復用提高了網絡包的處理效率,mmap、sendfile等系統調用實現的零拷貝,減少了用戶空間和內核空間之間的數據拷貝和上下文切換次數等等。在java的NIO中有大量的函數是直接封裝了系統調用。

3.2 brk

malloc小于128K(閾值可修改)的內存時,用的是brk申請內存。C語言中sbrk(可函數)是brk(系統調用)的簡單封裝,下面代碼打印的值可以看出first因為申請了0大小,所以和second指針位置相同。而third則表示的是second的尾部地址。可以看到虛擬地址是連續分配的,brk其實就是向上擴展heap的上界,配合查看圖2-5。

#include #include int main(){void *first = sbrk(0);void *second = sbrk(1);void *third = sbrk(0);printf("%p\n",first);printf("%p\n",second);printf("%p\n",third);}

圖3-1 brk代碼輸出

如果此時在 third+1地址處去初始化一個int值,是可以成功的,并不報錯。

#include #include int main(){void *first = sbrk(0);void *second = sbrk(1);void *third = sbrk(0);int *p = (int *)third+1;*p = 1;}

這是因為頁大小是4K,sbrk(1)其實也是申請一頁,所以third+1位置也是安全的。如果我們將second這行改為4096,那就是另一個故事了,會觸發段錯誤。

void *second = sbrk(4096);

圖3-2 brk代碼輸出2

3.3 mmap

malloc大于128K的內存時,用的是mmap。

// addr傳NULL則不關心起始地址,關心地址的話應傳個4k的倍數,不然也會歸到4k倍數的起始地址。void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);//釋放內存munmapint munmap(void *addr, size_t length);

mmap用法有兩種,一種是將文件映射到內存,另一種空文件映射,也就是把fd傳入-1,就會從映射區申請到一塊內存。malloc就是調用的第二種實現。

#include #include #include int main(){int* a =(int *) mmap(NULL, 100 * 4096, PROT_READ| PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS, -1, 0);int* b =a;for(int i=0;i<100;i++){b = (void *)a + (i*4096);*b =1;}while(1){sleep(1);}}

這里提交400K內存的申請,并且在每頁中都進行內存的使用。可以看到不映射文件的話觸發的是minflt次數是100次。

圖3-3 進程的內存minflt

這里是mmap內存的惰性加載,一開始mmap100頁時其實都沒有分配給進程,在用到的時候開始真正拿到內存,此時觸發minflt缺頁,因為不是映射的文件,不用從磁盤中調內存,所以是小錯誤。但是仍是消耗性能的。

如果mmap是映射的磁盤文件,也會惰性加載,在初次加載或者頁被逐出后再加載的時候,也會缺頁,這個時候就不是小錯誤minflt了,而是majflt。例如下面使用mmap來讀文件。

#include #include #include #include #include #include int main(){sleep(4);int fd = open("./1.txt", O_RDONLY, S_IRUSR|S_IWUSR);struct stat sb;if(fstat(fd, &sb) == -1){perror("cannot get file size\n");}printf("file size is %ld\n",sb.st_size);char *file_in_memory = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);for(int i=0;i

下圖是線程監聽的結果,為了方便觀察我在開始讀之前sleep 4s。可以看到紅框第一行,有一次majflt,這是第一次去讀文件,直接觸發了缺頁異常,且指向磁盤。是最耗時的錯誤。

圖3-3-2 進程mmap讀文件引發majflt

read和mmap都可以讀文件,前者有狀態轉換和多次拷貝,但是后者有缺頁中斷。在單純讀磁盤文件場景,兩者其實沒法在孰優孰劣上有定論。

3.4 共享內存

共享內存是進程間通信的一種方式,(管道 信號 信號量 套接字也是進程通信的方式)。共享內存的例子比比皆是,windows下最明顯,比如這個上傳文件的對話框就是共享內存里的,同一時間windows下不會彈出兩個該對話框。再比如動態鏈接庫,也是共享內存中的,多個進程可以共享,兩個進程mmap相同文件的方式可以實現共享內存,shmget則是更廣泛的共享內存的系統調用。

圖3-4 共享內存的典型例子

共享內存原理就是兩個進程中頁,映射到了相同的幀。代碼這里不寫了,直接參考geeks這篇的代碼。

4 java中的內存

4.1 java內存概述

jvm內存結構主要如圖4-1.本文不想對“常考”的知識點再次進行講解,網上有大量的文章來講內存結構各自的用途和GC相關的內容,這里我就不展開講了。下面幾節會講一些比較"冷門"的知識。

圖4-1 java的內存五區

4.2 對象頭與指針壓縮

在另一篇講計算java對象大小的文章中提到,java對象是由對象頭,對象內容組成,并且是8字節對齊的。其中對象頭有以下三部分組成:

  • Mark Word(64bits) 當前對象一些運行時數據如鎖
  • Klass Word(開壓縮32bits,不開64bits) 類型指針,指向類元數據Klass地址
  • array length(32bits) 數組對象才有

我們這里來看下Klass,有沒有想過我們反射的時候操作的都是Class對象而不是這里的Klass,兩者關系是:

Klass是C++對象InstanceKlass,里面有個_java_mirror字段指向對應的Class對象。

圖4-2 java對象頭指向metaspace

這里還提到了指針壓縮,64位系統,如果jvm堆內存小于32GB是可以開啟指針壓縮的,此時Klass指針只需要4個字節,同時對象指針也只需要4個字節。這里會衍生出兩個問題:

第一個就是4字節最多表示2^32個地址,每個地址里住的是一個字節,所以只能表示4GB,怎么還說32G下都能壓縮呢?

因為:上面提到對象都是8字節對齊,所以每個地址里住的是8字節,所以可以表示32GB,實際地址移3位。

第二個問題就是普通對象指針壓縮Compressed Object Pointers (“CompressedOops”),壓縮的是java堆上的對象的指針(引用)大小,而對象頭指向的是Klass,這是個C++的結構,這個指針也壓縮了嗎?

是的,CompressOops和CompressKlass是相伴而生,默認同時開啟的,Klass這部分需要連續的<4G的內存,因為是C++結構,沒有8字節對齊限制,所以4字節只能在4G內存上尋址,默認大小是1G。

4.3 metaspace

metaspace存儲的是類的元數據信息,上面提到的Klass就是在metaspace中的,一般開啟壓縮的metaspace有CompressClassSpace和NonClassSpace兩部分組成,其中前者內存占用較少,是后者的5-100分之一,前者又叫壓縮類空間,實際上這部分內存本身并沒有壓縮,只是對象頭中記錄的指向這里的指針進行了壓縮。

圖4-3 metaspace兩部分:非類區和壓縮類空間

壓縮類空間中Klass是c++的對象有著很多元數據字段,vtable是記錄虛方法指針,itable是接口方法指針。Non-class中則記錄了更詳細的元數據信息。開啟指針壓縮后,如果設置MaxMetaspaceSize參數實際上是限定的Non-class部分的大小,而不包括壓縮類空間。通過Jprofile中也能發現Metaspace只包括Non-class部分,那為什么我上來說Metaspace有兩部分呢,主要是從概念上講兩者都是元數據,在國外很多文章中也都歸為了Metaspace。這里只需要注意這個小細節就可以了。設置MaxMetaspaceSize參數也可以對壓縮類空間起到間接的限制,因為前面說了Non-class部分是class部分的n倍。

圖4-4 指針壓縮開啟時 非堆

將壓縮類空間和非類空間分開的原因之一,就是壓縮類空間是對象關聯的,只有4G上限,而將更多其他元數據剝離出去后,元空間可以遠超過4G。而如果不開啟指針壓縮,其實兩者就沒必要分開了。關閉指針壓縮后,-XX:-UseCompressedOops 兩部分會合為一個。統稱Metaspace

圖4-5 指針壓縮關閉時非堆

Q1:元空間內存什么時候分配?

一個新的類在需要被加載的時候,會使用ClassLoader在元空間申請內存,并存儲類的元數據信息。

Q2:元空間什么時候釋放內存?

元空間的內存是ClassLoader持有的,所以說只有對應的ClassLoader卸載掉的時候才會釋放。ClassLoader又是需要他所加載的類都消失的時候才能消失。一般是伴隨在一次GC的過程中進行這個釋放。另外元空間如果超過了上限也會導致OOM。

Q3:metaspace溢出會不會導致OOM?

當然會導致OOM,所以metaspace限制大小的配置,需要根據程序謹慎定制。一般通過不斷創建新的類,如加載新類(如hsf配置中下發groovy文件就會動態的加載新的class),或者動態代理類(spring中的增強類都是動態代理類)都會導致metaspace的增長。

cglibcglib3.2.4
//設置metaspace大小:-XX:MaxMetaspaceSize=200mpublic class T {public static void main(String[] args) {while (true) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Object.class);enhancer.setUseCache(false);enhancer.setCallback((FixedValue)()->":)");enhancer.create();}}}

監視會發現壓縮類空間和非類空間都在增大,后者在200M上有道紅線,在2分鐘左右溢出,程序掛掉,這個程序中壓縮類空間大概是分類的六分之一。

圖4-5a 壓縮類空間

圖4-5b 非類空間

4.4 堆外內存

上面的CodeCache和Metaspace毫無疑問是jvm管理下的堆外空間。但是除了這些常規的堆外空間,jvm還可以使用一些native方法,直接申請堆外內存。

例如做這么個demo,我們設置一個簡單的java程序的堆大小是10M,此時用jprofile查看內存堆提交了10M實際使用9M多,堆外提交了12M實際使用11M左右。所以算下來是20M+。直接查看進程內存會略大于這個值,因為這個20M是虛擬機內部的內存,本身運行還是需要一些額外內存的,進程提交的內存有90M,實際使用內存47M

圖4-6 進程的提交內存和實際內存

接下來我們使用Unsafe申請1G堆外內存(也可以用NIO中的ByteBuffer.allocateDirect())

public static void main(String[] args) throws InterruptedException, IllegalAccessException, NoSuchFieldException {Field f = Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(true);Unsafe us = (Unsafe) f.get(null);long addr = us.allocateMemory(1024 * 1024 * 1024);System.out.println("Hello World");System.out.println(addr);while(true){Thread.sleep(1000L);}}

可以看到提交的內存1G多,實際使用內存也是47M。

圖4-7 進程的提交內存和實際內存2

我甚至可以調整申請65G的內存,要知道我的電腦也只有64G的內存,但這仍不會報錯,可以看到提交的內存已經超過了物理內存上限,但是得益于前面講的虛擬內存的管理模式,使得應用申請了超過物理大小的內存,而如果真的使用起來的話,會有頁置換來協調。

圖4-8 進程可以提交超過現實存在的內存

上面的提交內存很大但是實際使用內存卻并不大:

圖4-9 任務管理器此時的狀態

Unsafe是很危險的一個類,不建議使用。但是可以幫助我們理解有些框架是如何工作的。比如前一陣子看的Ehcache就提供了堆外緩存就是用類似Unsafe申請的。堆外緩存需要自己實現序列化,因為Unsafe設置內存只能設置01字節碼不能設置為java對象。

堆外緩存的好處:緩存一般是短時間不需要清理的,如果在堆上則肯定會進入老年代,占用固定的一大塊空間,使得觸發full GC的門檻降低了,很容易到了那個門限值。而且GC過程中還要去遍歷這些對象,效率較低。

堆外內存的壞處:序列化需要自己實現,清理也需要自己實現,訪問速度比heap要慢。

???展開全文
相關文章
主站蜘蛛池模板: 国产成人av一区二区三区在线观看 | 久久99精品久久 | 亚洲国产精品国自产拍av秋霞 | 免费在线观看av | 成人福利在线视频 | 中文字幕日产av一二三区 | 在线观看国产黄色 | 九色av| 日韩av手机在线观看 | 三级网站免费观看 | 桃色伊人 | 日韩欧美一二三 | 成人自拍一区 | 欧美日韩高清一区 | 久久久久久一区二区三区四区别墅 | 国产网站在线免费观看 | 精品一区久久久 | 久久国产精品久久久久久久久久 | 欧美日本综合 | 国产一二区在线观看 | 久久1区| 国产精品久久久亚洲 | 久久亚洲成人 | 欧美13videosex性极品 | 国产一区二区美女 | 一级毛片在线看 | 能看的av| 亚洲精品成人在线 | 久久久久国产一区二区三区四区 | 久久精品久久久精品美女 | 日韩不卡在线视频 | 亚洲va国产2019 | 日本视频在线免费观看 | 欧美三级欧美成人高清www | 亚洲成人精品一区二区 | 亚洲成人va | 成人免费观看网站 | 国产伦精品一区 | 人成福利视频在线观看 | 欧美怡红院视频一区二区三区 | 婷婷99狠狠躁天天躁中文字幕 |