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

JVM內(nèi)存管理深入垃圾收集器與內(nèi)存分配策略

開發(fā) 后端
Java與C++之間有一堵由內(nèi)存動態(tài)分配和垃圾收集技術(shù)所圍成的高墻,墻外面的人想進(jìn)去,墻里面的人卻想出來。

Java與C++之間有一堵由內(nèi)存動態(tài)分配和垃圾收集技術(shù)所圍成的高墻,墻外面的人想進(jìn)去,墻里面的人卻想出來。

概述:

說起垃圾收集(Garbage Collection,下文簡稱GC),大部分人都把這項(xiàng)技術(shù)當(dāng)做Java語言的伴生產(chǎn)物。事實(shí)上GC的歷史遠(yuǎn)遠(yuǎn)比Java來得久遠(yuǎn),在1960年誕生于MIT的Lisp是***門真正使用內(nèi)存動態(tài)分配和垃圾收集技術(shù)的語言。當(dāng)Lisp還在胚胎時(shí)期,人們就在思考GC需要完成的3件事情:哪些內(nèi)存需要回收?什么時(shí)候回收?怎么樣回收?

經(jīng)過半個(gè)世紀(jì)的發(fā)展,目前的內(nèi)存分配策略與垃圾回收技術(shù)已經(jīng)相當(dāng)成熟,一切看起來都進(jìn)入“自動化”的時(shí)代,那為什么我們還要去了解GC和內(nèi)存分配?答案很簡單:當(dāng)需要排查各種內(nèi)存溢出、泄漏問題時(shí),當(dāng)垃圾收集成為系統(tǒng)達(dá)到更高并發(fā)量的瓶頸時(shí),我們就需要對這些“自動化”的技術(shù)有必要的監(jiān)控、調(diào)節(jié)手段。

把時(shí)間從1960年撥回現(xiàn)在,回到我們熟悉的Java語言。本文***章中介紹了Java內(nèi)存運(yùn)行時(shí)區(qū)域的各個(gè)部分,其中程序計(jì)數(shù)器、VM棧、本地方法棧三個(gè)區(qū)域隨線程而生,隨線程而滅;棧中的幀隨著方法進(jìn)入、退出而有條不紊的進(jìn)行著出棧入棧操作;每一個(gè)幀中分配多少內(nèi)存基本上是在Class文件生成時(shí)就已知的(可能會由JIT動態(tài)晚期編譯進(jìn)行一些優(yōu)化,但大體上可以認(rèn)為是編譯期可知的),因此這幾個(gè)區(qū)域的內(nèi)存分配和回收具備很高的確定性,因此在這幾個(gè)區(qū)域不需要過多考慮回收的問題。而Java堆和方法區(qū)(包括運(yùn)行時(shí)常量池)則不一樣,我們必須等到程序?qū)嶋H運(yùn)行期間才能知道會創(chuàng)建哪些對象,這部分內(nèi)存的分配和回收都是動態(tài)的,我們本文后續(xù)討論中的“內(nèi)存”分配與回收僅僅指這一部分內(nèi)存。

對象已死?

在堆里面存放著Java世界中幾乎所有的對象,在回收前首先要確定這些對象之中哪些還在存活,哪些已經(jīng)“死去”了,即不可能再被任何途徑使用的對象。

引用計(jì)數(shù)算法(Reference Counting)

最初的想法,也是很多教科書判斷對象是否存活的算法是這樣的:給對象中添加一個(gè)引用計(jì)數(shù)器,當(dāng)有一個(gè)地方引用它,計(jì)數(shù)器加1,當(dāng)引用失效,計(jì)數(shù)器減1,任何時(shí)刻計(jì)數(shù)器為0的對象就是不可能再被使用的。

客觀的說,引用計(jì)數(shù)算法實(shí)現(xiàn)簡單,判定效率很高,在大部分情況下它都是一個(gè)不錯(cuò)的算法,但引用計(jì)數(shù)算法無法解決對象循環(huán)引用的問題。舉個(gè)簡單的例子:對象A和B分別有字段b、a,令A(yù).b=B和B.a=A,除此之外這2個(gè)對象再無任何引用,那實(shí)際上這2個(gè)對象已經(jīng)不可能再被訪問,但是引用計(jì)數(shù)算法卻無法回收他們。

根搜索算法(GC Roots Tracing)

在實(shí)際生產(chǎn)的語言中(Java、C#、甚至包括前面提到的Lisp),都是使用根搜索算法判定對象是否存活。算法基本思路就是通過一系列的稱為“GC Roots”的點(diǎn)作為起始進(jìn)行向下搜索,當(dāng)一個(gè)對象到GC Roots沒有任何引用鏈(Reference Chain)相連,則證明此對象是不可用的。在Java語言中,GC Roots包括:

  1.在VM棧(幀中的本地變量)中的引用

  2.方法區(qū)中的靜態(tài)引用

  3.JNI(即一般說的Native方法)中的引用

生存還是死亡?

判定一個(gè)對象死亡,至少經(jīng)歷兩次標(biāo)記過程:如果對象在進(jìn)行根搜索后,發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈,那它將會被***次標(biāo)記,并在稍后執(zhí)行他的finalize()方法(如果它有的話)。這里所謂的“執(zhí)行”是指虛擬機(jī)會觸發(fā)這個(gè)方法,但并不承諾會等待它運(yùn)行結(jié)束。這點(diǎn)是必須的,否則一個(gè)對象在finalize()方法執(zhí)行緩慢,甚至有死循環(huán)什么的將會很容易導(dǎo)致整個(gè)系統(tǒng)崩潰。finalize()方法是對象***一次逃脫死亡命運(yùn)的機(jī)會,稍后GC將進(jìn)行第二次規(guī)模稍小的標(biāo)記,如果在finalize()中對象成功拯救自己(只要重新建立到GC Roots的連接即可,譬如把自己賦值到某個(gè)引用上),那在第二次標(biāo)記時(shí)它將被移除出“即將回收”的集合,如果對象這時(shí)候還沒有逃脫,那基本上它就真的離死不遠(yuǎn)了。

需要特別說明的是,這里對finalize()方法的描述可能帶點(diǎn)悲情的藝術(shù)加工,并不代表筆者鼓勵(lì)大家去使用這個(gè)方法來拯救對象。相反,筆者建議大家盡量避免使用它,這個(gè)不是C/C++里面的析構(gòu)函數(shù),它運(yùn)行代價(jià)高昂,不確定性大,無法保證各個(gè)對象的調(diào)用順序。需要關(guān)閉外部資源之類的事情,基本上它能做的使用try-finally可以做的更好。

關(guān)于方法區(qū)

方法區(qū)即后文提到的***代,很多人認(rèn)為***代是沒有GC的,《Java虛擬機(jī)規(guī)范》中確實(shí)說過可以不要求虛擬機(jī)在這區(qū)實(shí)現(xiàn)GC,而且這區(qū)GC的“性價(jià)比”一般比較低:在堆中,尤其是在新生代,常規(guī)應(yīng)用進(jìn)行一次GC可以一般可以回收70%~95%的空間,而***代的GC效率遠(yuǎn)小于此。雖然VM Spec不要求,但當(dāng)前生產(chǎn)中的商業(yè)JVM都有實(shí)現(xiàn)***代的GC,主要回收兩部分內(nèi)容:廢棄常量與無用類。這兩點(diǎn)回收思想與Java堆中的對象回收很類似,都是搜索是否存在引用,常量的相對很簡單,與對象類似的判定即可。而類的回收則比較苛刻,需要滿足下面3個(gè)條件:

  1.該類所有的實(shí)例都已經(jīng)被GC,也就是JVM中不存在該Class的任何實(shí)例。

  2.加載該類的ClassLoader已經(jīng)被GC。

  3.該類對應(yīng)的java.lang.Class 對象沒有在任何地方被引用,如不能在任何地方通過反射訪問該類的方法。

是否對類進(jìn)行回收可使用-XX:+ClassUnloading參數(shù)進(jìn)行控制,還可以使用-verbose:class或者-XX:+TraceClassLoading、-XX:+TraceClassUnLoading查看類加載、卸載信息。

在大量使用反射、動態(tài)代理、CGLib等bytecode框架、動態(tài)生成JSP以及OSGi這類頻繁自定義ClassLoader的場景都需要JVM具備類卸載的支持以保證***代不會溢出。

垃圾收集算法

在這節(jié)里不打算大量討論算法實(shí)現(xiàn),只是簡單的介紹一下基本思想以及發(fā)展過程。最基礎(chǔ)的搜集算法是“標(biāo)記-清除算法”(Mark-Sweep),如它的名字一樣,算法分層“標(biāo)記”和“清除”兩個(gè)階段,首先標(biāo)記出所有需要回收的對象,然后回收所有需要回收的對象,整個(gè)過程其實(shí)前一節(jié)講對象標(biāo)記判定的時(shí)候已經(jīng)基本介紹完了。說它是最基礎(chǔ)的收集算法原因是后續(xù)的收集算法都是基于這種思路并優(yōu)化其缺點(diǎn)得到的。它的主要缺點(diǎn)有兩個(gè),一是效率問題,標(biāo)記和清理兩個(gè)過程效率都不高,二是空間問題,標(biāo)記清理之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導(dǎo)致后續(xù)使用中無法找到足夠的連續(xù)內(nèi)存而提前觸發(fā)另一次的垃圾搜集動作。

為了解決效率問題,一種稱為“復(fù)制”(Copying)的搜集算法出現(xiàn),它將可用內(nèi)存劃分為兩塊,每次只使用其中的一塊,當(dāng)半?yún)^(qū)內(nèi)存用完了,僅將還存活的對象復(fù)制到另外一塊上面,然后就把原來整塊內(nèi)存空間一次過清理掉。這樣使得每次內(nèi)存回收都是對整個(gè)半?yún)^(qū)的回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動堆頂指針,按順序分配內(nèi)存就可以了,實(shí)現(xiàn)簡單,運(yùn)行高效。只是這種算法的代價(jià)是將內(nèi)存縮小為原來的一半,未免太高了一點(diǎn)。

現(xiàn)在的商業(yè)虛擬機(jī)中都是用了這一種收集算法來回收新生代,IBM有專門研究表明新生代中的對象98%是朝生夕死的,所以并不需要按照1:1的比例來劃分內(nèi)存空間,而是將內(nèi)存分為一塊較大的eden空間和2塊較少的survivor空間,每次使用eden和其中一塊survivor,當(dāng)回收時(shí)將eden和survivor還存活的對象一次過拷貝到另外一塊survivor空間上,然后清理掉eden和用過的survivor。Sun Hotspot虛擬機(jī)默認(rèn)eden和survivor的大小比例是8:1,也就是每次只有10%的內(nèi)存是“浪費(fèi)”的。當(dāng)然,98%的對象可回收只是一般場景下的數(shù)據(jù),我們沒有辦法保證每次回收都只有10%以內(nèi)的對象存活,當(dāng)survivor空間不夠用時(shí),需要依賴其他內(nèi)存(譬如老年代)進(jìn)行分配擔(dān)保(Handle Promotion)。

復(fù)制收集算法在對象存活率高的時(shí)候,效率有所下降。更關(guān)鍵的是,如果不想浪費(fèi)50%的空間,就需要有額外的空間進(jìn)行分配擔(dān)保用于應(yīng)付半?yún)^(qū)內(nèi)存中所有對象都100%存活的極端情況,所以在老年代一般不能直接選用這種算法。因此人們提出另外一種“標(biāo)記-整理”(Mark-Compact)算法,標(biāo)記過程仍然一樣,但后續(xù)步驟不是進(jìn)行直接清理,而是令所有存活的對象一端移動,然后直接清理掉這端邊界以外的內(nèi)存。

當(dāng)前商業(yè)虛擬機(jī)的垃圾收集都是采用“分代收集”(Generational Collecting)算法,這種算法并沒有什么新的思想出現(xiàn),只是根據(jù)對象不同的存活周期將內(nèi)存劃分為幾塊。一般是把Java堆分作新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ǎ┤缧律看蜧C都有大批對象死去,只有少量存活,那就選用復(fù)制算法只需要付出少量存活對象的復(fù)制成本就可以完成收集。

垃圾收集器

垃圾收集器就是收集算法的具體實(shí)現(xiàn),不同的虛擬機(jī)會提供不同的垃圾收集器。并且提供參數(shù)供用戶根據(jù)自己的應(yīng)用特點(diǎn)和要求組合各個(gè)年代所使用的收集器。本文討論的收集器基于Sun Hotspot虛擬機(jī)1.6版。

圖1.Sun JVM1.6的垃圾收集器

圖1展示了1.6中提供的6種作用于不同年代的收集器,兩個(gè)收集器之間存在連線的話就說明它們可以搭配使用。在介紹著些收集器之前,我們先明確一個(gè)觀點(diǎn):沒有***的收集器,也沒有***的收集器,只有最合適的收集器。

1.Serial收集器

單線程收集器,收集時(shí)會暫停所有工作線程(我們將這件事情稱之為Stop The World,下稱STW),使用復(fù)制收集算法,虛擬機(jī)運(yùn)行在Client模式時(shí)的默認(rèn)新生代收集器。

2.ParNew收集器

ParNew收集器就是Serial的多線程版本,除了使用多條收集線程外,其余行為包括算法、STW、對象分配規(guī)則、回收策略等都與Serial收集器一摸一樣。對應(yīng)的這種收集器是虛擬機(jī)運(yùn)行在Server模式的默認(rèn)新生代收集器,在單CPU的環(huán)境中,ParNew收集器并不會比Serial收集器有更好的效果。

3.Parallel Scavenge收集器

Parallel Scavenge收集器(下稱PS收集器)也是一個(gè)多線程收集器,也是使用復(fù)制算法,但它的對象分配規(guī)則與回收策略都與ParNew收集器有所不同,它是以吞吐量***化(即GC時(shí)間占總運(yùn)行時(shí)間最小)為目標(biāo)的收集器實(shí)現(xiàn),它允許較長時(shí)間的STW換取總吞吐量***化。

4.Serial Old收集器

Serial Old是單線程收集器,使用標(biāo)記-整理算法,是老年代的收集器,上面三種都是使用在新生代收集器。

5.Parallel Old收集器

老年代版本吞吐量優(yōu)先收集器,使用多線程和標(biāo)記-整理算法,JVM 1.6提供,在此之前,新生代使用了PS收集器的話,老年代除Serial Old外別無選擇,因?yàn)镻S無法與CMS收集器配合工作。

6.CMS(Concurrent Mark Sweep)收集器

CMS是一種以最短停頓時(shí)間為目標(biāo)的收集器,使用CMS并不能達(dá)到GC效率***(總體GC時(shí)間最小),但它能盡可能降低GC時(shí)服務(wù)的停頓時(shí)間,這一點(diǎn)對于實(shí)時(shí)或者高交互性應(yīng)用(譬如證券交易)來說至關(guān)重要,這類應(yīng)用對于長時(shí)間STW一般是不可容忍的。CMS收集器使用的是標(biāo)記-清除算法,也就是說它在運(yùn)行期間會產(chǎn)生空間碎片,所以虛擬機(jī)提供了參數(shù)開啟CMS收集結(jié)束后再進(jìn)行一次內(nèi)存壓縮。

內(nèi)存分配與回收策略

了解GC其中很重要一點(diǎn)就是了解JVM的內(nèi)存分配策略:即對象在哪里分配和對象什么時(shí)候回關(guān)于對象在哪里分配,往大方向講,主要就在堆上分配,但也可能經(jīng)過JIT進(jìn)行逃逸分析后進(jìn)行標(biāo)量替換拆散為原子類型在棧上分配,也可能分配在DirectMemory中(詳見本文***章)。往細(xì)節(jié)處講,對象主要分配在新生代eden上,也可能會直接老年代中,分配的細(xì)節(jié)決定于當(dāng)前使用的垃圾收集器類型與VM相關(guān)參數(shù)設(shè)置。我們可以通過下面代碼來驗(yàn)證一下Serial收集器(ParNew收集器的規(guī)則與之完全一致)的內(nèi)存分配和回收的策略。讀者看完Serial收集器的分析后,不妨自己根據(jù)JVM參數(shù)文檔寫一些程序去實(shí)踐一下其它幾種收集器的分配策略。

清單1:內(nèi)存分配測試代碼

  1. public class YoungGenGC {  
  2. private static final int _1MB = 1024 * 1024;  
  3. public static void main(String[] args) {   
  4. // testAllocation();   
  5. testHandlePromotion();   
  6. // testPretenureSizeThreshold();   
  7. // testTenuringThreshold();   
  8. // testTenuringThreshold2();   
  9. }  
  10. /**   
  11. * VM參數(shù):-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8  
  12. */   
  13. @SuppressWarnings("unused")   
  14. public static void testAllocation() {   
  15. byte[] allocation1, allocation2, allocation3, allocation4;   
  16. allocation1 = new byte[2 * _1MB];   
  17. allocation2 = new byte[2 * _1MB];   
  18. allocation3 = new byte[2 * _1MB];   
  19. allocation4 = new byte[4 * _1MB]; // 出現(xiàn)一次Minor GC   
  20. }  
  21. /**   
  22. * VM參數(shù):-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8  
  23. * -XX:PretenureSizeThreshold=3145728   
  24. */   
  25. @SuppressWarnings("unused")   
  26. public static void testPretenureSizeThreshold() {   
  27. byte[] allocation;   
  28. allocation = new byte[4 * _1MB]; //直接分配在老年代中   
  29. }  
  30. /**   
  31. * VM參數(shù):-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1  
  32. * -XX:+PrintTenuringDistribution   
  33. */   
  34. @SuppressWarnings("unused")   
  35. public static void testTenuringThreshold() {   
  36. byte[] allocation1, allocation2, allocation3;   
  37. allocation1 = new byte[_1MB / 4]; // 什么時(shí)候進(jìn)入老年代決定于XX:MaxTenuringThreshold設(shè)置   
  38. allocation2 = new byte[4 * _1MB];   
  39. allocation3 = new byte[4 * _1MB];   
  40. allocation3 = null;   
  41. allocation3 = new byte[4 * _1MB];   
  42. }  
  43. /**   
  44. * VM參數(shù):-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15  
  45. * -XX:+PrintTenuringDistribution   
  46. */   
  47. @SuppressWarnings("unused")   
  48. public static void testTenuringThreshold2() {   
  49. byte[] allocation1, allocation2, allocation3, allocation4;   
  50. allocation1 = new byte[_1MB / 4]; // allocation1+allocation2大于survivo空間一半   
  51. allocation2 = new byte[_1MB / 4];   
  52. allocation3 = new byte[4 * _1MB];   
  53. allocation4 = new byte[4 * _1MB];   
  54. allocation4 = null;   
  55. allocation4 = new byt  
  56. /**   
  57. * VM參數(shù):-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:-HandlePromotionFailure  
  58. */   
  59. @SuppressWarnings("unused")   
  60. public static void testHandlePromotion() {   
  61. byte[] allocation1, allocation2, allocation3, allocation4, allocation5, allocation6, allocation7;  
  62. allocation1 = new byte[2 * _1MB];   
  63. allocation2 = new byte[2 * _1MB];   
  64. allocation3 = new byte[2 * _1MB];   
  65. allocation1 = null;   
  66. allocation4 = new byte[2 * _1MB];   
  67. allocation5 = new byte[2 * _1MB];   
  68. allocation6 = new byte[2 * _1MB];   
  69. allocation4 = null;   
  70. allocation5 = null;   
  71. allocation6 = null;   
  72. allocation7 = new byte[2 * _1MB];   
  73. }   

規(guī)則一:通常情況下,對象在eden中分配。當(dāng)eden無法分配時(shí),觸發(fā)一次Minor GC。

執(zhí)行testAllocation()方法后輸出了GC日志以及內(nèi)存分配狀況。-Xms20M -Xmx20M -Xmn10M這3個(gè)參數(shù)確定了Java堆大小為20M,不可擴(kuò)展,其中10M分配給新生代,剩下的10M即為老年代。-XX:SurvivorRatio=8決定了新生代中eden與survivor的空間比例是1:8,從輸出的結(jié)果也清晰的看到“eden space 8192K、from space 1024K、to space 1024K”的信息,新生代總可用空間為9216K(eden+1個(gè)survivor)。

我們也注意到在執(zhí)行testAllocation()時(shí)出現(xiàn)了一次Minor GC,GC的結(jié)果是新生代6651K變?yōu)?48K,而總占用內(nèi)存則幾乎沒有減少(因?yàn)閹缀鯖]有可回收的對象)。這次GC是發(fā)生的原因是為allocation4分配內(nèi)存的時(shí)候,eden已經(jīng)被占用了6M,剩余空間已不足分配allocation4所需的4M內(nèi)存,因此發(fā)生Minor GC。GC期間虛擬機(jī)發(fā)現(xiàn)已有的3個(gè)2M大小的對象全部無法放入survivor空間(survivor空間只有1M大小),所以直接轉(zhuǎn)移到老年代去。GC后4M的allocation4對象分配在eden中。

 清單2:testAllocation()方法輸出結(jié)果

  1. [GC [DefNew: 6651K->148K(9216K), 0.0070106 secs] 6651K->6292K(19456K), 0.0070426 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]  
  2. Heap   
  3. def new generation total 9216K, used 4326K [0x029d0000, 0x033d0000, 0x033d0000)  
  4. eden space 8192K, 51% used [0x029d0000, 0x02de4828, 0x031d0000)   
  5. from space 1024K, 14% used [0x032d0000, 0x032f5370, 0x033d0000)   
  6. to space 1024K, 0% used [0x031d0000, 0x031d0000, 0x032d0000)   
  7. tenured generation total 10240K, used 6144K [0x033d0000, 0x03dd0000, 0x03dd0000)  
  8. the space 10240K, 60% used [0x033d0000, 0x039d0030, 0x039d0200, 0x03dd0000)  
  9. compacting perm gen total 12288K, used 2114K [0x03dd0000, 0x049d0000, 0x07dd0000)  
  10. the space 12288K, 17% used [0x03dd0000, 0x03fe0998, 0x03fe0a00, 0x049d0000)  
  11. No shared spaces configured.  

規(guī)則二:配置了PretenureSizeThreshold的情況下,對象大于設(shè)置值將直接在老年代分配。

執(zhí)行testPretenureSizeThreshold()方法后,我們看到eden空間幾乎沒有被使用,而老年代的10M控件被使用了40%,也就是4M的allocation對象直接就分配在老年代中,則是因?yàn)镻retenureSizeThreshold被設(shè)置為3M,因此超過3M的對象都會直接從老年代分配。

清單3:

  1. Heap   
  2. def new generation   total 9216K, used 671K [0x029d0000, 0x033d0000, 0x033d0000)  
  3.    eden space 8192K,   8% used [0x029d0000, 0x02a77e98, 0x031d0000)   
  4.   from space 1024K,   0% used [0x031d0000, 0x031d0000, 0x032d0000)   
  5.   to   space 1024K,   0% used [0x032d0000, 0x032d0000, 0x033d0000)   
  6. tenured generation   total 10240K, used 4096K [0x033d0000, 0x03dd0000, 0x03dd0000)  
  7.     the space 10240K,  40% used [0x033d0000, 0x037d0010, 0x037d0200, 0x03dd0000)  
  8.  compacting perm gen  total 12288K, used 2107K [0x03dd0000, 0x049d0000, 0x07dd0000)  
  9.     the space 12288K,  17% used [0x03dd0000, 0x03fdefd0, 0x03fdf000, 0x049d0000)  
  10.  No shared spaces configured.  

規(guī)則三:在eden經(jīng)過GC后存活,并且survivor能容納的對象,將移動到survivor空間內(nèi),如果對象在survivor中繼續(xù)熬過若干次回收(默認(rèn)為15次)將會被移動到老年代中。回收次數(shù)由MaxTenuringThreshold設(shè)置。

  分別以-XX:MaxTenuringThreshold=1和-XX:MaxTenuringThreshold=15兩種設(shè)置來執(zhí)行testTenuringThreshold(),方法中allocation1對象需要256K內(nèi)存,survivor空間可以容納。當(dāng)MaxTenuringThreshold=1時(shí),allocation1對象在第二次GC發(fā)生時(shí)進(jìn)入老年代,新生代已使用的內(nèi)存GC后非常干凈的變成0KB。而MaxTenuringThreshold=15時(shí),第二次GC發(fā)生后,allocation1對象則還留在新生代survivor空間,這時(shí)候新生代仍然有404KB被占用。

清單4:

  1. MaxTenuringThreshold=1 
  2. [GC [DefNew   
  3. Desired survivor size 524288 bytes, new threshold 1 (max 1)   
  4. - age 1: 414664 bytes, 414664 total   
  5. : 4859K->404K(9216K), 0.0065012 secs] 4859K->4500K(19456K), 0.0065283 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]  
  6. [GC [DefNew   
  7. Desired survivor size 524288 bytes, new threshold 1 (max 1)   
  8. : 4500K->0K(9216K), 0.0009253 secs] 8596K->4500K(19456K), 0.0009458 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]  
  9. Heap   
  10. def new generation total 9216K, used 4178K [0x029d0000, 0x033d0000, 0x033d0000)  
  11. eden space 8192K, 51% used [0x029d0000, 0x02de4828, 0x031d0000)   
  12. from space 1024K, 0% used [0x031d0000, 0x031d0000, 0x032d0000)   
  13. to space 1024K, 0% used [0x032d0000, 0x032d0000, 0x033d0000)   
  14. tenured generation total 10240K, used 4500K [0x033d0000, 0x03dd0000, 0x03dd0000)  
  15. the space 10240K, 43% used [0x033d0000, 0x03835348, 0x03835400, 0x03dd0000)  
  16. compacting perm gen total 12288K, used 2114K [0x03dd0000, 0x049d0000, 0x07dd0000)  
  17. the space 12288K, 17% used [0x03dd0000, 0x03fe0998, 0x03fe0a00, 0x049d0000)  
  18. No shared spaces configured.  
  19. MaxTenuringThreshold=15   
  20. [GC [DefNew   
  21. Desired survivor size 524288 bytes, new threshold 15 (max 15)   
  22. - age 1: 414664 bytes, 414664 total   
  23. : 4859K->404K(9216K), 0.0049637 secs] 4859K->4500K(19456K), 0.0049932 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]  
  24. [GC [DefNew   
  25. Desired survivor size 524288 bytes, new threshold 15 (max 15)   
  26. - age 2: 414520 bytes, 414520 total   
  27. : 4500K->404K(9216K), 0.0008091 secs] 8596K->4500K(19456K), 0.0008305 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]  
  28. Heap   
  29. def new generation total 9216K, used 4582K [0x029d0000, 0x033d0000, 0x033d0000)  
  30. eden space 8192K, 51% used [0x029d0000, 0x02de4828, 0x031d0000)   
  31. from space 1024K, 39% used [0x031d0000, 0x03235338, 0x032d0000)   
  32. to space 1024K, 0% used [0x032d0000, 0x032d0000, 0x033d0000)   
  33. tenured generation total 10240K, used 4096K [0x033d0000, 0x03dd0000, 0x03dd0000)  
  34. the space 10240K, 40% used [0x033d0000, 0x037d0010, 0x037d0200, 0x03dd0000)  
  35. compacting perm gen total 12288K, used 2114K [0x03dd0000, 0x049d0000, 0x07dd0000)  
  36. the space 12288K, 17% used [0x03dd0000, 0x03fe0998, 0x03fe0a00, 0x049d0000)  
  37. No shared spaces configured. 

規(guī)則四:如果在survivor空間中相同年齡所有對象大小的累計(jì)值大于survivor空間的一半,大于或等于個(gè)年齡的對象就可以直接進(jìn)入老年代,無需達(dá)到MaxTenuringThreshold中要求的年齡。

執(zhí)行testTenuringThreshold2()方法,并將設(shè)置-XX:MaxTenuringThreshold=15,發(fā)現(xiàn)運(yùn)行結(jié)果中survivor占用仍然為0%,而老年代比預(yù)期增加了6%,也就是說allocation1、allocation2對象都直接進(jìn)入了老年代,而沒有等待到15歲的臨界年齡。因?yàn)檫@2個(gè)對象加起來已經(jīng)到達(dá)了512K,并且它們是同年的,滿足同年對象達(dá)到survivor空間的一半規(guī)則。我們只要注釋掉其中一個(gè)對象new操作,就會發(fā)現(xiàn)另外一個(gè)就不會晉升到老年代中去了。

 清單5:

  1. [GC [DefNew   
  2. Desired survivor size 524288 bytes, new threshold 1 (max 15)   
  3. - age   1:     676824 bytes,     676824 total   
  4. : 5115K->660K(9216K), 0.0050136 secs] 5115K->4756K(19456K), 0.0050443 secs] [Times: user=0.00 sys=0.01, real=0.01 secs]  
  5.  [GC [DefNew   
  6. Desired survivor size 524288 bytes, new threshold 15 (max 15)   
  7. : 4756K->0K(9216K), 0.0010571 secs] 8852K->4756K(19456K), 0.0011009 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]  
  8.  Heap   
  9. def new generation   total 9216K, used 4178K [0x029d0000, 0x033d0000, 0x033d0000)  
  10.    eden space 8192K,  51% used [0x029d0000, 0x02de4828, 0x031d0000)   
  11.   from space 1024K,   0% used [0x031d0000, 0x031d0000, 0x032d0000)   
  12.   to   space 1024K,   0% used [0x032d0000, 0x032d0000, 0x033d0000)   
  13. tenured generation   total 10240K, used 4756K [0x033d0000, 0x03dd0000, 0x03dd0000)  
  14.     the space 10240K,  46% used [0x033d0000, 0x038753e8, 0x03875400, 0x03dd0000)  
  15.  compacting perm gen  total 12288K, used 2114K [0x03dd0000, 0x049d0000, 0x07dd0000)  
  16.     the space 12288K,  17% used [0x03dd0000, 0x03fe09a0, 0x03fe0a00, 0x049d0000)  
  17.  No shared spaces configured.  

規(guī)則五:在Minor GC觸發(fā)時(shí),會檢測之前每次晉升到老年代的平均大小是否大于老年代的剩余空間,如果大于,改為直接進(jìn)行一次Full GC,如果小于則查看HandlePromotionFailure設(shè)置看看是否允許擔(dān)保失敗,如果允許,那仍然進(jìn)行Minor GC,如果不允許,則也要改為進(jìn)行一次Full GC。

前面提到過,新生代才有復(fù)制收集算法,但為了內(nèi)存利用率,只使用其中一個(gè)survivor空間來作為輪換備份,因此當(dāng)出現(xiàn)大量對象在GC后仍然存活的情況(最極端就是GC后所有對象都存活),就需要老年代進(jìn)行分配擔(dān)保,把survivor無法容納的對象直接放入老年代。與生活中貸款擔(dān)保類似,老年代要進(jìn)行這樣的擔(dān)保,前提就是老年代本身還有容納這些對象的剩余空間,一共有多少對象在GC之前是無法明確知道的,所以取之前每一次GC晉升到老年代對象容量的平均值與老年代的剩余空間進(jìn)行比較決定是否進(jìn)行Full GC來讓老年代騰出更多空間。

取平均值進(jìn)行比較其實(shí)仍然是一種動態(tài)概率的手段,也就是說如果某次Minor GC存活后的對象突增,大大高于平均值的話,依然會導(dǎo)致?lián)J。@樣就只好在失敗后重新進(jìn)行一次Full GC。雖然擔(dān)保失敗時(shí)做的繞的圈子是***的,但大部分情況下都還是會將HandlePromotionFailure打開,避免Full GC過于頻繁。

清單6:

  1. HandlePromotionFailure = false 
  2. [GC [DefNew: 6651K->148K(9216K), 0.0078936 secs] 6651K->4244K(19456K), 0.0079192 secs] [Times: user=0.00 sys=0.02, real=0.02 secs]  
  3. [GC [DefNew: 6378K->6378K(9216K), 0.0000206 secs][Tenured: 4096K->4244K(10240K), 0.0042901 secs] 10474K->4244K(19456K), [Perm : 2104K->2104K(12288K)], 0.0043613 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]  
  4. HandlePromotionFailure = true 
  5. [GC [DefNew: 6651K->148K(9216K), 0.0054913 secs] 6651K->4244K(19456K), 0.0055327 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]  
  6. [GC [DefNew: 6378K->148K(9216K), 0.0006584 secs] 10474K->4244K(19456K), 0.0006857 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

 總  結(jié)

本章介紹了垃圾收集的算法、6款主要的垃圾收集器,以及通過代碼實(shí)例具體介紹了新生代串行收集器對內(nèi)存分配及回收的影響。

GC在很多時(shí)候都是系統(tǒng)并發(fā)度的決定性因素,虛擬機(jī)之所以提供多種不同的收集器,提供大量的調(diào)節(jié)參數(shù),是因?yàn)橹挥懈鶕?jù)實(shí)際應(yīng)用需求、實(shí)現(xiàn)方式選擇***的收集方式才能獲取***的性能。沒有固定收集器、參數(shù)組合,也沒有***的調(diào)優(yōu)方法,虛擬機(jī)也沒有什么必然的行為。筆者看過一些文章,撇開具體場景去談?wù)摾夏甏_(dá)到92%會觸發(fā)Full GC(92%應(yīng)當(dāng)來自CMS收集器觸發(fā)的默認(rèn)臨界點(diǎn))、98%時(shí)間在進(jìn)行垃圾收集系統(tǒng)會拋出OOM異常(98%應(yīng)該來自parallel收集器收集時(shí)間比率的默認(rèn)臨界點(diǎn))其實(shí)意義并不太大。因此學(xué)習(xí)GC如果要到實(shí)踐調(diào)優(yōu)階段,必須了解每個(gè)具體收集器的行為、優(yōu)勢劣勢、調(diào)節(jié)參數(shù)。

 原文鏈接:http://xgbjmxn.iteye.com/blog/1312429

【編輯推薦】

  1. Java精確截取字符串
  2. Java I/O系統(tǒng)基礎(chǔ)知識
  3. Java字符編碼根本原理
  4. Java中對象的等價(jià)性比較
  5. Java編程:數(shù)據(jù)的截尾與舍入

 

責(zé)任編輯:林師授 來源: xgbjmxn的博客
相關(guān)推薦

2010-02-22 08:58:35

JVM內(nèi)存模型垃圾收集

2017-09-21 14:40:06

jvm算法收集器

2024-03-14 09:00:00

2020-02-25 22:01:36

理解JVM垃圾收集器

2024-01-15 11:12:28

Go內(nèi)存開發(fā)

2018-04-08 08:45:53

對象內(nèi)存策略

2022-04-19 11:25:31

JVMZGC垃圾收集器

2024-12-30 08:03:08

2024-08-26 08:58:50

2010-09-26 16:42:04

JVM內(nèi)存組成JVM垃圾回收

2015-12-28 11:41:57

JVM內(nèi)存區(qū)域內(nèi)存溢出

2012-01-11 10:45:57

JavaJVM

2013-10-12 13:01:51

Linux運(yùn)維內(nèi)存管理

2009-10-30 10:47:48

VB.NET垃圾收集器

2011-07-21 14:54:26

java垃圾收集器

2022-07-25 10:15:29

垃圾收集器Java虛擬機(jī)

2018-11-01 10:34:37

JVM內(nèi)存配置

2011-08-15 16:28:06

Cocoa內(nèi)存管理

2020-05-27 21:13:27

JavaJVM內(nèi)存

2010-09-25 15:40:52

配置JVM內(nèi)存
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

国产精品综合在线视频| 中文字幕一区二区三区欧美日韩 | 欧美中日韩一区二区三区| 9i精品福利一区二区三区| 久久国产成人精品| 精品福利一区二区三区| 熟妇人妻va精品中文字幕| а天堂8中文最新版在线官网| 国产一区二区三区四| 91wwwcom在线观看| 国产午夜手机精彩视频| 私拍精品福利视频在线一区| 欧美日韩国产免费| 免费国产a级片| 中文字幕在线免费| 99久久久无码国产精品| 成人中文字幕+乱码+中文字幕| 日本亚洲欧美在线| 久久精品欧美一区| 亚洲欧美三级在线| 色婷婷免费视频| 亚洲伊人精品酒店| 色婷婷精品大在线视频| 青青青在线观看视频| 成人动漫在线免费观看| av在线一区二区| 91精品网站| 在线观看xxxx| 亚洲综合欧美| 久久久久国产精品www| 三上悠亚在线观看视频| 国产精品午夜一区二区三区| 精品国产自在久精品国产| 中文字幕在线综合| 日本高清不卡一区二区三区视频| 午夜久久福利影院| 日韩精品一区二区在线视频| 国产福利在线视频| 91亚洲男人天堂| 亚洲一区二区三区四区在线播放 | 日韩专区欧美专区| 91精品国产高清久久久久久91| 欧美特级一级片| 天天影视天天精品| 色噜噜狠狠狠综合曰曰曰88av| 天天躁日日躁aaaxxⅹ| 日本成人中文| 日韩福利在线播放| 久久人妻少妇嫩草av无码专区 | 亚洲一级二级片| 国产精品一国产精品| 日韩电影免费观看中文字幕| 在线黄色免费网站| 啪啪国产精品| 亚洲男子天堂网| 在线 丝袜 欧美 日韩 制服| 亚洲深夜福利在线观看| 精品视频www| 法国伦理少妇愉情| 国产伦精品一区二区三区千人斩| 亚洲欧美精品伊人久久| 日本一级免费视频| 欧美日韩有码| 日韩视频免费观看| 三级影片在线看| 欧美网站在线| 97超级碰碰人国产在线观看| 少妇太紧太爽又黄又硬又爽| 男女av一区三区二区色多| 日本亚洲精品在线观看| 在线观看亚洲黄色| 九九在线精品视频| 99在线看视频| 久热av在线| 亚洲欧洲一区二区在线播放| 中文字幕乱码免费| 国产福利电影在线播放| 色综合天天综合网天天看片| 中国黄色片免费看| 亚洲图色一区二区三区| 日韩毛片在线观看| 992在线观看| 国语精品一区| 国产成人一区二区三区小说| 国产又粗又黄又爽| 成人一区二区视频| 天堂精品一区二区三区| av在线播放国产| 精品久久久久久电影| 亚洲色图久久久| 一区二区精彩视频| 亚洲乱码国产乱码精品精天堂| 蜜桃av免费在线观看| 国产精品xvideos88| 日韩av男人的天堂| 99国产精品久久久久99打野战| 播五月开心婷婷综合| 涩涩涩999| 黑人极品ⅴideos精品欧美棵| 色综合一区二区三区| 久久精品视频在线观看免费| 天堂在线精品| 麻豆一区二区在线观看| 9i看片成人免费看片| 狠狠色狠狠色综合日日91app| 精品国产免费人成电影在线观... 精品国产免费久久久久久尖叫 | 亚洲每日在线| 成人激情视频免费在线| 欧美69xxxxx| 亚洲一区二区三区四区中文字幕| 天天影视综合色| 极品国产人妖chinesets亚洲人妖| 在线看欧美日韩| 在线精品免费视| 高清av一区二区| 一卡二卡3卡四卡高清精品视频| 九色porny自拍视频在线播放| 欧美高清视频不卡网| 亚洲午夜福利在线观看| 欧美人成在线| 成人亚洲综合色就1024| 国产色在线 com| 婷婷国产在线综合| 韩国三级hd中文字幕有哪些| 欧美影院三区| 欧洲成人午夜免费大片| 蜜桃视频久久一区免费观看入口| 中文字幕一区在线| 爱情岛论坛vip永久入口| 国产日韩三级| 色综合久久88| 国产黄色av网站| 国产精品盗摄一区二区三区| 国产超碰在线播放| 夜色77av精品影院| 55夜色66夜色国产精品视频| 亚洲欧美黄色片| 亚洲一区二区三区视频在线 | av日韩久久| 中文字幕日韩欧美在线 | 日本网站在线观看一区二区三区| 久久久com| 午夜久久中文| 亚洲精品视频免费| 欧美另类一区二区| thepron国产精品| 欧美综合在线播放| 欧美丝袜足交| 欧美一区在线直播| 男同在线观看| 在线看国产一区二区| 欧美做受高潮6| 日韩成人dvd| 亚洲欧美日韩精品综合在线观看| 福利一区视频| 菠萝蜜影院一区二区免费| 在线观看免费高清视频| 中文字幕一区在线观看视频| 在线免费观看av网| 欧美日韩精品| 国内精品久久久久久久果冻传媒| 国产传媒在线观看| 亚洲偷欧美偷国内偷| 樱花视频在线免费观看| 国产精品久久久久桃色tv| 激情五月俺来也| 一级欧洲+日本+国产| 亚洲在线观看视频网站| 黄页网站在线观看免费| 日韩av在线网站| 成人免费视频国产免费| 国产精品无人区| 伦伦影院午夜理论片| 樱桃成人精品视频在线播放| 麻豆精品传媒视频| 国产第一亚洲| 欧美精品videosex极品1| 免费一级在线观看播放网址| 欧美色窝79yyyycom| 亚洲一级生活片| 不卡电影一区二区三区| 中文字幕国产传媒| 欧美三级网页| 欧美久久电影| 日韩在线成人| 欧美壮男野外gaytube| 免费观看久久久久| 亚洲国产一区二区三区四区| 看黄色一级大片| 一区二区在线看| 国产综合精品在线| 国产成人综合自拍| 国产第一页视频| 综合在线一区| 欧美日韩综合网| 国产精品高清一区二区| 欧美一区第一页| 成人免费在线| 亚洲美女动态图120秒| 国产巨乳在线观看| 欧美午夜影院在线视频| 国产精品av久久久久久无| 丁香激情综合五月| 亚洲老女人av| 国产视频一区欧美| 女同性恋一区二区| 日本一二区不卡| 久久99精品久久久水蜜桃| 成人黄色91| 国产999精品视频| 99久久精品免费看国产小宝寻花| 中文字幕9999| 人成在线免费视频| 精品日本一线二线三线不卡| 中文字幕免费高清在线观看| 欧美日韩精品在线| 久久久久久久久久久网| 中文字幕一区二区三区四区不卡 | 天天干天天玩天天操| 国产亚洲毛片| 久久av综合网| 欧美+日本+国产+在线a∨观看| 日韩精品一区二区三区丰满| 国产精品自在| 99国产超薄丝袜足j在线观看 | 日本一区高清不卡| 麻豆成人入口| 国产精品v欧美精品v日韩| 婷婷激情成人| 国产精品自拍网| 素人一区二区三区| 国产精品草莓在线免费观看| 在线亚洲人成| 5566成人精品视频免费| 91高清视频在线观看| 九九热在线精品视频| 超碰在线网址| 久久国产精品99国产精| 黄色在线视频网站| 精品国产一区二区三区久久久 | 国产精品一区二区小说| 首页综合国产亚洲丝袜| 亚洲国产精品久久久久爰色欲| 亚洲每日更新| 欧美精品99久久| 国产精品三上| 日韩久久一级片| 欧美亚洲视频| 91av俱乐部| 麻豆久久久久久久| 亚洲精品久久久久久宅男| 另类小说综合欧美亚洲| 午夜免费看毛片| 国产在线一区观看| 中文字幕无码毛片免费看| 国产激情一区二区三区四区| 日韩欧美色视频| 成人涩涩免费视频| 岛国精品资源网站| 国产亚洲婷婷免费| 日本美女xxx| 亚洲欧美在线视频观看| 在线看的片片片免费| 亚洲一二三四在线| www.日本精品| 欧洲国内综合视频| 91久久久久国产一区二区| 91精品婷婷国产综合久久| 亚洲AV无码国产精品午夜字幕| 欧美v亚洲v综合ⅴ国产v| 天天干天天舔天天射| 亚洲天堂av在线播放| 午夜视频在线观看免费视频| 欧美成人精品在线| 狼人综合视频| 国产精品啪视频| 亚洲精选av| 欧美专区一二三 | 日韩精品免费一区二区三区竹菊| 欧美理论一区二区| 999久久久亚洲| 国产一级做a爰片久久毛片男| 亚洲一区二区三区高清| 少妇一级淫免费播放| 不卡的电视剧免费网站有什么| 男人舔女人下部高潮全视频| 亚洲美女视频一区| 国产免费av一区| 欧美一级日韩不卡播放免费| 天堂国产一区二区三区| 深夜福利日韩在线看| 96av在线| 成人黄色片在线| 欧美久久精品| 无码人妻精品一区二区三区99v| 日韩午夜激情| 三级av免费观看| 北条麻妃一区二区三区| 国产又粗又猛又爽又黄av| 有码一区二区三区| 国产精品xxxxxx| 亚洲的天堂在线中文字幕| eeuss影院在线播放| 久久久噜噜噜久久中文字免| 欧美大陆国产| 欧美日韩在线不卡一区| 韩国在线一区| 中文字幕第一页在线视频| 91美女在线观看| 男女免费视频网站| 欧美日韩精品欧美日韩精品一综合| 亚洲色图 校园春色| 久久91亚洲人成电影网站| 97欧美成人| 蜜桃av噜噜一区二区三| 欧美日本一区二区高清播放视频| av免费网站观看| www.亚洲人| 一区二区三区免费高清视频| 欧美日本一区二区在线观看| 日韩精品一二| 国内精品视频久久| 看亚洲a级一级毛片| 一区二区三区av| 日韩高清国产一区在线| 玖玖爱在线观看| 精品欧美激情精品一区| 亚洲乱码国产乱码精品精软件| 久久人人爽亚洲精品天堂| www.26天天久久天堂| 日韩av一区二区三区在线 | 一区二区三区中文字幕精品精品| 中文字幕av在线免费观看| 国产偷亚洲偷欧美偷精品| аⅴ资源天堂资源库在线| 成人午夜电影免费在线观看| 艳女tv在线观看国产一区| 欧美成人手机在线视频| 国产精品灌醉下药二区| 中文字幕+乱码+中文字幕明步| 亚洲精品视频在线观看视频| 亚洲天堂电影| 日本不卡久久| 日本午夜精品视频在线观看| 国产精久久一区二区三区| 91国在线观看| eeuss影院在线播放| 成人性生交大片免费看小说| 在线中文字幕亚洲| 日批视频在线看| 亚洲永久免费av| 天天射,天天干| 96精品视频在线| 激情婷婷综合| 亚洲综合欧美激情| 亚洲男女一区二区三区| 99久久精品免费看国产交换| 欧美大片免费看| 女同一区二区三区| 少妇高清精品毛片在线视频| 中文字幕电影一区| 国产精品爽爽久久| 欧美黑人性视频| 日韩最新在线| 天天干天天玩天天操| 一区二区三区在线观看视频| 欧美熟妇乱码在线一区| 日本午夜人人精品| 国产高清一区二区| 深田咏美中文字幕| 色一区在线观看| 国产不卡在线| 韩国精品一区二区三区六区色诱| 免费日韩视频| 成人欧美一区二区三区黑人一 | 欧美日韩p片| 国产精品无码一区二区三区免费| 欧亚一区二区三区| 羞羞视频在线观看不卡| 久久精品99| 国产专区欧美精品| 波多野结衣视频网站| 日日摸夜夜添一区| 国产色噜噜噜91在线精品| 中文字幕一区二区三区四区在线视频| 亚洲乱码一区二区三区在线观看| 刘亦菲毛片一区二区三区| 国产精品jvid在线观看蜜臀| 牛夜精品久久久久久久99黑人| 五月婷婷综合在线观看| 欧美丰满少妇xxxbbb| 精品极品在线| 国产麻豆电影在线观看| 成人激情黄色小说| 在线观看国产精品入口男同| 午夜精品福利在线观看| 婷婷综合视频| 免费在线观看你懂的| 欧美一级片在线| 影音成人av|