3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

深入理解JVM虚拟机(总结篇)

發布時間:2023/12/10 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解JVM虚拟机(总结篇) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

JVM平臺上還可以運行其他語言,運行的是Class字節碼。只要能翻譯成Class的語言就OK了。挺強大的。

  • JVM廠商很多
  • 垃圾收集器、收集算法
  • JVM檢測工具

關于類的加載:

  • ?Java代碼中,類型(interface, class,enum等,有些是在運行時候生成的,比如動態代理)的加載、連接與初始化過程都是在程序運行期間完成的。不涉及到對象的概念。同時也是個Runtime階段。
  • ?提供了更大的靈活性,增加了更多的可能性。提供了一些擴展,靈活擴展。

Java虛擬機與程序的生命周期:

? 在如下幾種情況下,Java虛擬機將會結束生命周期:

  • 執行了System.exit()方法
  • 程序正常執行結束
  • 程序執行過程遇到了異常或者錯誤異常終止了
  • 操作系統出現錯誤導致Java虛擬機進行終止
  • 類的加載、連接與初始化:

    加載:查找并加載類的二進制數據

    連接:?

    • 驗證: 確保被加載類的正確性。Class有格式的。
    • 準備:為類的靜態變量分配內存,并將其初始化為默認值??
    • 注:
      1.類的靜態變量或類的靜態方法,通常可以看做全局的,由類去直接調用。此時還是個類的概念,不存在對象。
      2.關于默認值問題:
      class Test{
      public static int a = 1;
      }
      中間過程: Test類加載到內存的過程中,會給a分配一個內存。然后將a初始化為默認值0(整型變量)
    • 解析: 把類中的符號引用轉為直接引用。符號的引用也是間接的引用方式。

    初始化: 為類的靜態變量賦予正確的初始值

    • class Test{public static int a = 1;} 此時的a才真正成為1了

    類的使用與卸載

    ?使用:?類的方法變量使用等

    ?卸載: class字節碼文件,加載到內存里面。形成了自己的數據結構,駐留在內存里面。可以銷毀掉。卸載到了就不能進行new 對象了。

    總體流程:

    Java程序對類的使用方式分為兩種:

  • 主動使用
  • 被動使用
  • 所有的Java虛擬機實現必須在每個類或接口被Java程序“首次主動使用”時才初始化他們。即初始化只會執行一次。

    主動使用,七種(非精確劃分,大體劃分):

  • 創建類的實例。
  • 訪問某個類或接口的靜態變量,或者對靜態變量賦值。 字節碼層面上,使用的助記符:get static、? put static
  • 調用類的靜態方法。 invoke static
  • 反射(如Class.forName("com.test.t1"))
  • 初始化一個類的子類 比如:class Parent{}class Child extends Parent{}初始化Child時候,先去初始化Parent 
  • Java虛擬機啟動時被表明為啟動類的類(Java Test) Java虛擬機啟動時候,被標明為啟動的類,即為有main方法的類,也會主動使用 
  • JDK1.7開始提供動態語言支持: 注:
    1.java.lang.invoke.MethodHandle實例的解析結果REF_getStatic, REF_putStatic, REF_invokeStatic句柄對應的類沒有初始化,則初始化
    2.1.7開始提供了對動態語言的支持。特別的JVM平臺上通過腳本引擎調用JS代碼(動態語言)。  
  • :助記符了解即可

    除了以上七種情況,其他使用Java類的方式都被看做是對類的被動使用,都不會導致類的初始化。

    類的加載:

    ?類的加載指的是將類 .class文件中的二進制數據讀入內存中,將其放在運行時數據區的方法區內,然后在內存中創建一個java.lang.Class對象(規范并說明Class對象位于哪里,HotSpot虛擬機將其放在了方法區中,JVM沒有規范這個)用來封裝類在方法區內的數據結構。

    ?引申:一個類不管生成了多少實例,所有的實例對應只有一份Class對象。 Class對象是面鏡子,能反映到方法區中的Class文件的內容、結構等各種信息。

    加載.class文件的方式:

  • 從本地系統中直接加載
  • 通過網絡下載
  • 從zip、jar等貴方文件中加載
  • 從專有數據庫中提取
  • 將Java源文件動態編譯為.class文件
  • public class MyTest1 {public static void main(String[] args) {System.out.println(MyChild1.str1); // System.out.println(MyChild1.str2);} }class MyParent1{//靜態成員變量public static String str1 = "str1";// 靜態代碼塊(程序加載初始化時候去執行)static {System.out.println("MyParent1 -----> static block running");} } class MyChild1 extends MyParent1{//靜態成員變量public static String str2 = "str2";static {System.out.println("MyChild1 -----> static block running");} }

    str1 子類調用了繼承到的父類的str1,子類的靜態代碼塊沒有執行。str1是父類中定義的。MyParent1的主動使用,但是沒有主動使用MyChild1. 總結:看定義的!

    str2 可以執行,同時初始化子類時候,父類會主動使用。所有的父類都會被初始化!

    MyTest1是一個啟動類,主動使用。先加載之。?

    總結:?

  • 對于靜態字段來說,只有直接定義了該字段的類才會被初始化。
  • 當一個類在初始化時候,要求其父類全部已經初始化完畢。每個父類最多只能初始化一次! 
  • 引申: -XX:+TraceClassLoading,用于追蹤類的加載信息并打印出來。可以看到類的加載情況。

    ? ? ? ? ? ?打印: 虛擬機在當前啟動情況下所加載的類的信息。

    總結設置方式:

    所有JVM參數都是: -XX: 開頭??

    ? 類似于Boolean類型的開關:

    ? ? ? ? -XX:+<option> 表示開啟option選項

    ? ? ? ? -XX: - <option>? 表示關閉option選項

    ? 賦值:? ?

    ? ? ?-XX:<option>=<value>, 表示將option選項的值設置為value??

    ?關于常量:

    public class MyTest2 {public static void main(String[] args) {System.out.println(MyParent2.str);} }class MyParent2{// final修飾成為常量public static final String str = "hello world";static {System.out.println("MyParent2 ----> run");}}

     

    在編譯階段這個常量被存入到 調用這個常量的方法所在的類的常量池中

    本例中:

    ? ?“hello world”是一個常量,會放置到MyTest2類的常量池中。

    ? ?這里指的時將常量存放到了MyTest2的常量池匯總,之后MyTest2與MyParent2就沒有任何關系了

    ? ?甚至,極端一些。我們可以將MyParent3的class文件刪除。(編譯完畢后,把class字節碼刪除)

    總結:

    • ?常量編譯階段會存入到調用這個常量的方法所在的類的常量池中。
    • ?本質上,調用類并沒有直接引用到定義常量的類,因此并不會觸發定義常量類的初始化。

    引申反編譯: javap -c? 類的全路徑名字

    助記符引申:

    • ldc表示將int,float 或 String類型的常量值從常量池中推送至棧頂。
    • bipush表示將單字節(-128 ~ 127)的常量值推送至棧頂  
    • sipush表示將一個短整型常量值(-32768 ~ 32767)推送至棧頂
    • iconst_1 表示將int類型的1推送至棧頂 (iconst_1 ~ iconst_5)

    助記符是在rt.jar中相關類去實現的。

    如果常量的值,在編譯器不能確定下來呢?

    public class MyTest3 {public static void main(String[] args) {System.out.println(MyParent3.str);} }class MyParent3 {public static final String str = UUID.randomUUID().toString();static {System.out.println("MyParent3 -- run");} }

    此時放在MyTest3類的常量池中沒有意義的。

    總結:  

    ? 當一個常量值并非編譯期間可以確定的,那么其值就不會被放到調用類的常量池中。這時在程序運行時,會導致主動使用這個常量所在的類,顯然會導致這個類被初始化。

    new對象實例情況:

    public class MyTest4 {public static void main(String[] args) {MyParent4 myParent4 = new MyParent4();} } class MyParent4{static {System.out.println("MyParent4 --> run");} }

    對這個類的主動使用。

    如果多次new,只會初始化一次。首次主動使用。

    數組情況:

    public class MyTest4 {public static void main(String[] args) {MyParent4[] myParent4s = new MyParent4[1];} } class MyParent4{static {System.out.println("MyParent4 --> run");} }

    不在七種情況范圍內。不會初始化!

    不是MyParent4的實例!

    到底創建的什么實例?getClass!,數組的實例到底是個啥玩意兒?

    public class MyTest4 {public static void main(String[] args) {MyParent4[] myParent4s = new MyParent4[1];//看看是啥Class<? extends MyParent4[]> aClass = myParent4s.getClass();System.out.println(aClass);} } class MyParent4{static {System.out.println("MyParent4 --> run");} }

    Java虛擬機在運行期,創建出來的類型。是個數組類型。有點類似動態代理

    數組類型也是比較特殊的。[Lxxxx

    二維數組也是一樣的特殊

    看下父類型:

    public class MyTest4 {public static void main(String[] args) {MyParent4[] myParent4s = new MyParent4[1];//看看是啥System.out.println(myParent4s.getClass().getSuperclass());} } class MyParent4{static {System.out.println("MyParent4 --> run");} }

    父類型其實是Object

    總結:

    ?對于數組實例來說,其類型是由JVM在運行期動態生成的

    ?動態生成的類型,其父類就是Object

    ?對于數組來說,JavaDoc經常將構成數組的元素為Component,實際上就是將數組降低一個維度后的類型。

    看下原生類型的數組:

    public class MyTest4 {public static void main(String[] args) {int[] ints = new int[3];System.out.println(ints.getClass());System.out.println(ints.getClass().getSuperclass());} } class MyParent4{static {System.out.println("MyParent4 --> run");} }

    助記符:

    ? anewarray: 表示創建一個引用類型的(比如類、接口、數組)數組,并將其引用值壓如棧頂。

    ? newarray: 表示創建一個指定的原始類型(如:int,float,char等)的數組,并將其引用值壓入棧頂。

    以上所總結的是類與類之間的關系,包括繼承的。下面接口的特點:

    public class MyTest5 {public static void main(String[] args) {System.out.println(MyChild5.b);} }interface MyParent5 {public static int a = 5; }interface MyChild5 extends MyParent5 {public static int b = 6; }

    ? 接口是沒有靜態代碼塊的。可以通過手動刪除class文件來證明之。

    public class MyTest5 {public static void main(String[] args) {System.out.println(MyChild5.b);} }interface MyParent5 {public static int a = 5; }interface MyChild5 extends MyParent5 {// 只有在運行時候才會賦值,會放到MyTest5的常量池里面。如果Class刪除了,運行時候就會報錯!public static int b = new Random().nextInt(2); }

    ? 結論:

    • 當一個接口在初始化時候,并不要求其父類接口都完成了初始化。
    • 只有在真正使用到父類接口的時候(如引用接口中定義的常量時),才會初始化。
    • 類,一定要先初始化父類。
    public class MyTest6 {public static void main(String[] args) {Singleton instance = Singleton.getInstance();System.out.println("counter"+ instance.counter1);System.out.println("counter"+ instance.counter2);} } class Singleton{public static int counter1;public static int counter2 = 0;private static Singleton singleton = new Singleton();private Singleton(){counter1++;counter2++;}public static Singleton getInstance(){return singleton;} }

    分析: 先賦值: 默認的0 和 給定的0,然后構造方法進行++操作。?

    如果更改位置:

    public class MyTest6 {public static void main(String[] args) {Singleton instance = Singleton.getInstance();System.out.println("counter1-->"+ instance.counter1);System.out.println("counter2-->"+ instance.counter2);} } class Singleton{public static int counter1;private static Singleton singleton = new Singleton();private Singleton(){counter1++;counter2++;System.out.println(counter1);System.out.println(counter2);}public static int counter2 = 0;public static Singleton getInstance(){return singleton;} }

    ?

    ?解析:

    按照從上到下的順序進行初始化。

    類主動使用時候,先準備,給類的靜態變量賦初始值。

    此時:?

    ? ?counter1 初始值 0?

    ? ?singleton 初始值 null

    ? ?counter2 初始值 0

    接著調用靜態方法 getInstance時候,賦初始值。

    sigleton 會指向一個實例,然后執行私有構造方法。

    然后執行到 public static int counter2 = 0時候,顯示賦值0了。

    總結:

    ?先準備

    ?再初始化: 根據類里面代碼的順序去執行的.真正的賦值(準備為其提供初始值,要不談不上做++操作)

    ?

    ?畫個圖:

    ?關于類的實例化:

    ? 為對象分配內存,即為new對象,在堆上面。

    ? ?為實例變量賦默認值、為實例變量賦正確的初始值都跟靜態變量似的了。賦予默認值之后,再去賦予開發者指定的值。

    類的加載:

    • ? 類的加載的最終產品是位于內充中的Class對象
    • ? Class對象封裝了類在方法區內的數據結構,并且向Java程序員提供了訪問方法區內的數據結構的接口

    Class是反射的入口。像一面鏡子一樣。

    有兩種類型的類加載器:

    ? 1.Java虛擬機自帶的加載器

    • 根類加載器(BootStrap)
    • 擴展類加載器(Extension)
    • 系統(應用)類加載器(System)

    2.用戶自定義的類加載器

    • java.lang.ClassLoader的子類
    • 用戶可以定制類的加載方式

    類的加載:?

    ?類加載器并不需要等到某個類被“首次主動使用”時候再加載它

    ?注:

    • ? JVM規范允許類加載器在預料某個類將要被使用時就預先加載它。如果在預先加載的過程中遇到了.class文件確實或者存在錯誤,類加載器必須在程序首次主動使用該類時候才報告錯誤(LinkageaError錯誤)
    • ? 如果這個類一直沒有被程序主動使用,那么類加載器就不會報告錯誤

    類的驗證:

    ? 類被加載后,就進入連接階段。連接就是將已經讀入到內存中的類的二進制數據合并到虛擬機的運行時的環境中去。

    類的驗證的內容:

    • ? 類文件的結構檢查
    • ? 語義檢查
    • ? 字節碼驗證
    • ? 二進制兼容性的驗證

    在準備階段:

    ??

    ?

    ?初始化階段:

    ?

    ?類的初始化步驟:

    • ?假如這個類還沒有被加載和連接,那就先進行加載和連接
    • ?假如類存在直接父類,并且這個父類還沒有被初始化,那就先初始直接父類
    • ?假如類中存在初始化語句,那就依次執行這些初始化語句?

    只有當程序訪問的靜態變量或靜態方法確實在當前類或當前接口定義時,才可以認為是對類或接口的主動使用。

    調用ClassLoader類的loadClass方法加載一個類,并不是對類的主動使用,不會導致類的初始化。

    ?

    除了以上虛擬機自帶的加載器外,用戶還可以定制自己的類加載器。Java提供了抽象類java.lang.ClassLoader,所有用戶自定義的類加載器都應該繼承ClassLoader類

    ?

    引申看下這個例子:

    public class MyTest {public static void main(String[] args) {System.out.println(MyChild.b);} }interface MyParent{public static int a = 5; } interface MyChild extends MyParent{public static final int b = 8; }

    分析:

    MyTest類有main函數。會主動使用,先去加載。

    接口和類其實是不同的,如下:

    加載層面:?

    如果是類的話,MyChild肯定會被加載。如果是接口的話,不會被加載。

    如果把b 修改為 Random(運行期才知道的值)。會將Parend 和 Child都加載. 很重要的一點是變量是編譯器的還是運行期才能確定的

    如果 parent和child都是final,test用到的常量會放入自己的常量池中,則不會對parent和child進行加載了。

    如果把接口換做class,則存在加載,不加載的話必須是final的!

    總結出了final關鍵字的區別小結:

    • ? final修飾的變量,決定當前類是否加載。(static修飾的,不會這樣)
    • ? implement 實現的接口,不會加載

    final修飾后,哪個類去主動調用就將這個常量放入到自己類的常量池里面。

    Remember:

    ?block 優先 構造函數執行,每次都執行。

    證明初始化一個類時候,不會初始化他的接口:

    public class MyTest5 {public static void main(String[] args) {System.out.println(MyChild5.b);} }interface MyParent5 {public static Thread thread = new Thread(){{System.out.println("MyParent5 Thread ==========");}}; }interface MyChild5 extends MyParent5 {public static int b = 6; } class C{{System.out.println("hello c{block}");}public C(){System.out.println("hello c(construct)");} }

    ?如果將父子的interface 改成class 則會初始化父類

    ?當一個類被初始化時候,他所實現的類是不會被初始化的。

    繼續看下面例子:

    public class MyTest5 {public static void main(String[] args) {System.out.println(MyChild5.b);} }interface MyGrandPa{public static Thread thread = new Thread(){{System.out.println("MyGrandPa Thread ==========");}}; }interface MyParent5 extends MyGrandPa{public static Thread thread = new Thread(){{System.out.println("MyParent5 Thread ==========");}}; }interface MyChild5 extends MyParent5 {public static int b = 6; } class C{{System.out.println("hello c{block}");}public C(){System.out.println("hello c(construct)");} }

    總結:

  • ?先看是否是finanl修飾,是的話,就不用加載別的類。前提是編譯器的。
  • 再看interface否。
  • 類加載器的雙親委派機制:

    ? 在雙親委派機制中,各個加載器按照父子關系形成了樹形結構,除了根類加載器之外,其余的類加載器都有且只有一個父類加載器。

    如果有一個類加載器能夠成功加載Test類,那么這個類加載器被稱為定義類加載器,所有能夠成功返回Class對象引用的類加載器(包括定義類加載器)都被稱為初始化類加載器。(了解即可)

    public class MyTest7 {public static void main(String[] args) throws ClassNotFoundException {Class<?> clazz = Class.forName("java.lang.String");System.out.println(clazz.getClassLoader());Class<?> mClazz = Class.forName("com.jvm.t1.M");System.out.println(mClazz.getClassLoader());} } //位于工程的classPath目錄地址下 class M{}

    如下例子:

    package com.jvm.t1;public class MyTest9 {static {System.out.println("MyTest9 static block");}public static void main(String[] args) {System.out.println(Child.b);} }class Parent{static int a = 3;static {System.out.println("parent static block");} }class Child extends Parent{static int b = 4;static {System.out.println("chile static block");} }

    ?便于查看加載過程清晰:

    輸出結果:

    看下面的例子:

    public class MyTest10 {static {System.out.println("MyTest10 static block");}public static void main(String[] args) {//聲明類型的使用,并不是主動使用Parent2 parent2;System.out.println("-------");parent2 = new Parent2();System.out.println("---------");System.out.println(parent2.a);System.out.println("---------");System.out.println(Child2.b);} } class Parent2{static int a = 3;static {System.out.println("Parent2 static block");} }class Child2 extends Parent2{static int b = 4;static {System.out.println("Child2 static block");} }

    ?使用child時候,parent已經被初始化了,只會初始化一次。

    總結:

    初始化一次就OK了。?

    看下面例子:

    class Parent3{static int a = 3;static {System.out.println("Parent3 static block");}static void doSomeThing(){System.out.println("do something");} } class Child3 extends Parent3{static {System.out.println("Child3 static block");} }public class MyTest11 {public static void main(String[] args) {//訪問父類的。調用父類的Parent的(主動使用)System.out.println(Child3.a);//訪問的父類的。調用父類的Parent的(主動使用)Child3.doSomeThing();} }

    總結:?

    • 雖然名字是Child3 但是沒有對其主動使用。
    • 如果使用子類去訪問父類定義的變量、方法,本質上都表示對于父類的主動使用!

    看下面例子:

    class CL{static {System.out.println("static block class CL");} }public class MyTest12 {public static void main(String[] args) throws ClassNotFoundException {//系統類加載器(應用類加載器)ClassLoader classLoader = ClassLoader.getSystemClassLoader();//指定加載的類//這個不會導致類的初始Class<?> clazz = classLoader.loadClass("com.jvm.t1.CL");System.out.println(clazz);System.out.println("-------");//類的初始化,反射導致類的初始化clazz = Class.forName("com.jvm.t1.CL");System.out.println(clazz);} }

    總結:

    • 調用classLoader.loadClass 不是對類的主動使用,不會導致初始化
    • 反射是對類的主動使用

    關于雙親委派機制:?

    public class MyTest13 {public static void main(String[] args) {ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println(systemClassLoader);while (null != systemClassLoader){systemClassLoader = systemClassLoader.getParent();System.out.println(systemClassLoader);}} }

    ?結論:

    ?在HotSpot中,BootStrap ClassLoader使用null表示的.(啟動類加載器)

    看下面例子:

    public class MyTest14 {public static void main(String[] args) {//獲取上下文的類加載器。線程創建者提供的。(有默認值的)ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();System.out.println(contextClassLoader);} }

    類型是APPClassLoader,加載應用的類加載器(系統類加載器)。

    看下面的例子:

    public class MyTest14 {public static void main(String[] args) throws IOException {//獲取上下文的類加載器。線程創建者提供的。(有默認值的)ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();//存在磁盤上的字節碼(磁盤上的目錄)String resourceName = "com/jvm/t1/MyTest13.class";//給定名字的所有資源(圖片、音頻等)Enumeration<URL> resources = contextClassLoader.getResources(resourceName);while (resources.hasMoreElements()){URL url = resources.nextElement();System.out.println(url);}} }

    ?

    獲取ClassLoader的途徑:

    ?我們自己定義的類,APPClassLoader:

    public class MyTest14 {public static void main(String[] args) throws IOException {Class<MyTest14> myTest14Class = MyTest14.class;System.out.println(myTest14Class.getClassLoader());} }

    public class MyTest14 {public static void main(String[] args) throws IOException {Class<String> stringClass = String.class;System.out.println(stringClass.getClassLoader());} }

    ?String 這個類位于rt.jar

    用戶自定義的類加載器都直接或間接的從ClassLoader類繼承下來。

    數組類的Class對象并不是由類加載器創建的,運行時由于Java虛擬機自動創建的。只有數組如此

    public class MyTest15 {public static void main(String[] args) {String[] strings = new String[2];System.out.println(strings.getClass().getClassLoader());System.out.println("--------------");MyTest15[] myTest15s = new MyTest15[12];System.out.println(myTest15s.getClass().getClassLoader());System.out.println("--------------");int[] ins = new int[2];System.out.println(ins.getClass().getClassLoader());} }

    總結:

    • 根據里面的每個元素的類型定義的!String、MyTest15。?
    • 雖然獲取到了數組的類加載器,但是數組對應的Class對象并不是ClassLoader加載的,是JVM動態創建的。
    • 原生類型,沒有加載器。

    ?自己定義類加載器,看下面例子:

    public class MyTest16 extends ClassLoader {private String classLoaderName = "";private String fileExtension = ".class";public MyTest16(String classLoaderName) {super(); // 將系統類加載器當做該類加載器的父類加載器this.classLoaderName = classLoaderName;}public MyTest16(ClassLoader parent, String classLoaderName) {super(parent); //顯示指定該類的加載器的父類加載器this.classLoaderName = classLoaderName;}private byte[] loadClassData(String name) {InputStream is = null;byte[] data = null;ByteArrayOutputStream baos = null;try {//注意win和linuxthis.classLoaderName = this.classLoaderName.replace(".", "/");is = new FileInputStream(new File(name + this.fileExtension));baos = new ByteArrayOutputStream();int ch ;while (-1 != (ch = is.read())) {baos.write(ch);}// 字節數組輸出流轉換成字節數組data = baos.toByteArray();} catch (Exception e) {e.printStackTrace();} finally {try {is.close();baos.close();} catch (Exception e) {e.printStackTrace();}}return data;}@Overrideprotected Class<?> findClass(String className) throws ClassNotFoundException {byte[] data = this.loadClassData(className);//返回Class對象return this.defineClass(className, data, 0 , data.length);}public static void test(ClassLoader classLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException {//內部底層的api已經被我們重寫了Class<?> clazz = classLoader.loadClass("com.jvm.t1.MyTest15");Object object = clazz.newInstance();System.out.println(object);}@Overridepublic String toString() {return "[" + this.classLoaderName + "]";}public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {MyTest16 loader1 = new MyTest16("loader1");test(loader1);} }

    其實此時我們定義的 findClass是沒有被調用的!以為雙親委派機制,讓父類去加載了!

    看下面例子:

    public class MyTest16 extends ClassLoader {private String classLoaderName = "";private String fileExtension = ".class";private String path;public MyTest16(String classLoaderName) {super(); // 將系統類加載器當做該類加載器的父類加載器this.classLoaderName = classLoaderName;}public void setPath(String path){this.path = path;}public MyTest16(ClassLoader parent, String classLoaderName) {super(parent); //顯示指定該類的加載器的父類加載器this.classLoaderName = classLoaderName;}private byte[] loadClassData(String className) {InputStream is = null;byte[] data = null;ByteArrayOutputStream baos = null;className.replace(",","/");try {//注意win和linuxthis.classLoaderName = this.classLoaderName.replace(".", "/");//指定磁盤全路徑is = new FileInputStream(this.path + new File(className + this.fileExtension));baos = new ByteArrayOutputStream();int ch ;while (-1 != (ch = is.read())) {baos.write(ch);}// 字節數組輸出流轉換成字節數組data = baos.toByteArray();} catch (Exception e) {e.printStackTrace();} finally {try {is.close();baos.close();} catch (Exception e) {e.printStackTrace();}}return data;}@Overrideprotected Class<?> findClass(String className) throws ClassNotFoundException {System.out.println("findClass invoked:" + className);System.out.println("class loader name" + this.classLoaderName);byte[] data = this.loadClassData(className);//返回Class對象return this.defineClass(className, data, 0 , data.length);}@Overridepublic String toString() {return "[" + this.classLoaderName + "]";}public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {// 創建自定義類加載器 名字“loader1” 父類加載器是系統類加載器MyTest16 loader1 = new MyTest16("loader1");//此路徑為classPath,故 findClass方法不會被調用執行! 如果換個路徑,不是classPath就會去執行了!loader1.setPath("D:\\eclipse_pj\\dianshang\\jvmTest\\out\\production\\jvmTest\\");Class<?> clazz = loader1.loadClass("com.jvm.t1.MyTest15");System.out.println("class:"+ clazz.hashCode());Object object = clazz.newInstance();System.out.println(object);} }

    ?委托給父類,父類去classPath目錄下面找,找到了加載之。

    關于命名空間:

    • 每個類加載器都有自己的命名空間,命名空間由該加載器及所有父加載器所加載的類組成
    • 同一個命名 空間中,不會出現類的完整名字(包括類的包名)相同的兩個類
    • 在不同的命名空間中,有可能會出現類的完整名字(包括類的包名)相同的兩個類?

    關于類的卸載:

    • ?當MySample類被加載、連接和初始化后,他的聲明周期就開始了。當代表MySample類的Class對象不再被引用,即不可觸及時,Class對象就會結束聲明周期,MySample類在方法區內的數據也會被卸載,從而結束Sample類的生命周期。
    • ?一個類何時結束生命周期,取決于代表他的Class對象何時結束生命周期。
    • 由用戶自定義的類加載器所加載的類是可以被卸載的。??

    ? 加載? <----> 卸載

    ?看下面的例子:

    public class MySample {MySample(){System.out.println("MySample is loaded by"+ this.getClass().getClassLoader());MyCat myCat = new MyCat();} } public class MyCat {public MyCat() {System.out.println("MyCat is loaded by" + this.getClass().getClassLoader());} } public class MyTest17 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {MyTest16 loader1 = new MyTest16("loader1");//要加載的類Class<?> clazz = loader1.loadClass("com.jvm.t1.MySample");System.out.println("clazz"+ clazz.hashCode());//如果注釋掉改行,那么并不會實例化MySample對象,即MySample構造方法不會被調用// 因此不會實例化MyCat對象,即沒有對MyCat進行主動使用,這里就不會加載MyCat classObject object = clazz.newInstance();// new instance 沒有任何參數。調用無參構造方法} }

    ?關于命名空間的說明:?

  • ? 子加載器加載的類,能夠訪問父加載器加載的類。??
  • ? 父加載器加載的類,不能訪問子加載器加載的類。
  • public class Test3 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {MyTest16 loader1 = new MyTest16("loader1`");MyTest16 loader2 = new MyTest16("loader2`");loader1.setPath("/User/test/");loader1.setPath("/User/test/");//加載相同的類。(都委托為appClassLoader了)Class<?> clazz1 = loader1.loadClass("com.jvm.test.Test");//加載過了Class<?> clazz2 = loader2.loadClass("com.jvm.test.Test");// 都是app加載的,雙親委派System.out.println(clazz1 == clazz2);Object o1 = clazz1.newInstance();Object o2 = clazz2.newInstance();Method setMyPerson = clazz1.getMethod("setMyPerson", Object.class);//執行o1的方法,參數是o2setMyPerson.invoke(o1, o2);} }

    情況1.如果 class字節碼在classPath,返回 true。 執行成功。(讀者自行考慮,提示雙親委派)

    情況2.如果 class字節碼只在:"/User/test/" 。返回false。執行報錯。

    ? ? ? ?原因

    • ? ?命名空間。 兩個loader不存在父子關系,是平行的。在jvm中存在兩個命名空間。
    • ? ?不同命名空間的類不可見,引用不到就報錯。(子加載器的命名空間包含所有父加載器的命名空間,子可看到父類加載的所有類。)

    雙親委派的好處:

  • ? ?可以確保Java核心庫的類型安全,所有的Java應用都至少會引用java.lang.Object類,也就是說在運行期,java.lang.Object這個類會被加載到Java虛擬機中。
  • ? ?如果這個加載過程是由Java應用自己的類加載器所完成的,那么很可能就會在JVM匯總存在多個版本的java.lang.Object類。而且這些類之間是不兼容,相互不可見的(命名空間)。
  • ? ?借助雙親委派機制,java核心類庫中的類的加載工作都是啟動類加載器統一完成的。確保了Java應用所使用的都是同一個版本的Java核心類庫,他們之間是相互兼容的。Java核心類庫不會被自定義的替代。啟動類去加載之。
  • ? ?不同的類加載器可以為相同名稱(binary name)的類創建額外的命名空間,相同名稱的類可以并存在Java虛擬機中,只需要不用的類加載器(包括沒有父子關系、不同類加載器)來加載他們即可。不同類加載器所加載的類是不兼容的。這就相當于在Java虛擬機內部創建了一個又一個相互隔離的? ? ? ?Java類空間,這類技術在很多框架中都得到了實際應用。
  • 知識總結:?

  • 關于擴展類加載器:需要做成jar包,再放到指定目錄下。
  • 在運行期,一個Java類是由該類的完全限定名(binary name, 二進制名)和用于加載該類的定義列類加載器(defing loader)所共同決定的。如果同樣名字(即相同的完全限定名)的類是由兩個不同的加載器所加載,那么這些類就是不同的。即便 .class文件的字節碼完全一樣,并且從相同的位置加載亦如此。
  • 在Oracle的hotSopt實現中,系統屬性sun.boot.class.path如果修改錯了,則運行會報錯,提示: Error occurred during Initialization of VM
  • 內建于JVM中的啟動類加載器會加載java.lang.ClassLoader以及其他的Java平臺類,當JVM啟動時候,一塊特殊的機器碼會運行,他會擴展類加載器與系統類加載器,這塊特殊的機器碼叫做啟動類加載器(BootStrap)。
  • 啟動類加載器并不是Java類,而其他的加載器都是Java類。啟動類加載器是特定于平臺的機器指令,它負責開啟整個加載過程。
  • 所有類加載器(除啟動類加載器)都被實現為Java類。不過,總歸要有一個組件來加載第一個Java類加載器,從而讓整個加載過程能夠順利進行下去,加載第一個純Java類加載器就是啟動類加載器的職責。
  • 啟動類加載器還會負責加載供JRE正常運行所需要的基本組件,這包括java.util與java.lang包中的類等等。
  • 簡單看下:

    public class test4 {public static void main(String[] args) {System.out.println(ClassLoader.class.getClassLoader());//擴展類System.out.println(Launcher.class.getClassLoader());} }

    ?

    可以自己做系統類加載器。略。需要控制臺指令顯示指定

    通過改變屬性,提示:

    System.getProperty("java.system.class.loader")

    引申:

    ?getSystemClassLoader()

    • ? 返回用于委托的系統類加載器,
    • ? 創建的ClassLoader默認的爸爸(也是用于啟動應用的類加載器)。
    • ? 創建類加載器,然后設置為調用這個方法的線程的上下文類加載器。(Contex Class Loader)。應用框架,服務器大量使用的!
    • ? 默認的系統類加載器,與此類實現相關的實例。?
    • ? java.system.class.loader所指定的類,是被默認的系統類加載器加載。必須要定義public的構造方法,傳遞自定義類加載器的爸爸。

    OpenJDK是JDK開源版本。

    解析Class.forName:

    ?其實:Class.forName("Foo") 等價于 Class.forName("Foo",true, this.getClass().getClassLoader() )?

    關于線程上下文的類加載器: Thread.currentThread().setContextClassLoader(sys)

    作用就是改變雙親委派模型在某些場景下不適用的情況。

    看下面例子:

    public class MyTest24 {public static void main(String[] args) {System.out.println(Thread.currentThread().getContextClassLoader());System.out.println(Thread.class.getClassLoader()); // 路徑位置導致的} }

    當前類加載器(Current ClassLoader)

    每個類都會使用自己的類加載器(即加載自身的類加載器)去加載其它類(指的是所依賴的類):

    ? 如果ClassX引用了ClassY,那么ClassX的類加載器就會去加載ClassY(前提是ClassY尚未被加載)

    線程上下文類加載器:

    • ?線程上下文類加載器是從JDK1.2開始引入的,類Thread中的getContextClassLoader? 與 setContextClassLoader(ClassLoader cl) 分別用來獲取和設置上下文類加載器
    • 如果沒有通過setContextClassLoader進行設置的話。線程將繼承其父線程的上下文類加載器。
    • Java應用運行時的初始線程的上下文類加載器是系統類加載器。在線程中運行的代碼可以通過該類加載器來加載類與資源。

    ?線程上下文類加載器的重要性:

    ?應用場景:?

    ?SPI(Service Provider Interface)

    ?父ClassLoader可以使用當前線程Thread.currentThread().getContexClassLoader() 所指定的ClassLoader加載的類,這就改變了父ClassLoader不能使用子ClassLoader或是其他沒有直接父子關系的ClassLoader加載的類的情況。

    ?線程上下文類加載器就是當前線程的Current ClassLoader

    在雙親委派模型下,類加載是由下至上的,即下層的類加載器會委托上層進行加載。但是對于SPI來說,有些接口是Java類核心庫所提供的,而Java核心庫是由啟動類加載器來加載的,而這些接口的實現卻來自于不同的jar包(廠商提供。

    Java的啟動類加載器是不會加? 載其他來源你的Jar包 ,這樣的傳統的雙親委派模型就無法滿足SPI的要求。而通過給當前線程設置上下文類加載器,就可以由設置的上下文類加載器來實現對于接口實現類的加載。

    ?總結:接口是啟動類加載器加載的, 實現類應用類加載器加載,通過給當前的線程設置上下文類加載器,實現對于接口實現類的加載,打破了雙親委派模型現在。(框架開發,底層開發會用到)

    (JDK中沒有對于JDBC的任何實現,除了傳統的接口之外,具體實現都是由廠商趨勢線的,比如MySQL。)

    看下面代碼:

    public class MyTest25 implements Runnable {private Thread thread;public MyTest25(){thread = new Thread(this);thread.start();}@Overridepublic void run() {// 獲取到上下文類加載器ClassLoader classLoader = this.thread.getContextClassLoader();this.thread.setContextClassLoader(classLoader);System.out.println("Class:"+classLoader.getClass());System.out.println("Class:"+classLoader.getParent().getClass());}public static void main(String[] args) {MyTest25 myTest25 = new MyTest25();} }

    沒有設置,所以線程將繼承父線程的上下文類加載器。

    線程上下文類加載器的一般使用模式(獲取 - 使用 - 還原)

    注意:如果一個類由A加載器加載,那么這個類的依賴也是由相同的類加載器加載的(如果該依賴之前沒有被加載過的話)

    ContextClassLoader的作用就是為了破壞Java的類加載委托機制??

    當高層提供了統一的接口讓底層去實現,同時又要在高層加載(或者實例化)低層的類時候,就必須要通過線程上下文類加載器來幫助高層的ClassLoader找到并加載該類

    看下面例子:

    public class MyTest26 {public static void main(String[] args) {//設置下// Thread.currentThread().setContextClassLoader(MyTest26.class.getClassLoader());ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);Iterator<Driver> iterator = loader.iterator();while (iterator.hasNext()){Driver driver = iterator.next();System.out.println("driver" + driver.getClass() + ", loader" + driver.getClass().getClassLoader() );}System.out.println("當前線程上下文類加載器:" + Thread.currentThread().getContextClassLoader());System.out.println("ServiceLoader的類加載器:" + ServiceLoader.class.getClassLoader());} }

    ?

    關于字節碼:

    對于能編譯成class字節碼的代碼,class的規范,合法性保證好了就OK了。

    對于Idea編譯器,是非常熟悉class字節碼了,可以隨心所欲的反編譯。

    對于java代碼:

    public class MyTest1 {private int a = 1;public int getA() {return a;}public void setA(int a) {this.a = a;} }

    idea看字節碼:

    // // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) //package com.jvm.t1.t2;public class MyTest1 {private int a = 1;public MyTest1() {}public int getA() {return this.a;}public void setA(int a) {this.a = a;} }

    通過反編譯指令:

    ?看到三個方法:其中一個是默認的構造方法。

    詳細查看字節碼信息:輸入

    javap -c com.jvm.t1.t2.MyTest1

    Compiled from "MyTest1.java" public class com.jvm.t1.t2.MyTest1 {
    //構造方法public com.jvm.t1.t2.MyTest1();
    //下面都是助記符Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: aload_05: iconst_16: putfield #2 // Field a:I9: returnpublic int getA();Code:0: aload_01: getfield #2 // Field a:I4: ireturnpublic void setA(int);Code:0: aload_01: iload_12: putfield #2 // Field a:I5: return }

    看下面指令:

    javap -verbose com.jvm.t1.t2.MyTest1

    Classfile /D:/eclipse_pj/dianshang/jvmTest/out/production/jvmTest/com/jvm/t1/t2/MyTest1.classLast modified 2019-10-20; size 473 bytesMD5 checksum c5b1387c6f6c79b14c1b6a5438da3b29Compiled from "MyTest1.java" public class com.jvm.t1.t2.MyTest1minor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER// 常量池: 占據相當大的比重 Constant pool:#1 = Methodref #4.#20 // java/lang/Object."<init>":()V#2 = Fieldref #3.#21 // com/jvm/t1/t2/MyTest1.a:I#3 = Class #22 // com/jvm/t1/t2/MyTest1#4 = Class #23 // java/lang/Object#5 = Utf8 a#6 = Utf8 I#7 = Utf8 <init>#8 = Utf8 ()V#9 = Utf8 Code#10 = Utf8 LineNumberTable#11 = Utf8 LocalVariableTable#12 = Utf8 this#13 = Utf8 Lcom/jvm/t1/t2/MyTest1;#14 = Utf8 getA#15 = Utf8 ()I#16 = Utf8 setA#17 = Utf8 (I)V#18 = Utf8 SourceFile#19 = Utf8 MyTest1.java#20 = NameAndType #7:#8 // "<init>":()V#21 = NameAndType #5:#6 // a:I#22 = Utf8 com/jvm/t1/t2/MyTest1#23 = Utf8 java/lang/Object
    //方法的描述 {public com.jvm.t1.t2.MyTest1();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: aload_05: iconst_16: putfield #2 // Field a:I9: returnLineNumberTable:line 3: 0line 5: 4LocalVariableTable:Start Length Slot Name Signature0 10 0 this Lcom/jvm/t1/t2/MyTest1;public int getA();descriptor: ()Iflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: getfield #2 // Field a:I4: ireturnLineNumberTable:line 8: 0LocalVariableTable:Start Length Slot Name Signature0 5 0 this Lcom/jvm/t1/t2/MyTest1;public void setA(int);descriptor: (I)Vflags: ACC_PUBLICCode:stack=2, locals=2, args_size=20: aload_01: iload_12: putfield #2 // Field a:I5: returnLineNumberTable:line 12: 0line 13: 5LocalVariableTable:Start Length Slot Name Signature0 6 0 this Lcom/jvm/t1/t2/MyTest1;0 6 1 a I } SourceFile: "MyTest1.java"

    使用如上的這個命令分析字節碼時候,將會分析該字節碼文件的魔數,版本號,常量池,類信息,類的構造方法,類中的方法信息,類變量與成員變量等信息。

    備注:

    ?魔數: 所有的.class字節碼文件的前4個字節都是魔數,魔數值為固定值: 0xCAFEBABE。

    ?魔數之后的4個字節為版本信息,前兩個字節表示minor version(次版本號),后兩個字節表示major? version(主版本號)。

    常量池(constant pool): 緊接著主板號之后就是常量池入口。一個Java類中定義的很多信息都是由常量池來維護和描述的。常量池在整個字節碼文件中占的比重最大,里面的信息會被很多地方引用到。相當于把常量集中在一個地方,其他地方用到時候去引用之。通過Index找到常量池中特定的常量。可以將常量池看做是class文件的資源倉庫。比如:Java類總定義的方法與變量信息,都是存儲在常量池中。常量池中主要存儲兩類常量:字面量與符號引用量。

    注意:常量池!里面存放的不一定都是常量。也有變量信息。

    • 字面量如文本字符串,Java中聲明為final 的常量值等,而符號引用,比如說類和接口的全局限定名,字段的名稱和描述符,方法的名稱和描述符等。

    常量池的總體結構: Java類所對應的常量池主要由常量池數量與常量池數組(常量表)這兩部分共同組成。常量池數量緊跟在主版本后面,占據2個字節;常量池數組則緊跟在常量池數量之后。常量池數組和一般的數組不同的是,常量池數組中不同的元素的類型,結構都是不同的,長度當然也就不同;但是每一種元素的數都是一個u1類型,該字節是個標志位,占據1個字節。JVM在解析常量池時候,會根據這個u1類型來獲取元素的具體類型。值得注意的是:常量池數組中元素的個數 = 常量池數 - 1 (其中0暫時不使用)。目的是滿足某些常量池索引值的數據在特定情況下需要表達 【不引用任何一個常量池】的含義。根本原因在于,索引為0也是一個常量(保留常量)。只不過它不位于常量表中,這個常量就對應null值。所以常量池的索引從1而非從0開始。

    如下,從1開始:

    常量池中數據類型:

    ?在JVM規范中,每個變量/字段都有描述信息,描述信息主要的作用是描述字段的數據類型、方法的參數列表(包括數量、類型與順序)與返回值。根據描述符規則,基本數據類型和代表無返回值的void類型都

    用一個大寫字符來表示,對象類型則使用字符L加對象的全限定名稱來表示。為了壓縮字節碼文件的體積。對于基本數據類型,JVM都只使用一個大寫字母來表示,如下所示:

    B ---> byte? ?C --> char? D ---> doube F ---> float? I --> int? J --long S? --> short? Z --> boolean? V --> void

    L --->對象類型 ,如:?L java/lang/String

    對于數組類型來說,每一個維度使用一個前置的 [ 來表示,如 int[ ]? 被記錄為 [I , String[][] 被記錄為[[ Ljava/lang/String

    用描述符描述方法時,按照先參數列表,后返回值的順序來描述。參數列表按照參數的嚴格順序放在一組之內,如方法:

    get getName (int id, String name)描述為:

    常量池里面存儲的各種 index? 和 信息

    Java字節碼整體結構:

    ?完整Java字節碼接口例子:

    Access_Flag訪問標志

    訪問標志信息包括該Class文件是類還是接口,是否被定義成public,是否是abstract,如果是類,是否被聲明稱final。

    ?

    ?字段表集合:

    ? 字段表用于描述類和接口中聲明的變量。這里的字段包含了類級別變量(靜態變量)以及實例變量(非靜態變量),但是不包括方法內部聲明的局部變量。

    一個field_info包含的信息:

    ?

    ? 方法表:

    methods_count: u2

    前三個字段和field_info一樣

    ?方法中每個屬性都是一個attribute_info結構

    JVM預定義了部分attribute,但是編譯器自己也可以實現自己的attribute寫入class文件里,供運行使用

    不同的attribute通過attribute_name_index來區分

    ?Code結構

    Code attribute的作用是保存該方法的結構,如所對應的字節碼

    • attribute_length 表示attribute所包含的字節數,不包含attribute_name_index 和 attribute_length字段
    • max_stack 表示這個方法運行的任何時刻所能達到的操作數棧的最大深度
    • max_locals表示方法執行期間創建的局部變量的數目,包含用來表示傳入的參數的局部變量?
    • code_length表示該方法所包含的字節碼的字節數以及具體的指令碼
    • 具體字節碼即是該方法被調用時,虛擬機所執行的字節碼
    • exception_table,這里存放的是處理異常的信息
    • 每個exception_table表項由start_pc, end_pc, handler_pc, catch_type 組成
    • start_pc和end_pc表示在code數組中的從start_pc到end_pc處(包含start_pc, 不包含end_pc)的指令拋出的異常會由這個表項來處理。
    • handeler_pc 表示處理異常的代碼的開始處,catch_type 表示會被處理的異常類型,它指向常量池里的一個異常類。當catch_type為0時,表示處理所有異常。
    • 方法中的每個屬性都是一個attribute_info結構

    ?code attribute的作用是保存該方法的結構,如所對應的字節碼

    ?

    ?推薦大家使用:??jclasslib 閱讀字節碼信息

    ?Java中,每一個方法都是可以訪問this(表示對當前對象的引用),

    ?字節碼角度,如果方法本身是個非靜態(實例)的,this可以作為方法的第一個方法,可以隱式的傳遞進來。會使得每個實例方法都可以訪問this。至少會有個局部變量,這個局部變量就是this。

    對于某各類Test,中的靜態方法 使用了synchronized 關鍵字,相當于給這個Test對應的Class對象加鎖了。

    關于this關鍵字:?

    Java編譯器在編譯時候,把對this方法的訪問,轉變成了對普通參數的訪問。在Java中,每一個非靜態實例的方法的局部變量中,至少會存在一個指向當前對象的局部變量。即:

    對于Java類中的每一個實例方法(非static方法),其中在編譯后所生成的字節碼當中,方法參數的數量總會比源代碼匯總方法的參數多一個(this),它位于方法的第一個參數位置處;這樣我們就可以在Java實例方法中使用this訪問當前對象的屬性以及其他方法。這個操作是在編譯期間完成的,由javac編譯器,在編譯時候將對this的訪問轉化為對一個普通實例方法參數的訪問,接下來在運行期間,由JVM在調用實例方法時,自動向實例方法傳入該this參數。所以,在實例方法的局部變量表中,至少會有一個指向當前對象的局部變量。

    關于異常處理:

    ?Code結構:

    ?attribute_length表示attribute鎖包含的字節數,不包含attribute_name_index和attribute_length字段

    ?max_stack表示這個方法運行的任何時刻所能達到的操作數棧的最大深度

    ?max_locals表示方法執行期間所創建的局部變量的數目,包含用來表示傳入的參數的局部變量

    ?code_lenght表示該方法所含的字節碼的字節數以及具體的指令碼

    ?具體字節碼即是該方法被調用時,虛擬機所執行的字節碼

    ?exception_table, 這里存放的是處理異常的消息

    ?每個exception_tabel 表項由start_pc, end_pc , handler_pc ,catch_type 組成

    ?start_pc 和 end_pc 表示在code 數組中的從start_pc都end_pc處(包含start_pc, 不包含end_pc)的指令拋出的異常會由這個表項來處理

    ?handler_pc表示處理異常的代碼的開始處。catch_type 表示會被處理的異常類型,它指向常量池里的一個異常類。當catch_type為0時,表示處理所有的異常。

    Java字節碼對于異常的處理方式:

    ?1.? 統一采用異常表的方式來對異常進行處理

    ?2. 老版本中,并不是使用遺產表的方式來對異常進行處理的,而是采用特定的指令方式(了解)

    ?3. 當異常處理存在finally語句塊時,現代化的JVM采取的方式將finally語句塊的字節碼拼接到每一個catch塊后面,換句話說,程序存在多少個catch塊,就會在每一個catch塊后面重復多少個finally語句塊的字節碼。

    棧幀,是一種用于幫助虛擬機執行方法調用與方法執行的數據結構。

    棧幀, 本身是一種數據結構,封裝了風閥的局部變量表,動態鏈接信息,方法的返回地址操作數棧等信息。

    Java中,對于不同的類之間的關系,編譯期間,地址關系實際上是不知道的。什么時候知道?

    ? 1. 類加載時候

    ? 2. 真正調用時候,才知道目標方法地址。

    基于以上兩點,引申出了符號引用和直接引用。

    有些符號引用是在類加載階段或是第一次使用時就會轉換為直接引用,這種轉換叫做靜態解析;另外一些符號引用則是在每次運行期轉為直接引用,這種轉換叫做動態鏈接,這體現為Java的多態性

    比如父類因用戶指向子類實現。

    Aninaml a = new Cat();a.run();a = new Fish();a.run

    編譯時候,a都是Animal.? 字節碼角度,都是Animal

    運行時候,每次運行期,都會進行一次直接引用的轉換。

    JVM 方法調用的字節碼指令:

    ?1. invokeinterface:調用接口中的方法,實際上是在運行期決定的,決定到底調用實現該接口的那個對象的特定方法(一個接口,n個實現類)。

    ?2. invokestatic: 調用靜態方法

    ?3.invokespecial: 調用自己的私有方法,構造方法(<init>) 以及父類的方法

    ?4. invokevirtual: 調用虛方法,運行期動態查找的過程。

    ?5. invokedynamic: 動態調用方法。

    靜態解析的四種情況:

    ? 1. 靜態方法

    ? 2.父類方法

    ? 3. 構造方法

    ?4. 私有方法(公有方法可以被重寫或者復寫,多態的可能。私有方法在加載時候就能夠被確定了)

    以上四種稱之為: 非虛方法。他們是在類加載階段就可以將符號引用轉換為直接引用的。

    public class MyTest5 {public void test(GrandPa grandPa){System.out.println("grandPa");}public void test(Father father){System.out.println("father");}public void test(Son son){System.out.println("son");}public static void main(String[] args) {//都是GrandPal類型的GrandPa father = new Father();GrandPa son = new Son();MyTest5 myTest5 = new MyTest5();myTest5.test(father);myTest5.test(son);}}class GrandPa{} class Father extends GrandPa{} class Son extends Father{

    以上代碼 ,? father的靜態類型是Grandpa,而father的實際類型(真正指向的類型)是Father??

    變量本身的靜態類型是不會被改變的,?GrandPa father

    結論:

    ?變量的靜態類型是不會發生變化的,而變量的實際類型是可以發生變化的(多態的一種體現)。實際類型是在運行期方可確定。

    以上,方法的重載,參數類型不一樣。方法重載是一種純粹的靜態行為。

    所以,當使用myTest5調用方法的時候, 是根據類型進行匹配。尋找類型是 GrandPa的。編譯器就可以完全確定的。

    public class MyTest6 {public static void main(String[] args) {Fruit apple = new Apple();Fruit orange = new Orange();apple.test();orange.test();apple = new Orange();apple.test();} }class Fruit{public void test(){System.out.println("fruit");} } class Apple extends Fruit{@Overridepublic void test() {System.out.println("apple");} } class Orange extends Fruit{@Overridepublic void test() {System.out.println("orange");} }

    引申:

    Java中,new起到了三個作用:

    ?1. 在堆上開辟空間

    ?2. 執行構造方法

    ?3. 將構造方法執行后返回的堆上的此引用值返回

    方法的動態分派:

    ?方法的動態分派涉及到一個重要概念:方法接收者

    ?invokevirtual字節碼指令的多態查找流程

    ?方法重載和方法重寫,我們可以得到這個方法重載是靜態的,是編譯器行為,方法重寫是動態的,是運行期行為。

    public class MyTest7 {public static void main(String[] args) {Animal animal = new Animal();Dog dog = new Dog();animal.test("hello");dog.test(new Date( ));} }class Animal{public void test(String str){System.out.println("animal str");}public void test(Date date){System.out.println("animal date");} }class Dog extends Animal{@Overridepublic void test(String str) {System.out.println("dog str");}@Overridepublic void test(Date date) {System.out.println("dog date");} }

    ?

    針對于方法調用動態分派的過程,虛擬機會在類的方法區建立一個虛方法表的數據結構(virtual method table,簡稱 vtable)

    現代JVM在執行Java代碼的時候,通常會將解釋執行與編譯執行二者結合起來執行。

    所謂解釋執行:通過解釋器讀取字節碼,遇到相應的指令就去執行該指令

    所謂編譯執行:通過及時編譯器(Just In Time, JIT)將字節碼轉為本地機器碼來執行,現代JVM會根據代碼熱點來生成相應的本地機器碼。

    基于棧的指令集合基于寄存器的指令集之間的關系:

    ?1. JVM執行指令時所采取的的方式是基于棧的指令集

    ?2. 基于棧的指令集的主要操作: 入棧、出棧

    ?3. 基于棧的指令集的優勢在于他可以在不同平臺間一直,而基于寄存器的指令集是與硬件架構密切關聯的,無法做到可移植。

    ?4.? 基于棧的指令集的缺點: 完成相同的操作,執行數量通常要比基于寄存器的指令集數量多 。基于棧的指令集是在內存中操作的,而基于寄存器的指令集是直接由CPU執行的,它是在高速緩沖區中進行的,速度要快很多。雖然虛擬機可以采用一些優化手段,但總體? ? ? 來說,基于棧的指令集的執行速度要慢一些。

    注意:

    棧 配合 局部變量表使用,局部變量表的0位置是this?

    對應動態代理,主要有一個類(proxy)和一個接口(InvocationHandler)去搞定。

    接口:

    public interface Subject {void request(); }

    實現類:

    public class RealSubject implements Subject {@Overridepublic void request() {System.out.println("reslsubjct");} }

    代理類:

    /*** 動態代理文件*/ public class DynamicSubject implements InvocationHandler {private Object sub;public DynamicSubject(Object obj){this.sub = obj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("before calling"+ method);method.invoke(this.sub, args);System.out.println("after calling"+ method);return null;} }

    測試:

    public class Client {public static void main(String[] args) {RealSubject realSubject = new RealSubject();DynamicSubject dynamicSubject = new DynamicSubject(realSubject);Class<?> clazz = realSubject.getClass();//獲取 Class對象是為了,動態代理需要類加載器。Subject subject = (Subject) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), dynamicSubject);subject.request();System.out.println(subject.getClass());} }

    程序運行期動態生成的:

    首先創建代理類,然后創建代理類的實例對象。?

    對象分為兩部分內容:?

    1, 對象本身擁有的那些數據(位于堆)

    2, 對象所屬的類型(元數據信息,MetaData) 所有實例對應一個Class對象。位于方法區(存儲的一部分對象的類型數據信息)

    方案一:

    對象引用的是一個指向對象實例的指針,另外一個指針指向方法區中的類型數據

    方案二:(HotSpot的方案)

    對象引用的是對象本身,和一個指向方法區匯總的類型數據指針? (對象實例數據、方法區)

    ?兩種方案的差別L

    堆發生垃圾回收頻率很高,對于垃圾回收算法來說,有幾種會涉及到對象移動(壓縮):為了保證區域連續的地方增大,移動之

    方案一:對象一旦移動了,指針值會發生變化!隨著每次垃圾回收會變化。

    方案二:指針不會隨之變化。

    ?JVM內存劃分:

    虛擬機棧

    程序計數器

    本地方法棧:主要用于處理本地方法

    堆: JVM管理的最大一塊內存空間

    線程共享的區域,主要存儲元信息。從JDK1.8開始,徹底廢棄永久代。使用元空間(meta space)

    運行時常量池(方法區的一部分): 方法區的一部分內容。編譯后的字節碼的符號引用等等。加載完后,放入到方法區的運行時常量池。

    直接內存: Direct Memory。 與Java NIO密切相關,JVM通過堆上的DirectByteBuffer來直接操作內存。

    現代幾乎所有的垃圾收集器都是采用的分代收集算法,所以堆空間也基于這一點進行了相應的劃分。

    Java對象的創建:

    ?new

    ?反射

    ?克隆

    ?反序列化

    new關鍵字創建對象的3個步驟:

    ?1, 在堆內存中創建出對象的實例

    ?2, 為對象成員變量賦初始值(指的是,實例變量,區別靜態變量)

    ?3, 將對象的引用返回。?

    虛擬機干的活兒: 檢查指令的參數new指令創建一個對象,指令參數是不是能在常量池中定位成一個類的符號引用。查看這個類是不是已經加載、鏈接、初始化了。

    指針碰撞: 前提是堆中的空間通過一個指針進行分割,一側是已經被占用的空間,另一側是未被占用的空間。

    空閑列表:(前提是堆內存空間中已被使用與未被使用的空間交織在一起的。這時,虛擬機就需要通過一個列表來記錄那些空間是可以用的,哪些空間是已被使用的,接下來找出可以容納下新創建對象的且未被使用的空間,在此空間存放該對象,同時還要修改列表的記錄)

    一個對象包含三部分布局:

    ? 1.對象的頭,?

    ? 2.實例數據(class中定義的成員變量)?

    ? 3.對齊填充

    永久代屬于與堆連接的一個空間,對于永久代處理是比較麻煩的。

    元空間,使用的操作系統的本地內存。可以不連續的。元空間里還有元空間虛擬機,管理元空間的內存的分配和回收情況。 初始大小21M,隨著對于內存占用,會進行垃圾回收,甚至內存擴展,可以擴展到內存大小的最大值。

    存放一個類的元數據信息,在框架中,用到運行期動態生成類的手段。動態創建出來的類,元信息放在元空間。

    元空間參數: -XX:MaxMetaspaceSize=200M

    在Java虛擬機(以下簡稱JVM)中,類包含其對應的元數據,比如類的層級信息,方法數據和方法信息(如字節碼,棧和變量大小),運行時常量池,已確定的符號引用和虛方法表。

    在過去(當自定義類加載器使用不普遍的時候,幾乎不動態搭理),類幾乎是“靜態的”并且很少被卸載和回收,因此類也可以被看成“永久的”。另外由于類作為JVM實現的一部分,它們不由程序來創建,因為它們也被認為是“非堆”的內存。

    在JDK8之前的HotSpot虛擬機中,類的這些“永久的”數據存放在一個叫做永久代的區域。永久代一段連續的內存空間,我們在JVM啟動之前可以通過設置-XX:MaxPermSize的值來控制永久代的大小,32位機器默認的永久代的大小為64M,64位的機器則為85M。永久代的垃圾回收和老年代的垃圾回收是綁定的,一旦其中一個區域被占滿,這兩個區都要進行垃圾回收。但是有一個明顯的問題,由于我們可以通過?XX:MaxPermSize 設置永久代的大小,一旦類的元數據超過了設定的大小,程序就會耗盡內存,并出現內存溢出錯誤(OOM)。

    備注:在JDK7之前的HotSpot虛擬機中,納入字符串常量池的字符串被存儲在永久代中,因此導致了一系列的性能問題和內存溢出錯誤。想要了解這些永久代移除這些字符串的信息,請訪問這里查看。

    隨著Java8的到來,我們再也見不到永久代了。但是這并不意味著類的元數據信息也消失了。這些數據被移到了一個與堆不相連的本地內存區域,這個區域就是我們要提到的元空間。

    這項改動是很有必要的,因為對永久代進行調優是很困難的。永久代中的元數據可能會隨著每一次Full GC發生而進行移動。并且為永久代設置空間大小也是很難確定的,因為這其中有很多影響因素,比如類的總數,常量池的大小和方法數量等。

    同時,HotSpot虛擬機的每種類型的垃圾回收器都需要特殊處理永久代中的元數據。將元數據從永久代剝離出來,不僅實現了對元空間的無縫管理,還可以簡化Full GC以及對以后的并發隔離類元數據等方面進行優化。

    移除永久代的影響

    由于類的元數據分配在本地內存中,元空間的最大可分配空間就是系統可用內存空間。因此,我們就不會遇到永久代存在時的內存溢出錯誤,也不會出現泄漏的數據移到交換區這樣的事情。最終用戶可以為元空間設置一個可用空間最大值,如果不進行設置,JVM 會自動根據類的元數據大小動態增加元空間的容量。

    注意:永久代的移除并不代表自定義的類加載器泄露問題就解決了。因此,你還必須監控你的內存消耗情況,因為一旦發生泄漏,會占用你的大量本地內存,并且還可能導致交換區交換更加糟糕。

    元空間內存管理

    元空間的內存管理由元空間虛擬機來完成。先前,對于類的元數據我們需要不同的垃圾回收器進行處理,現在只需要執行元空間虛擬機的 C++ 代碼即可完成。在元空間中,類和其元數據的生命周期和其對應的類加載器是相同的。話句話說,只要類加載器存活,其加載的類的元數據也是存活的,因而不會被回收掉。

    我們從行文到現在提到的元空間稍微有點不嚴謹。準確的來說,每一個類加載器的存儲區域都稱作一個元空間,所有的元空間合在一起就是我們一直說的元空間。當一個類加載器被垃圾回收器標記為不再存活,其對應的元空間會被回收。在元空間的回收過程中沒有重定位和壓縮等操作。但是元空間內的元數據會進行掃描來確定 Java 引用。

    元空間虛擬機負責元空間的分配,其采用的形式為組塊分配。組塊的大小因類加載器的類型而異。在元空間虛擬機中存在一個全局的空閑組塊列表。當一個類加載器需要組塊時,它就會從這個全局的組塊列表中獲取并維持一個自己的組塊列表。當一個類加載器不再存活,那么其持有的組塊將會被釋放,并返回給全局組塊列表。類加載器持有的組塊又會被分成多個塊,每一個塊存儲一個單元的元信息。組塊中的塊是線性分配(指針碰撞分配形式)。組塊分配自內存映射區域。這些全局的虛擬內存映射區域以鏈表形式連接,一旦某個虛擬內存映射區域清空,這部分內存就會返回給操作系統。

    上圖展示的是虛擬內存映射區域如何進行元組塊的分配。類加載器 1 和 3 表明使用了反射或者為匿名類加載器,他們使用了特定大小組塊。 而類加載器 2 和 4 根據其內部條目的數量使用小型或者中型的組塊。

    ?參考:https://www.infoq.cn/article/Java-PERMGEN-Removed

    命令:jstat -gc 進程號? 打印元空間信息

    ? ? ? ? ? ?jmap -clstats PID? 打印類加載器數據

    ? ? ? ? ? jcmd PID GC.class_stats? 診斷命令

    jcmd 是從jdk1.7開始增加的命令

    ?1. jcmd pid VM.flag:查看JVM啟動參數

    ?2. jcmd pid help: 列出當前運行的Java進行可以執行的操作

    ?3. jcmd pid help JFR.dump: 查看具體命令的選項

    ?4. jcmd pid PerfCounter.print: 查看JVM性能相關參數

    ?5. jcmd pid VM.uptime:查看JVM的啟動時長

    ?6. jcmd pid GC.class_histogram 查看系統中類的統計信息

    ?7. jcmd pid Thread.print: 查看線程堆棧信息

    ?8. jcmd pid GC.heap_dump filename: 導出heap dump文件,導出的文件可以通過jvisualvm查看

    ?9. jcmd pid VM.system_properties: 查看JVM的屬性信息

    10. jcmd pid VM.version: 查看目標JVM進程的版本信息

    11. jcmd pid VM.command_line:查看JVM啟動的命令行參數信息

    jstack: 可以查看或是導出Java應用程序中棧線程的堆棧信息

    jmc: java? Mission Control

    補充:

    ?針對于犯法調用動態分派的過程,虛擬機會在類的方法區建立一個虛方法表的數據結構(virtual method table, vtable)

    ?針對于invokeinterface指令來說,迅疾會建立一個叫接口方法表的數據結構(interface method table, itable)?

    JVM運行時數據區:

    程序計數器

    本地方法棧

    Java虛擬機棧(JVM Stack)

    • ? ? Java虛擬機棧描述的是Java方法的執行模型: 每個方法執行的時候都會創建一個幀(Frame)棧用于存放局部變量表,操作棧,動態鏈接,方法出口等信息。一個方法的執行過程,就是這個方法對于幀棧的入棧出棧過程。
    • ? ?線程隔離

    堆?

    • ? 堆里存放的是對象的實例
    • ? 是Java虛擬機管理內存中最大的一塊
    • ? GC主要的工作區域,為了高效的GC,會把堆細分成更多的子區域
    • ?線程共享

    方法區:

    • ? 存方法每個Class的結構信息,包括常量池,字段描述,方法描述
    • ?GC的非主要工作區域

    看下面例子:

    public void method(){Object obj = new Object();}

    生成了兩部分內存區域:

    ?1.obj這個引用變量,因為是方法內的變量,放到JVM Stack里面

    ?2. 真正Object class的實例對象,放到Heap里面

    上述的new語句一共消耗12個byte。JVM規定引用占4個byte(JVM Stack),而空對象是8個byte(在Heap)

    方法結束后,對應Stack中的變量馬上回收,但是Heap中的對象要等GC來回收

    垃圾判斷算法:

    ?引用計數算法(Reference Counting)?

    ? ? 無法解決對象循環引用的問題

    ?跟搜索算法(Root Tracing)?

    ? ?GC Root:?

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

    ? ? ? 方法區中的引用變量

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

    方法區:

    ? Java虛擬機規范表示可以不要求虛擬機在這區實現GC,這區GC的“性價比”一般比較低

    ? 在堆中,尤其是在新生代,常規應用進行一次GC一般可以回收70%~95%的空間,二方法區的GC效率遠小于此

    ? 當前的商業JVM都有實現方法區的GC,主要回收兩部分內容:廢棄常量與無用類

    主要回收兩部分內容: 廢棄常量與無用類

    ?類回收需要滿足如下3個條件:

    ? 該類所有的實例都已經備GC, 也就是JVM中不存在該Class的任何實例

    ? 加載該類的ClassLoader已經被GC

    ? 該類對應的Java.lang.Class對象沒有任何地方被引用,如不能在任何地方通過反射訪問該類的方法

    ? 在大量使用反射、動態代理、CGLib等字節碼框架、動態生成JSP以及OSGi這類頻繁自定義ClassLoader的場景都需要JVM具備類卸載的支持以保證方法區不會溢出

    標記清除算法:

    ? 標記+清除兩個過程效率不高嗎,需要掃描所有對象。

    ? 產生不連續碎片,空間碎片提多可能會導致后續使用中無法找到足夠的連續內存而提前觸發另一次的垃圾收集動作

    復制算法:?

    現在的商業虛擬機用的復制算法來回收新生代。Hotspot虛擬機默認eden和survivor的大小比例是8:1,也就是每次只有10%的內存是“浪費”的

    在對象存活率高的時候,效率有所下降

    如果不想浪費50%的空間,就需要有額外的空間進行分配擔保用于應付半區內存中所有對象都100%存活的極端情況,所以在老年代一般不能直接選用這種算法。

    只需要掃描存活的對象,效率更高

    不會產生碎片

    需要浪費額外的內存作為復制區

    適合生命周期端的對象,因為每次GC總能回收大部分的對象,復制的開銷比較小

    標記整理算法:

    ?沒有內存碎片

    ?比標記清除耗費更多的時間

    分代收集:

    ?一般是把Java對分為新生代和老年代,這樣就可以根據年齡特點采用最適當的收集算法。新生代GC有大批對象死去,只有少量存活,那就選用復制算法只需要付出少量存活對象的復制成本就可以完成收集。

    老年代:

    • ? 存放了經過一次或多次GC還存活的對象
    • ? 一般采用Mark-Sweep或者Mark-Compact算法進行GC
    • ?有多種垃圾收集器可以選擇。每種垃圾收集器可以看做一個GC算法的具體實現。可以根據具體應用的需求選擇合適的垃圾收集器(吞吐量?響應時間?)

    永久代:

    ?并不屬于對(Heap)但是GC也會涉及到這個區域

    ?存放了每個Class的機構信息,包括常量池,字段描述,方法描述。與垃圾收集要收集的Java對象關系不大

    內存結構:

    ?備注: 在Hotspot中本地方法棧和JVM方法棧是同一個,因此也可以用-Xss控制

    GC要做的是將那些dead的對象所占用的內存回收掉

    • ? ?HotSpot認為沒有引用的對象是dead的
    • ? HotSpot將引用分為四種: Strong、Soft、 Weak、 Phantom? ? ?

    Strong即默認通過Object o = new Object() 這種方式賦值的引用

    Soft、Weak、Phantom這三種原則都是集成Reference

    在Full GC時會對Reference類型的引用進行特殊處理

    ? Soft: 內存不夠時候,一定會被GC。長期不用也會被GC

    ? Weak: 一定會被GC,當Mark為Dead,會在ReferenceQueue中通知

    ? Phantom: 本來就沒有引用,當從JVM heap中釋放時會通知

    GC的時機:

    ?在分代模型的基礎上,GC從時機上分為兩種: Scavenge GC 和 Full GC

    ? ?1.Scanvenge GC(Minor GC) :

    ? ? ? ? ? ? 觸發時機: 新對象生成時,Eden空間滿了

    ? ? ? ? ? ? 理論上Eden區大多數對象會在Scavenge GC回收,復制算法的執行效率會很高,Scavenge GC時間比較短

    ? 2. Full GC(stop the world)

    ? ? ?對整個JVM進行整理,包括Young、Old 和Perm

    ? ? 主要的觸發時機: 1.Old滿了? 2.Perm滿了 3.System.gc()

    ? ? 效率很低,盡量減少Full GC

    垃圾收集器的并發和并行:

    ?并行: 多個垃圾收集器的線程同時工作,但是用戶線程處于等待狀態

    ?并發: 收集器在工作的同事,可以允許用戶線程工作。 并發不代表解決了GC停頓的問題,在關鍵的步驟還是要停頓。比如在垃圾收集器標記垃圾的時候。但是在清除垃圾的時候,用戶線程可以和GC線程并發執行。


    Serial收集器:

    ?單線程收集器,收集時會暫停所有工作線程(Stop The World)使用復制收集算法,虛擬機運行在Client模式時的默認新生代收集器。

    ?最早的收集器,單線程進行GC

    ?新生代和老年代都可以使用

    ?在新生代,采用復制算法;在老年代,采用標記整理算法

    ?因為是單線程GC,沒有多線昵稱切換的額外開銷,簡單使用

    ?Hotpot Client模式缺省的收集器

    ?

    ?ParNew收集器

    ? ParNew收集器就是Serial的多線程版本,除了使用多個收集線程外,其余行為包括算法,STW,對象分配規則,回收策略等都與Serial收集器一模一樣

    ?對應的這種收集器是JVM運行在Server模式的默認新生代收集器,在單CPU的環境中,ParNew收集器并不會比Serial收集器有更好的效果。只有在多CPU的環境下,效率才會比Serial收集器高

    ?使用復制算法(因為針對新生代)

    ?通過-XX:ParallelGCThreads來控制GC線程數的多少。需要結合具體的CPU個數

    ?Server模式下新生代的缺省收集器

    ?Parallel Scavenge收集器

    ? Parallel Scavenge 收集器也是一個多線程的收集器,也是使用復制算法,但它的對象分配規則與回收策略都與ParNew收集器有所不同,它是以吞吐量最大化(即GC時間棧中運行時間最小)為目標的收集器實現,它允許較長時間的STW換取總吞吐量最大化

    Serial Old收集器

    ?Serial Old是單線程收集器,使用標記整理算法,是老年代的收集器。

    ?老年代版本吞吐量優先收集器,使用多線程和標記整理,JVM1.6提供,在此以前,新生代使用了PS收集器的話,老年代除Serial Old別無選擇。因為PS無法與CMS收集器配合工作。

    Parallel Old

    ?Parallel Scavenge在老年代的實現

    ?在JVM1.6才出現 Parallel Old

    ?采用對線程,Mark-Compact算法

    ?更注重吞吐量

    ?Parallel Scavenge + Prallel Old = 高吞吐量,但GC停頓可能不理想

    ?

    CMS(Concurrent Mak Sweep)

    ?cms是一種最短停頓時間為目標的收集器,使用CMS不能達到GC效率最高(總體GC時間最小),但它能盡可能降低GC時服務的停頓時間,CMS收集器使用的是標記清除算法

    ? 追求最短停頓時間,非常適合Web應用

    ? 只針對老年區,一般結合ParNew使用

    ? Concurrent,GC線程和用戶線程并發工作(盡量并發)

    ? Mark-Sweep

    ? 只有在多CPU環境下才有意義

    ? 使用-XX:+UseConcMarkSweepGC打開

    ?缺點:

    ?以犧牲CPU資源的帶勁來減少用戶線程的停頓。當CPU個數少于4到時候,有可能對吞吐量影響非常大

    ?CMS在并發清理的過程中,用戶線程還在跑。這是需要預留一部分空間給用戶線程

    ?CMS用Mark-Sweep,會帶來碎片問題。碎片過多時候會容易頻繁觸發Full GC。

    GC樂基收集器的JVM參數定義

    Java內存泄露常見原因:

    ?1.對象定義在錯誤范圍(Wrong Scope)

    ?2.異常(Exception)處理不當

    ?3.集合數據管理不當

    如果Foo實例對象的生命較長,會導致臨時性內存泄露。(這里的names變量其實只有臨時作用)

    class Foo{private String[] names;public void doIt(int length){if (names == null || names.length < length){names = new String[length];populate(names);print(names);}} }

    JVM喜歡生命周期短的兌現,這樣做已經足夠高效。 (成員變量變成局部變量)

    class Foo{public void doIt(int length){String[] names = new String[length];populate(names);print(names);} }

    連接不釋放:finally

    rs.close() // 必須釋放

    集合數據管理不當:

    當使用 Array-based的數據結構(ArrayList,HashMap等),盡量減少resize

    • 比如new ArrayList時,盡量估算size,在創建的時候把size確定
    • 減少resize可以避免沒有必要的array copying, gc碎片等問題

    如果一個List只需要順序訪問,不需要隨機訪問(Random Access),用LinkedList代替ArrayList

    • LinkedList本質是鏈表,不需要resize,但只適用于順序訪問

    輸出JVM垃圾回收,詳盡的情況:-verbose:gc

    ?初始大小-Xms20M

    ?堆大小-Xmx20M

    (相等,啟動時候不會出現都抖動問題。)

    ?新生代大小 -Xmn10m

    ?垃圾回收詳細信息:-XX:+PrintGCDetails?

    ?Edn:survor的比值 :?-XX:SurvivorRatio=8

    public class t {public static void main(String[] args) {int size = 1024 * 1024;//原生的數組 里面都是0byte[] myAlloc1 = new byte[2 * size];byte[] myAlloc2 = new byte[2 * size];byte[] myAlloc3 = new byte[2 * size];System.out.println("hello world");} }

    看出沒有發生GC:

    繼續添加 數組:

    byte[] myAlloc3 = new byte[2 * size];
    發生了GC

    ?新生代的垃圾回收,怎么回收都不夠呀。除了程序的,JVM啟動時候也會有大量的對象

    ?Full GC 會導致Stop? the World 要避免之 Full GC后老年代有可能會變多哦

    來個GC的

    ?PS: Parallel Scavenge收集器

    9216K / 1024 = 9K?

    這樣的新生代使用的是9k? 因為from to? 有一個區域始終是閑置的

    5646 - 624 = 5022K? 執行完gc后,新生代釋放的空間容量(包括晉升到老年代的)

    5646-4728 = 918? 執行完gc后,總的堆空間釋放的容量?

    5022 - 918 = 4104K? 老年代使用的容量?

    當新生代已經容納下,待分配的對象時候,新創建的,直接誕生在老年代。

    看下面的例子:

    public class t {public static void main(String[] args) {int size = 1024 * 1024;//原生的數組 里面都是0byte[] myAlloc1 = new byte[2 * size];byte[] myAlloc2 = new byte[2 * size];byte[] myAlloc3 = new byte[2 * size];//這個數組直接在老年代分配!byte[] myAlloc4 = new byte[4 * size];System.out.println("hello world");} }

    沒有發生Full GC:

    直接在老年代分配對象!

    不指定時候,默認使用:

    PSYoungGen: Parallel Scavenge(新生代垃圾收集器)

    ParOldGen: Parallel Old (老年代垃圾收集器)

    命令行指令:

    ?java -XX:+PrintCommandLineFlags -version?

    虛擬機參數:?-XX:PretenureSizeThreshold=4194304 ?閾值? 必須配合另外一個參數!-XX:UseSerialGC 否則不起作用

    當新創建的對象大小超過這個,就直接誕生在老年代了

    public class t {public static void main(String[] args) {int size = 1024 * 1024;//5M 超過閾值 byte[] myAlloc1 = new byte[5 * size];} }

    運行結果:

    ?JVM:

    -Xms20M
    -Xmx20M
    -Xmn10m
    -XX:+PrintGCDetails
    -XX:SurvivorRatio=8
    -XX:PretenureSizeThreshold=4194304
    -XX:+UseSerialGC

    結論:

    ?PretenureSizeThreadshold:設置超過多個時直接在老年代分配

    ?如果需要分配10M

    public class t {public static void main(String[] args) {int size = 1024 * 1024;//10M byte[] myAlloc1 = new byte[10 * size];} }

    ?經過大量GC后,發現不能分配。放不下10M! 新生代+老年代一共才10M

    設置閾值后,即便是新生代能容納,一樣會分配到老年代!

    MaxTenuringThreshold作用: 在可以自動調節對象晉升(Promote)到老年代閾值的GC中,設置該閾值的最大值(再怎么自動調節也不會超過這個最大值)。?

    --XX:MaxTenuringThreshold=5

    改參數默認15,CMS中默認值為6,G1默認為15(在JVM中,該數值是由4個bit來表示的,所以最大值111 即15)

    -XX:+PrintTenuringDistribution 打印的效果,比如打印年齡為1的對象的情況等等

    經歷了多次GC后,存活的對象會在From? Survivor 和 To Survivor之間來回存放,而這里的一個前提是這兩個空間有足夠的空間來存放數據,在GC算法中,會計算每個對象年齡的大小。如果達到了某個年齡后發現總大小已經大于了Survivor空間的50%,那么這時就需要調調整閾值,不能在繼續等到默認的15次GC后才完成晉升。

    因為這樣會導致Survivor空間不足,所以需要調整閾值,讓這些存活對象盡快完成晉升。

    看下面的例子:

    -Xms20M
    -Xmx20M
    -Xmn10m
    -XX:+PrintGCDetails
    -XX:SurvivorRatio=8
    -XX:MaxTenuringThreshold=5
    -XX:+PrintTenuringDistribution
    -XX:+PrintCommandLineFlags

    public class t {public static void main(String[] args) {int size = 1024 * 1024;//10Mbyte[] myAlloc1 = new byte[2 * size];byte[] myAlloc2 = new byte[2 * size];byte[] myAlloc3 = new byte[2 * size];byte[] myAlloc4 = new byte[2 * size];System.out.println("hello world");} }

    運行結果:

    ?-XX:+PrintCommandLineFlags : 圖中箭頭的啟動參數信息

    max 是設置的閾值? new Threshold 5 是自動調整的值

    看下面的例子:

    配置:

    -verbose:gc
    -Xmx20M
    -Xmn50m
    -XX:TargetSurvivorRatio=60
    -XX:+PrintTenuringDistribution
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps
    -XX:+UseConcMarkSweepGC
    -XX:+UseParNewGC
    -XX:MaxTenuringThreshold=3

    解釋:

    -verbose:gc

    -Xmx20M

    -Xmn50m

    -XX:targetSurvivorRatio=60 #在某一個survivor空間已經被存活的占據空間60,重新計算閾值。

    -XX:+PrintTenuringDistribution #打印對象在survivor在對空間的年齡情況

    -XX:+PrintGCDetails

    -XX:+PrintGCDateStamps? ?#打印當前GC執行的的時間戳

    -XX:+UserConcMarkSweepGC? #指定垃圾收集器CMS(CMS是用在老年代的)

    -XX:UserParNewGC? #新生代ParNew

    -XX:MaxTenuringThreshold=3

    public class t {public static void main(String[] args) throws InterruptedException {byte[] byte1 = new byte[512 * 1024];byte[] byte2 = new byte[512 * 1024];myGC();Thread.sleep(1000);System.out.println("111");myGC();Thread.sleep(1000);System.out.println("222");myGC();Thread.sleep(1000);System.out.println("333");myGC();Thread.sleep(1000);System.out.println("444");byte[] byte3 = new byte[512 * 1024];byte[] byte4 = new byte[512 * 1024];byte[] byte5 = new byte[512 * 1024];myGC();Thread.sleep(100);System.out.println("555");myGC();Thread.sleep(100);System.out.println("666");System.out.println("hello world");}private static void myGC() {for (int i = 0; i < 40; i++) {byte[] byteArray = new byte[2014 * 1024];}} }

    ?多次垃圾回收情況

    ?CMS:

    枚舉根節點:

    ?當執行系統停頓下來后,并不需要一個不漏的檢查完所有執行上下文和全局的引用位置,虛擬機應當是有辦法直接得知哪些地方存放著對象引用。在HotSpot的實現中,是使用一組成為OopMap的數據結構來達到這個目的的。

    安全點:

    ?在OopMap的協助下,HotSpot可以快速且準確的完成GC Root枚舉,但一個很現實的問題隨之而來:可能導致引用關系變化,或者說OopMap內容變化的指令非常多,如果為每一條指令都生成對應的OopMap,那將會需要大量的額外空間,這樣GC的空間成本將會變得更? ? ?高亮

    ?實際上,HotSpot并沒有為每條指令都生成OopMap,而只是在“特定的位置”記錄了這些信息,這些位置成為安全點(SafePoint),即程序執行時并非在所有地方都停頓下來開始GC,只有在達到安全點時才能暫停。

    ?SafePoint 的選定既不能太少以至于讓GC等待時間太長,也不能過于頻繁以至于過飯增大運行時的負載。所以,安全點的選定基本上是以“是否具有讓程序長時間執行的特征”為標準進行選定的。因為每條指令執行的時間非常短暫,程序不太可能因為指令流長度太長而過? ?長的時間運行,“長時間執行”的最明顯特征就是指令序列復用,例如方法調用、循環跳轉、異常跳轉等。所以具有這些功能的指令才會產生SafePoint.

    ? 對于SafePoint,另一個需要考慮的問題時如何在GC發生時讓所有線程(這里不包括執行JNI調用的線程)都“跑”到最近的安全點上再停頓下來:搶占式中斷(Preemptive Suspension)和主動式中斷(Voluntary Suspension)

    • ? ? 搶占式中斷: 他不需線程的執行代碼主動去配合,在GC發生時,首先把所有線程全部中斷,如果有線程中斷的地方不在安全點上,就恢復線程,讓它“跑”到安全點上。
    • ? ? 主動式中斷: 當GC需要中斷線程的時候,不直接對線程操作,僅僅簡單的設置一個標志,各個線程執行時主動去輪訓這個標志,發現中斷標志為真時就自己中斷掛起。輪訓標志的地方和安全點是重合的,另外再加上創建對象需要分配內存的地方

    現在幾乎沒有虛擬機采用搶占式中斷來暫停線程從而響應GC事件

    安全區域:

    ?在使用SafePoint似乎已經完美地解決了如何進入GC的問題,但實際上情況卻不一定。SafePoint機制保證了程序執行時候,在不太長時間內就會遇到可進入GC的SafePoint。但如果程序在“不執行”的時候呢?所謂程序不執行就是沒有分配CPU時間,典型的例子就是出于Sleep狀態或者Blocked狀態,這時候線程無法響應JVM的中斷請求,JVM也顯然不太可能等地線程重新分配CPU時間,對于這種情況,就需要安全區域(Safe Regin)來解決了。

    ?在線程執行到Safe Regin中的代碼時候,首先標識自己已經進入了Safe Regin,那樣,當在這段時間里JVM要發起GC時,就不用管標識自己為Safe Region狀態的線程線程了。在線程要離開Safe Region時,他要檢查系統是否已經完成了根節點枚舉(或者是整個GC過程),如果完成了,那線程就繼續執行,否則他就必須等待線程直到收到可以安全離開Safe Region的信號為止。

    CMS收集器

    ?CMS收集器,以獲取最短回收停頓時間為目標,多數應用于互聯網站或者B/S系統的服務器上

    ?CMS是基于“標記-清除”算法實現的,整個工程分為四個步驟:

  • 初始標記(CMS initial Mark)
  • 并發標記? (CMS concurrent mark)
  • 重新標記? (CMS remark)
  • 并發清除? (CMS concurrent sweep)
  • 其中,初始標記、重新標記這兩個步驟仍然需要“Stop The World”

    初始標記只是標記一下GC Roots能直接關聯到的對象,速度很快

    并發標記階段就是進行GC Roots Tracing的過程?

    重新標記階段則是為了修正并發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍微長一些,但遠比并發標記的時間短。

    CMS收集器的運作步驟如下圖所示,在整個過程中耗時最長的并發標記和并發清除過程收集器線程都是可以與用戶線程一起工作,因此,從總體上看,CMS收集器的內存回收過程是與用戶線程一起并發執行的

    ?優點:

    ?并發收集、低停頓,Oracle公司的一些官方文檔中也稱之為并發低停頓收集器(Concurrent Low Pause Collector)

    ?缺點:

    • CMS收集器對CPU資源非常敏感
    • CMS收集器無法處理浮動垃圾(FloatingGarbage),可能出現“Concurrent mode Failure”失敗而導致另一次Full GC的產生。如果在應用中老年代增長不是太快,可以適當調高參數-XX:CMSInitiatingOccupancyFraction的值來提高觸發百分比,以便降低內存回收次數從而獲取更好的性能,要是CMS運行期間預留的內存無法滿足程序需要時,虛擬機將啟動后備預案:臨時啟用Serial Old收集器來重新進行老年代的垃圾收集,這樣停頓時間就很長了。所有說參數-XX:CMSInitiatingOccupancyFraction設置的太高很容易導致大量“Concurrent Mode Failure”失敗,性能反而降低。
    • 收集結束時候,會有大量空間碎片產生,空間碎片過多時,將會給大對象分配帶來很大麻煩,往往出現老年代還有很大空間剩余,但是無法找到足夠大的連續空間來支配當前對象,不得不提前進行一次Full GC。CMS收集器提供了一個 XX:+UseCMSCompactAtFullCollection開關參數(默認就是開啟的),用于在CMS收集器頂不住時候要進行Full GC時開啟內存碎片的合并整理過程,內存整理的過程是無法自拔的,空間碎片問題沒有了,但停頓時間不得不邊長。

    ? 空間分配擔保:

    ?在發生Minor GC之前,虛擬機會先檢查老年代最大可用的連續空間是否大于新生代所有對象總空間,如果這個條件成立,那么Minor GC可以確保是安全的。當大量對象在MinorGC后仍存活,就需要老年代進行空間分配擔保,把Survivor無法容納的對象直接進入老年代。如果老年代判斷到剩余空間不足(根據以往每一次回收晉升到老年代對象容量的平均值作為經驗值),則進行一次Full GC

    CMS收集器收集步驟:

  • Initial Mark
  • Concurrent Mark
  • Concurrent Preclean
  • Concurrent Abortable Preclean
  • Final Remark
  • Concurrent Sweep
  • Concurrent Reset
  • 1. 是CMS兩次Stop The World 事件中的其中一次,這個階段的目標是:標記哪些直接被GC Root 引用或者被年輕代存活對象所引用的所有對象。

    ?2.在這個階段Garbage Collector會遍歷老年代,然后標記所有存活的對象,它會根據上個階段找到的GC Root是遍歷查找。并發標記階段,他會與用戶的應用程序并發運行。并不是老年代的所有的存活對象都會被標記,因為在標記期間用戶的程序可能會改變一些引用。

    ?在上面的圖中,與階段1的圖進行對比,就會發現有一個對象的引用地址發生了變化。

    3. 這是個并發階段,與應用的線程并發運行,并不會stop應用的線程。在并發運行的過程中,一些對象的引用可能會發生變化,但是這種情況發生時,JVM會將包含這個對象的區域(Card)標記為Dirty,也就是Card Marking

    ? ? 在pre-clean階段,那些能夠從Dirty對象到達的對象也會被標記,這個標記做完之后,dirty card 標記就會被清除了

    4.?這也是一個并發階段,但是同樣不會影響影響用戶的應用線程,這個階段是為了盡量承擔 STW(stop-the-world)中最終標記階段的工作。這個階段持續時間依賴于很多的因素,由于這個階段是在重復做很多相同的工作,直接滿足一些條件(比如:重復迭代的次數、? ? ? 完成的工作量或者時鐘時間等)

    5.這是第二個 STW 階段,也是 CMS 中的最后一個,這個階段的目標是標記所有老年代所有的存活對象,由于之前的階段是并發執行的,gc 線程可能跟不上應用程序的變化,為了完成標記老年代所有存活對象的目標,STW 就非常有必要了。

    ? 通常 CMS 的 Final Remark 階段會在年輕代盡可能干凈的時候運行,目的是為了減少連續 STW 發生的可能性(年輕代存活對象過多的話,也會導致老年代涉及的存活對象會很多)。這個階段會比前面的幾個階段更復雜一些。

    經歷著五個階段之后,老年代所有存活的對象都被標記過來,現在可以通過清除算法去清除哪些老年代不再使用的對象。

    6.這里不需要STW,它是與用戶的應用程序并發運行,這個階段是:清除哪些不再使用的對象,回收他們的占用空間為將來使用

    ?

    ?7. 這個階段也是并發執行的,它會重設CMS內部的數據機構,為下次的GC做準備

    總結:

    • CMS通過將大量工作分散到并發處理階段來減少STW時間,在這塊做的非常優秀,但是CMS也有一些其他問題。
    • CMS收集器無法處理浮動垃圾(Floating garbage)可能出現“Concurrent Mode Failure”失敗而導致另一次Full GC的產生,可能引發串行Full GC
    • 空間碎片,導致無法分配大對象,CMS收集器提供了一個-XX:+UseCMSCompactAtFullCollection開發參數(默認就是開啟的),用于在CMS收集器頂不住要進行Full GC時開啟內存碎片的合并整理過程,內存整理的過程是無法并發的,空間碎片問題沒有了,但停頓時間不得不變長;
    • 對于堆比較大的應用,GC的時間難以估計。

    看下面例子:

    配置參數:

    -verbose:gc
    -Xmx20M
    -Xms20M
    -Xmn10m
    -XX:+PrintGCDetails
    -XX:SurvivorRatio=8
    -XX:+UseConcMarkSweepGC

    public class t {public static void main(String[] args) {int size = 1024 * 1024;byte[] myAlloc1 = new byte[4 * size];System.out.println("1111");byte[] myAlloc2 = new byte[4 * size];System.out.println("2222");byte[] myAlloc3 = new byte[4 * size];System.out.println("3333");byte[] myAlloc4 = new byte[4 * size];System.out.println("4444");}}

    ?默認情況下,老年代的CMS 和新生代的 ParNew是成對出現的。

    ?可以看到CMS收集過程

    吞吐量:

    ? 吞吐量關注的是,在一個指定的時間內,最大化一個應用的工作量。?
    ? 如下方式來衡量一個系統吞吐量的好壞:

    ? ? 1.在一小時內同一個事務(或者任務、請求)完成的次數(tps)。?
    ? ? 2.數據庫一小時可以完成多少次查詢;

    ? ? 3.對于關注吞吐量的系統,卡頓是可以接受的,因為這個系統關注長時間的大量任務的執行能力,單次快速的響應并不值得考慮

    響應能力:

    響應能力指一個程序或者系統對請求的是否能夠及時響應。?
    比如:?
    ? ?一個桌面UI能多快的響應一個事件;?
    ? ?一個網站能夠多快返回一個頁面請求;?
    ? ?數據庫能夠多快返回查詢的數據;

    ? ?對于這類對響應能力敏感的場景,長時間的停頓是無法接受的。

    G1收集器是一個面向服務端的垃圾收集器,適用于多核處理器,大內存容量的服務端系統

    它滿足短時間GC停頓的同時達到一個較高的吞吐量

    JDK7以上版本使用

    G1收集器的設計目標

    • 與應用線程同時工作,幾乎不需要stop-the-world(與CMS類似);
    • 整理剩余空間,不產生內存碎片;(CMS只能在full-GC時,用stop-the-world整理碎片內存)
    • GC停頓更加可控;
    • 不犧牲系統的吞吐量;
    • GC不要求額外的內存空間(CMS需要預留空間存儲浮動垃圾);

    G1的設計規劃是要替換掉CMS

    • ?G1在某些方面彌補了CMS的不足,比如,CMS使用的是mark-sweep算法,自然會產生碎片;然而G1基于copying算法,高效的剩余內存,而不需要管理內存碎片。
    • ?另外,G1提供了更多手段,以達到對gc停頓的可控。

    ?堆:

    下面看看G1:

    ?G1收集器堆結構

    • ? heap被劃分為一個個相等的不連續的內存區域(regions), 每個region都有一個分代的角色: eden、survivor、old
    • ?對每個角色的數量并沒有強制限定,也就是說對每種分代內存的大小,可以動態變化
    • ?G1最大的特點就是高效的執行回收,優先去執行哪些大量對象可回收的區域(region)
    • ?G1使用了gc停頓可預測的模型,來滿足用戶設定的gc停頓時間,根據用戶設定的目標時間,G1會自動的選擇哪些region需要清除,一次清除多少個region
    • ?G1從多個region中復制存活的對象,然后集中放入一個region中,同時整理、清除內存(copying收集算法)

    G1 VS CMS

    • ?對比使用mark-sweep的CMS,G1使用的copying算法不會造成內存碎片
    • ?對比Parallel Scavenge(基于copying)、parallel Old收集器(基于mark-compact-sweep), Parallel會對整個區域做整理導致gc停頓時間比較長,而G1只是特定的整理幾個region
    • G1并非一個實時的收集器,與parallel Scavenge一樣,對gc停頓時間的設置并不絕對生效,只是G1有較高的幾率保證不超過設定的gc停頓時間。與之前的gc收集器對比,G1會根據用戶設定的gc停頓時間,智能評估哪幾region需要被回收可以滿足用戶的設定。

    G1重要概念:

    • ?分區(Region): G1采取了不同的策略來解決并行、串行和CMS收集器的碎片、暫停時間不可控等問題---G1將整個的堆分成相同大小的分區(region)每個分區都可能是年輕代也可能是老年代,但是在同一時刻只能屬于某個代。
    • 年輕代、幸存區、老年代這些概念還存在,成為邏輯上的概念,這樣方便復用之前分代框架的邏輯。
    • 在物理上不需要連續,則帶來了額外的好處——有的分區內垃圾對象特別多,有的分區內垃圾對象很少,G1會優先回收垃圾對象特別多的分區,這樣可以花費較少的時間來回收這些分區的垃圾,這也就是G1名字的由來,即首先收集垃圾最多的分區。
    • 新生代其實并不是適用于這種算法的,依然是在新生代滿了的時候,對整個新生代進行回收—— 整個新生代中的對象,要么被回收、要么晉升到老年代,至于新生代也采取分區機制的原因,則是因為這樣跟老年代的策略統一,方便調整代的大小。
    • G1還是一種帶壓縮的收集器,在回收老年代的分區時,是將存活的對象從一個分區拷貝到另一個可用分區,這個拷貝的過程就實現了局部的壓縮。每個分區的大小從1M到32M不等,但是都是2的冥次方。
    • 收集集合(CSet):一組可被回收的分區的集合。在CSet中存活的數據會在GC過程中被移動到另一個可用分區,CSet中的分區可以來自Eden空間、survivor空間、或者老年代。CSet會占用不到整個堆空間的1%大小。

    • 已記憶集合(RSet)每個Region都有一個關聯的Remembered Set。RSet記錄了其他Region中的對象引用本Region中對象的關系,屬于points-into結構(誰引用了我的對象)。RSet的價值在于使得垃圾收集器不需要掃描整個堆找到誰引用了當前分區中的對象,只需要掃描RSet即可。

    如下圖所示,Region1和Region3中的對象都引用了Region2中的對象,因此在Region2的RSet中記錄了這兩個引用。

    ?

    • ?G1 GC則是在points-out的card table之上再加了一層結構來構成points-into RSet:每個region會記錄下到底哪些別的region有指向自己的指針,而這些指針分別在哪些card的范圍內。
    • ?這個RSet其實是一個hash table,key是別的region的起始地址,value是一個集合,里面的元素是card table的index。 舉例來說,如果region A的RSet里有一項的key是region B,value里有index為1234的card,它的意思就是region B的一個card里有引用指向region A。所以對region A來說,該RSet記錄的是points-into的關系;而card table仍然記錄了points-out的關系。
    • ?Snapshot-At-The-Beginning(SATB):SATB是維持并發GC的正確性的一個手段,G1 GC的并發理論基礎就是SATB。SATB是由Taiichi Yuasa為增量式標記清除垃圾收集器設計的一個標記算法。

    • 并發標記是并發多線程的,但并發線程在同一時刻只掃描一個分區。

    G1相對于GMS的優勢:

    1、G1在壓縮空間方面有優勢

    2、G1通過將內存空間分成區域(Region)的方式避免內存碎片問題

    3、Eden, Survivor, Old區不再固定、在內存使用效率上來說更靈活

    4、G1可以通過設置預期停頓時間(Pause Time)來控制垃圾收集時間避免應用雪崩現象(預測模型,統計數據分析)

    5、G1在回收內存后會馬上同時做合并空閑內存的工作、而CMS默認是在STW(stop the world)的時候做

    6、G1會在Young GC中使用、而CMS只能在O區使用

    重要概念:

    • ?每個分區都可能是年輕代也可能是老年代,但是在同一時刻只能屬于某個代。年輕代,幸存區,老年代這些概念還存在,成為邏輯上的概念,這樣方便復用之前分代框架的邏輯。
    • ?在物理上不需要連續,則帶來了額外的好處---有的分區內垃圾對象特別多,有的分區內垃圾對象很少,G1會優先回收垃圾對象特別多的分區,這樣可以花費比較少的時間來回收這些分區的垃圾,這也是G1名字的由來,即首先收集垃圾最多的分區。
    • ?依然是在新生代滿了的時候,對整個新生代進行回收---整個新生代中的對象,要么被回收,要么晉升,至于新生代也采取分區機制的原因,則是因為這樣老年代的策略統一,方便調整代的大小。

    G1的適合場景

    • 服務端多核CPU、JVM內存占用較大的應用(至少大于4G)
    • 應用在運行過程中會產生大量內存碎片、需要經常壓縮空間
    • 想要更可控、可預期的GC停頓周期;防止高并發下應用雪崩現象

    G1 GC模式:

    • G1提供了兩種GC模式,Young GC和Mixed GC,兩種都是Stop The World(STW)的
    • ?Young GC: 選定所有年輕代里的Region。通過控制年輕代的Region個數,即年輕代內存大小,來控制Young GC的時間開銷
    • ?Mixed GC: 選定所有年輕代里的Region,外加根據global concurrent marking統計得出收集收益高的若干老年Region(垃圾對象多的老年代Region).在用戶指定的開銷目標范圍內盡可能選擇收益高的老年代Region
    • ?Mixed GC不是Full GC,它只能回收部分老年代的Region,如果Mixed GC實在無法跟上程序分配內存的速度,導致老年代填滿無法繼續進行Mixed GC,就會使用serial old GC(Full GC)來收集整個GC heap。所以本質上,G1是不提供Full GC的
    • global concurrent marking,它的執行過程類似CMS,但是不同的是,在G1 GC中,它主要是為Mixed GC提供標記服務的,并不是一次GC過程的一個必須環節。

    global concurrent marking的執行過程分為四個步驟:

    初始標記(initial mark,STW)。它標記了從GC Root開始直接可達的對象。
    并發標記(Concurrent Marking)。這個階段從GC Root開始對heap中的對象標記,標記線程與應用程序線程并行執行,并且收集各個Region的存活對象信息。
    重新標記(Remark,STW)。標記那些在并發標記階段發生變化的對象,將被回收。
    清除垃圾(Cleanup)。清除空Region(沒有存活對象的),加入到free list。

    第一階段initial mark是共用了Young GC的暫停,這是因為他們可以復用root scan操作,所以可以說global concurrent marking是伴隨Young GC而發生的。

    第四階段Cleanup只是回收了沒有存活對象的Region,所以它并不需要STW。

    G1在運行過程中的主要模式:

    1. YGC(不同于CMS)

    2. 并發階段

    3. 混合模式

    4. Full GC(一般是G1出現問題時發生)

    注:

    在Eden充滿時觸發,在回收之后所有之前屬于Eden的區塊全變成空白。然后把剩余的存活對象移動到S區。

    什么時候觸發Mixed GC?

    由一些參數控制,另外也控制著哪些老年代Region會被選入CSet(收集集合)

    • G1HeapWastePercent:在global concurrent marking結束之后,我們可以知道old gen regions中有多少空間要被回收,在每次YGC之后和再次發生Mixed GC之前,會檢查垃圾占比是否達到此參數,只有達到了,下次才會發生Mixed GC。
    • G1MixedGCLiveThresholdPercent:old generation region中的存活對象的占比,只有在此參數之下,才會被選入CSet。
    • G1MixedGCCountTarget:一次global concurrent marking之后,最多執行Mixed GC的次數。
    • G1OldCSetRegionThresholdPercent:一次Mixed GC中能被選入CSet的最多old generation region數量

    除了以上的參數,G1 GC相關的其他主要的參數有:

    參數含義
    -XX:G1HeapRegionSize=n設置Region大小,并非最終值
    -XX:MaxGCPauseMillis設置G1收集過程目標時間,默認值200ms,不是硬性條件
    -XX:G1NewSizePercent新生代最小值,默認值5%
    -XX:G1MaxNewSizePercent新生代最大值,默認值60%
    -XX:ParallelGCThreadsSTW期間,并行GC線程數
    -XX:ConcGCThreads=n并發標記階段,并行執行的線程數
    -XX:InitiatingHeapOccupancyPercent設置觸發標記周期的 Java 堆占用率閾值。默認值是45%。這里的java堆占比指的是non_young_capacity_bytes,包括old+humongous

    G1收集概覽:

    • G1算法將堆劃分為若干區域(Region),它仍然屬于分代收集器。不過,這些區域的一部分包含新生代,新生代的垃圾收集依然采用暫停所有應用線程的方式,將存活對象拷貝到老年代或者Survivor空間。老年代也分成很多區域,G1收集器通過將對象從一個區域復制到另一個區域,完成了清理工作。這就意味著,在正常的處理 過程中,G1完成了堆的壓縮(至少是部分堆壓縮),這樣就不會有CMS內存碎片問題的存在。
    • ?在G1中,還有一種特殊的區域,叫Humongous區域。 如果一個對象占用的空間超過了分區容量50%以上,G1收集器就認為這是一個巨型對象。這些巨型對象,默認直接會被分配在年老代,但是如果它是一個短期存在的巨型對象,就會對垃圾收集器造成負面影響。為了解決這個問題,G1劃分了一個Humongous區,它用來專門存放巨型對象。如果一個H區裝不下一個巨型對象,那么G1會尋找連續的H分區來存儲。為了能找到連續的H區,有時候不得不啟動Full GC
    • Young GC主要是對Eden區進行GC,它在Eden空間耗盡時會被觸發。在這種情況下,Eden空間的數據移動到Survivor空間中,如果Survivor空間不夠,Eden空間的部分數據會直接晉升到年老代空間。Survivor區的數據移動到新的Survivor區中,也有部分數據晉升到老年代空間中。最終Eden空間的數據為空,GC停止工作,應用線程繼續執行。
    • 如果僅僅GC 新生代對象,我們如何找到所有的根對象呢? 老年代的所有對象都是根么?那這樣掃描下來會耗費大量的時間。于是,G1引進了RSet的概念。它的全稱是Remembered Set,作用是跟蹤指向某個heap區內的對象引用。

    ??

    • 在CMS中,也有RSet的概念,在老年代中有一塊區域用來記錄指向新生代的引用。這是一種point-out,在進行Young GC時,掃描根時,僅僅需要掃描這一塊區域,而不需要掃描整個老年代。
    • 但在G1中,并沒有使用point-out,這是由于一個分區太小,分區數量太多,如果是用point-out的話,會造成大量的掃描浪費,有些根本不需要GC的分區引用也掃描了。

    • 于是G1中使用point-in來解決。point-in的意思是哪些分區引用了當前分區中的對象。這樣,僅僅將這些對象當做根來掃描就避免了無效的掃描。

    • 由于新生代有多個,那么我們需要在新生代之間記錄引用嗎?這是不必要的,原因在于每次GC時,所有新生代都會被掃描,所以只需要記錄老年代到新生代之間的引用即可。

    • 需要注意的是,如果引用的對象很多,賦值器需要對每個引用做處理,賦值器開銷會很大,為了解決賦值器開銷這個問題,在G1 中又引入了另外一個概念,卡表(Card Table)。一個Card Table將一個分區在邏輯上劃分為固定大小的連續區域,每個區域稱之為卡。卡通常較小,介于128到512字節之間。Card Table通常為字節數組,由Card的索引(即數組下標)來標識每個分區的空間地址

      Young GC 階段:

      階段1:根掃描
      靜態和本地對象被掃描
      階段2:更新RS
      處理dirty card隊列更新RS
      階段3:處理RS
      檢測從年輕代指向年老代的對象
      階段4:對象拷貝
      拷貝存活的對象到survivor/old區域
      階段5:處理引用隊列
      軟引用,弱引用,虛引用處理

      關于G1 Mix GC:?Mix GC不僅進行正常的新生代垃圾收集,同時也回收部分后臺掃描線程標記的老年代分區。

      它的GC步驟分2步:

    • 全局并發標記(global concurrent marking)
    • 拷貝存活對象(evacuation)

    提到并發標記,我們不得不了解并發標記的三色標記算法。它是描述追蹤式回收器的一種有用的方法,利用它可以推演回收器的正確性。 首先,我們將對象分成三種類型的。

    • 黑色:根對象,或者該對象與它的子對象都被掃描
    • 灰色:對象本身被掃描,但還沒掃描完該對象中的子對象
    • 白色:未被掃描對象,掃描完成所有對象之后,最終為白色的為不可達對象,即垃圾對象

    根對象被置為黑色,子對象被置為灰色。

    繼續由灰色遍歷,將已掃描了子對象的對象置為黑色。

    遍歷了所有可達的對象后,所有可達的對象都變成了黑色。不可達的對象即為白色,需要被清理。

    這看起來很美好,但是如果在標記過程中,應用程序也在運行,那么對象的指針就有可能改變。這樣的話,我們就會遇到一個問題:對象丟失問題。

    我們看下面一種情況,當垃圾收集器掃描到下面情況時:

    這時候應用程序執行了以下操作:

    A.c=C

    B.c=null

    這樣,對象的狀態圖變成如下情形:

    這時候垃圾收集器再標記掃描的時候就會下圖成這樣:


    引申SATB

    在G1中,使用的是STAB(snapshot-at-the-beginning)的方式,刪除的時候記錄所有的對象,它有3個步驟:

    1,在開始標記的時候生成一個快照圖標記存活對象

    2,在并發標記的時候所有被改變的對象入隊(在write barrier里把所有舊的引用所指向的對象都變成非白的)

    3,可能存在游離的垃圾,將在下次被收集

    G1混合式回收

    ?這樣,G1到現在可以知道哪些老的分區可回收垃圾最多。 當全局并發標記完成后,在某個時刻,就開始了Mix GC。這些垃圾回收被稱作“混合式”是因為他們不僅僅進行正常的新生代垃圾收集,同時也回收部分后臺掃描線程標記的分區。

    ?混合式GC也是采用的復制的清理策略,當GC完成后,會重新釋放空間。

    G1分代算法:

    為老年代設置分區的目的是老年代里有的分區垃圾多,有的分區垃圾少,這樣在回收的時候可以專注于收集垃圾多的分區,這也是G1名稱的由來。

    不過這個算法并不適合新生代垃圾收集,因為新生代的垃圾收集算法是復制算法,但是新生代也使用了分區機制主要是因為便于代大小的調整。

    SATB:

    SATB的全稱是Snapchat-At-The_Beginning。SATB是維持并發GC的一種手段。G1并發的基礎就是SATB。SATB可以理解成在GC開始之前對堆內存里的對象做一次快照,此時活的對象就認為是活的,從而形成一個對象圖。

    在GC收集的時候,新生代的對象也認為是活的對象,除此之外其他不可達的對象都認為是垃圾對象。除此之外其他不可達的對象都認為是垃圾對象。


    如何找到在GC的過程中分配的對象呢(對象引用的變更,新對象的生成)?

    每個region記錄著兩個top-at-mark-start(TAMS)指針,分別為prevTAMS和nextTAMS。在TAMS以上的對象就是新分配的,因而被視為隱式marked。

    通過這種方式我們就找到了在GC過程中新分配的對象,并把這些對象認為是活的對象。


    解決了對象在GC過程中分配的問題,那么在GC過程中引用發生變化的問題怎么解決呢,

    G1給出的解決辦法是通過Write Barrier。Write Barrier就是對引用字段進行賦值做了環切。通過Write Barrier就可以了解到哪些引用對象發生了什么樣的變化。

    SATB全稱為Snapshot At The Beginning,其要點如下:

    • mark的過程就是遍歷heap標記live object的過程,采用的是三色標記算法,這三種顏色為white(表示還未訪問到)、gray(訪問到但是它用到的引用還沒有完全掃描)、back(訪問到而且其用到的引用已經完全掃描完)。
    • 整個三色標記算法就是從GC roots出發遍歷heap,針對可達對象先標記white為gray,然后再標記gray為black;遍歷完成之后所有可達對象都是balck的,所有white都是可以回收的。
    • SATB僅僅對于在marking開始階段進行“snapshot”(marked all reachable at mark start),但是concurrent的時候并發修改可能造成對象漏標記。
    • 對black新引用了一個white對象,然后又從gray對象中刪除了對該white對象的引用,這樣會造成了該white對象漏標記。
    • 對black新引用了一個white對象,然后從gray對象刪了一個引用該white對象的white對象,這樣也會造成了該white對象漏標記。
    • 對black新引用了一個剛new出來的white對象,沒有其他gray對象引用該white對象,這樣也會造成了該white對象漏標記。
    對于三色算法在concurrent的時候可能產生的漏標記問題,SATB在marking階段中,對于從gray對象移除的目標引用對象標記為gray,對于black引用的新產生的對象標記為black;由于是在開始的時候進行snapshot,因而可能存在Floating Garbage 對象的漏標和誤標:
    其實誤標沒什么關系,頂多造成浮動垃圾,在下次gc還是可以回收的,但是漏標的后果是致命的,把本應該存活的對象給回收了,從而影響的程序的正確性。

    漏標的情況只會發生在白色對象中,且滿足以下任意一個條件:

  • 并發標記時,應用線程給一個黑色對象的引用類型字段賦值了該白色對象
  • 并發標記時,應用線程刪除所有灰色對象到該白色對象的引用
  • 對于第一種情況,利用post-write barrier,記錄所有新增的引用關系,然后根據這些引用關系為根重新掃描一遍

    對于第二種情況,利用pre-write barrier,將所有即將被刪除的引用關系的舊引用記錄下來,最后以這些舊引用為根重新掃描一遍

    停頓預測模型 G1收集器突出表現出來的一點是通過一個停頓預測模型來根據用戶配置的停頓時間來選擇CSet的大小,從而達到用戶期待的應用程序暫停時間。通過-XX:MaxGCPauseMillis參數來設置。這一點有點類似于ParallelScavenge收集器。關于停頓時間的設置并不是越短越好。設置的時間越短意味著每次收集的CSet越小,導致垃圾逐步積累變多,最終不得不退化成Serial GC;停頓時間設置的過長,那么會導致每次都會產生長時間的停頓,影響了程序對外的響應時間。 通過-XX:MaxGCPauseMillis參數來設置。這一點有點類似于ParallelScavenge收集器。關于停頓時間的設置并不是越短越好。設置的時間越短意味著每次收集的CSet越小,導致垃圾逐步積累變多,最終不得不退化成Serial GC;停頓時間設置的過長,那么會導致每次都會產生長時間的停頓,影響了程序對外的響應時間。

    G1的收集模式:

    YoungGC:收集年輕代里的Region
    MixGC:年輕代的所有Region+全局并發標記階段選出的收益高的Region
    無論是YoungGC還是MixGC都只是并發拷貝的階段

    分代G1模式下選擇CSet有兩種子模式,分別對應YoungGC和mixedGC:
    YoungGC:CSet就是所有年輕代里面的Region
    MixedGC:CSet是所有年輕代里的Region加上在全局并發標記階段標記出來的收益高的Region

    G1的運行過程是這樣的,會在Young GC和Mix GC之間不斷的切換運行,同時定期的做全局并發標記,在實在趕不上回收速度的情況下使用Full GC(Serial GC)。

    ?初始標記是搭在YoungGC上執行的,在進行全局并發標記的時候不會做Mix GC,在做Mix GC的時候也不會啟動初始標記階段。當MixGC趕不上對象產生的速度的時候就退化成Full GC,這一點是需要重點調優的地方。

    G1調優實戰:

    1.不斷調優暫停時間指標:

    ?通過XX:MaxGCPauseMillis=x可以設置啟動應用程序暫停的時間,G1在運行的時候會根據這個參數選擇CSet來滿足響應時間的設置。一般情況下這個值設置到100ms或者200ms都是可以的(不同情況下會不一樣),但如果設置成50ms就不太合理。暫停時間設置的太短,就會導致出現G1跟不上垃圾產生的速度。最終退化成Full GC。所以對這個參數的調優是一個持續的過程,逐步調整到最佳狀態。

    2.不要設置新生代和老年代的大小

    ?G1收集器在運行的時候會調整新生代和老年代的大小。通過改變代的大小來調整對象晉升的速度以及晉升年齡,從而達到我們為收集器設置的暫停時間目標。

    ?設置了新生代大小相當于放棄了G1為我們做的自動調優。我們需要做的只是設置整個堆內存的大小,剩下的交給G1自己去分配各個代的大小。

    3. 關注Evacuation Failure

    Evacuation Failure類似于CMS里面的晉升失敗,堆空間的垃圾太多導致無法完成Region之間的拷貝,于是不得不退化成Full GC來做一次全局范圍內的垃圾收集 。

    -XX:MaxGCPauseMillis=200m? ##最大GC停頓時間是200m

    總結

    以上是生活随笔為你收集整理的深入理解JVM虚拟机(总结篇)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    欧美成人家庭影院 | 国产成人精品三级麻豆 | 久久久成人毛片无码 | 啦啦啦www在线观看免费视频 | 色综合久久久无码网中文 | 无码乱肉视频免费大全合集 | 玩弄中年熟妇正在播放 | 中文无码精品a∨在线观看不卡 | 国产绳艺sm调教室论坛 | 人妻中文无码久热丝袜 | 在线а√天堂中文官网 | 亚洲狠狠色丁香婷婷综合 | 国产偷自视频区视频 | 性欧美牲交xxxxx视频 | 亚洲综合在线一区二区三区 | 国产在线精品一区二区三区直播 | 久久精品99久久香蕉国产色戒 | 波多野结衣av一区二区全免费观看 | 国产莉萝无码av在线播放 | 亚洲 a v无 码免 费 成 人 a v | 任你躁国产自任一区二区三区 | 精品一区二区三区波多野结衣 | 全球成人中文在线 | 人人妻人人澡人人爽欧美精品 | 久久视频在线观看精品 | 国产熟妇另类久久久久 | 国产成人一区二区三区在线观看 | 色五月五月丁香亚洲综合网 | 中文字幕人妻丝袜二区 | 国产精品国产自线拍免费软件 | 色综合久久88色综合天天 | 性生交大片免费看l | 荫蒂添的好舒服视频囗交 | 一区二区传媒有限公司 | 亚洲人成无码网www | 国产偷抇久久精品a片69 | 国产成人精品一区二区在线小狼 | 狠狠色欧美亚洲狠狠色www | 中文字幕久久久久人妻 | 亚洲精品欧美二区三区中文字幕 | 国产一精品一av一免费 | 伊人久久大香线蕉av一区二区 | 亚洲自偷自偷在线制服 | ass日本丰满熟妇pics | 久久伊人色av天堂九九小黄鸭 | 亚洲中文字幕无码中字 | 性生交大片免费看l | 国产精品香蕉在线观看 | 国产精品久久国产精品99 | 欧美日韩精品 | 亚洲综合无码久久精品综合 | 精品无码成人片一区二区98 | 西西人体www44rt大胆高清 | 国产成人无码午夜视频在线观看 | 国产明星裸体无码xxxx视频 | 水蜜桃亚洲一二三四在线 | 国产精品-区区久久久狼 | 天堂亚洲免费视频 | 免费网站看v片在线18禁无码 | 久久国产36精品色熟妇 | 一本色道久久综合狠狠躁 | 性史性农村dvd毛片 | 免费播放一区二区三区 | 麻豆国产人妻欲求不满 | 天堂а√在线中文在线 | 国产精品99爱免费视频 | 在线播放亚洲第一字幕 | 国产成人精品一区二区在线小狼 | 人妻少妇精品无码专区动漫 | 亚洲人成网站免费播放 | 性色欲网站人妻丰满中文久久不卡 | 国产欧美熟妇另类久久久 | 天天躁夜夜躁狠狠是什么心态 | 国产一区二区三区四区五区加勒比 | 狠狠cao日日穞夜夜穞av | 中文无码精品a∨在线观看不卡 | 十八禁真人啪啪免费网站 | 日日摸夜夜摸狠狠摸婷婷 | 又色又爽又黄的美女裸体网站 | 久久国产劲爆∧v内射 | 精品国偷自产在线 | 美女张开腿让人桶 | 国产香蕉尹人视频在线 | 欧美老妇交乱视频在线观看 | 国产肉丝袜在线观看 | 欧美 丝袜 自拍 制服 另类 | 亚洲一区二区三区含羞草 | 牛和人交xxxx欧美 | 老头边吃奶边弄进去呻吟 | 久久天天躁狠狠躁夜夜免费观看 | 国产成人精品一区二区在线小狼 | 国产精品二区一区二区aⅴ污介绍 | 无人区乱码一区二区三区 | 国产网红无码精品视频 | 又色又爽又黄的美女裸体网站 | 亚洲精品国偷拍自产在线麻豆 | 荫蒂被男人添的好舒服爽免费视频 | 亚洲成a人片在线观看无码 | 国产明星裸体无码xxxx视频 | 国产色xx群视频射精 | 丁香啪啪综合成人亚洲 | 两性色午夜免费视频 | 久久 国产 尿 小便 嘘嘘 | 国产卡一卡二卡三 | 亚洲精品一区二区三区大桥未久 | 蜜桃无码一区二区三区 | 亚洲一区二区三区 | 国内精品九九久久久精品 | 国产激情艳情在线看视频 | 久久精品成人欧美大片 | 中文字幕无线码 | 欧美阿v高清资源不卡在线播放 | 激情五月综合色婷婷一区二区 | 成人精品天堂一区二区三区 | 国产激情无码一区二区app | 天天躁日日躁狠狠躁免费麻豆 | 激情内射日本一区二区三区 | 亚洲国产精品无码一区二区三区 | 亚洲精品国偷拍自产在线麻豆 | 成人欧美一区二区三区 | 欧美成人午夜精品久久久 | 成年美女黄网站色大免费视频 | 亚洲中文字幕成人无码 | 天天av天天av天天透 | 亚洲中文字幕va福利 | av小次郎收藏 | ass日本丰满熟妇pics | 久久国产精品偷任你爽任你 | 性欧美大战久久久久久久 | 丰满少妇女裸体bbw | 精品国精品国产自在久国产87 | 东北女人啪啪对白 | 国产内射老熟女aaaa | 日日麻批免费40分钟无码 | 真人与拘做受免费视频一 | 偷窥村妇洗澡毛毛多 | 亚洲gv猛男gv无码男同 | 曰韩无码二三区中文字幕 | 99久久婷婷国产综合精品青草免费 | 亚洲 日韩 欧美 成人 在线观看 | 中文字幕人妻无码一夲道 | 少妇太爽了在线观看 | 色一情一乱一伦一区二区三欧美 | 永久免费观看美女裸体的网站 | 四虎影视成人永久免费观看视频 | 无码人妻丰满熟妇区毛片18 | 日韩精品一区二区av在线 | 少妇性l交大片欧洲热妇乱xxx | 国产亚洲日韩欧美另类第八页 | 欧美国产亚洲日韩在线二区 | 又大又黄又粗又爽的免费视频 | 精品国产一区av天美传媒 | 青草视频在线播放 | 中文字幕人成乱码熟女app | 老司机亚洲精品影院无码 | 亚洲自偷精品视频自拍 | 无码人妻精品一区二区三区下载 | 亚洲无人区午夜福利码高清完整版 | 水蜜桃亚洲一二三四在线 | 丰满人妻翻云覆雨呻吟视频 | 激情综合激情五月俺也去 | 露脸叫床粗话东北少妇 | 日韩av激情在线观看 | 日韩av无码中文无码电影 | 乌克兰少妇xxxx做受 | 樱花草在线社区www | 在线观看国产一区二区三区 | ass日本丰满熟妇pics | 日韩 欧美 动漫 国产 制服 | 日本乱人伦片中文三区 | 精品成在人线av无码免费看 | 久久97精品久久久久久久不卡 | 中文字幕亚洲情99在线 | 国产人妻精品一区二区三区不卡 | 日韩人妻无码一区二区三区久久99 | 亚洲欧美精品伊人久久 | 岛国片人妻三上悠亚 | 免费国产成人高清在线观看网站 | 久久久成人毛片无码 | 中文字幕色婷婷在线视频 | 18无码粉嫩小泬无套在线观看 | 高清无码午夜福利视频 | 欧美精品无码一区二区三区 | 色婷婷久久一区二区三区麻豆 | 国产精品久久久久久亚洲毛片 | 欧美人与动性行为视频 | 国产精品嫩草久久久久 | 久久久久亚洲精品中文字幕 | 国产成人综合美国十次 | 波多野结衣一区二区三区av免费 | 国产精品国产三级国产专播 | 国产手机在线αⅴ片无码观看 | 无码av岛国片在线播放 | 国内老熟妇对白xxxxhd | 老熟妇乱子伦牲交视频 | 综合网日日天干夜夜久久 | 成人一在线视频日韩国产 | 国产欧美亚洲精品a | 俺去俺来也www色官网 | 丰满诱人的人妻3 | 人人妻人人澡人人爽精品欧美 | 久久无码专区国产精品s | 人人澡人人透人人爽 | 国产熟女一区二区三区四区五区 | 蜜桃视频韩日免费播放 | 日日鲁鲁鲁夜夜爽爽狠狠 | 亚洲欧美色中文字幕在线 | 国产va免费精品观看 | 装睡被陌生人摸出水好爽 | 老司机亚洲精品影院无码 | 波多野结衣一区二区三区av免费 | 国产农村乱对白刺激视频 | 精品偷拍一区二区三区在线看 | 精品国产乱码久久久久乱码 | 亚洲国产成人a精品不卡在线 | 熟女俱乐部五十路六十路av | 国产精品二区一区二区aⅴ污介绍 | 日本丰满护士爆乳xxxx | 漂亮人妻洗澡被公强 日日躁 | 成人欧美一区二区三区 | 亚洲国产精品久久久天堂 | www国产亚洲精品久久久日本 | 国产精品沙发午睡系列 | 精品国产av色一区二区深夜久久 | 日本www一道久久久免费榴莲 | 暴力强奷在线播放无码 | 国产精品内射视频免费 | 亚洲色偷偷偷综合网 | 久久亚洲中文字幕精品一区 | 天天做天天爱天天爽综合网 | 国产极品视觉盛宴 | 国产高潮视频在线观看 | 午夜精品一区二区三区在线观看 | 欧美一区二区三区 | 国产精品亚洲综合色区韩国 | 欧美日韩一区二区免费视频 | 曰本女人与公拘交酡免费视频 | 亚洲精品国产品国语在线观看 | 伊人久久婷婷五月综合97色 | 亚洲精品午夜国产va久久成人 | 2020久久超碰国产精品最新 | 国产精品免费大片 | 性欧美熟妇videofreesex | 欧美变态另类xxxx | 天天躁日日躁狠狠躁免费麻豆 | 又黄又爽又色的视频 | а√天堂www在线天堂小说 | 激情爆乳一区二区三区 | 欧美老人巨大xxxx做受 | 久久久无码中文字幕久... | 亚洲精品久久久久久久久久久 | 日日鲁鲁鲁夜夜爽爽狠狠 | 少妇人妻偷人精品无码视频 | 婷婷五月综合激情中文字幕 | 人人妻人人澡人人爽欧美一区 | 国模大胆一区二区三区 | 伊人久久大香线蕉亚洲 | 国产日产欧产精品精品app | 久久熟妇人妻午夜寂寞影院 | 九九久久精品国产免费看小说 | 国产成人精品一区二区在线小狼 | 日产精品99久久久久久 | 99精品无人区乱码1区2区3区 | 国内精品久久久久久中文字幕 | 波多野结衣aⅴ在线 | 香港三级日本三级妇三级 | 高潮毛片无遮挡高清免费视频 | 伊人久久婷婷五月综合97色 | 丰腴饱满的极品熟妇 | 中国大陆精品视频xxxx | 精品一区二区不卡无码av | 精品一区二区三区波多野结衣 | 国产精品99久久精品爆乳 | 亚洲中文字幕无码中字 | 久热国产vs视频在线观看 | 久久久久久a亚洲欧洲av冫 | 午夜成人1000部免费视频 | 99久久精品日本一区二区免费 | 亚洲成色在线综合网站 | 成人试看120秒体验区 | 成 人影片 免费观看 | 国产va免费精品观看 | 久久亚洲中文字幕无码 | 精品熟女少妇av免费观看 | 亚洲精品成人av在线 | 成年美女黄网站色大免费全看 | 在线a亚洲视频播放在线观看 | 亚洲人成人无码网www国产 | av无码久久久久不卡免费网站 | 欧美35页视频在线观看 | aⅴ亚洲 日韩 色 图网站 播放 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 思思久久99热只有频精品66 | 亚洲小说春色综合另类 | 久久精品视频在线看15 | 亚洲精品一区二区三区大桥未久 | 野狼第一精品社区 | 波多野结衣乳巨码无在线观看 | 三上悠亚人妻中文字幕在线 | 国产亚洲精品久久久久久久久动漫 | 欧美日韩人成综合在线播放 | 中文字幕日产无线码一区 | 国产无套内射久久久国产 | 国产精品永久免费视频 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 国产午夜精品一区二区三区嫩草 | 成人性做爰aaa片免费看不忠 | 色五月丁香五月综合五月 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 一本久久a久久精品亚洲 | 88国产精品欧美一区二区三区 | 国产精品高潮呻吟av久久4虎 | 九月婷婷人人澡人人添人人爽 | 国产精品久久国产精品99 | 高清国产亚洲精品自在久久 | 日本www一道久久久免费榴莲 | 人妻夜夜爽天天爽三区 | 亚洲小说春色综合另类 | 女人被男人爽到呻吟的视频 | 亚洲另类伦春色综合小说 | 欧美熟妇另类久久久久久不卡 | 久久99精品国产麻豆蜜芽 | 欧美亚洲国产一区二区三区 | 国产成人无码av一区二区 | 日本www一道久久久免费榴莲 | 中文字幕色婷婷在线视频 | 美女扒开屁股让男人桶 | 在线精品亚洲一区二区 | 狠狠色噜噜狠狠狠狠7777米奇 | 国产三级精品三级男人的天堂 | 天天av天天av天天透 | 精品 日韩 国产 欧美 视频 | 又粗又大又硬又长又爽 | 四虎永久在线精品免费网址 | 国产97人人超碰caoprom | 无码人妻精品一区二区三区下载 | 亚洲中文字幕乱码av波多ji | 国产色精品久久人妻 | 人妻少妇精品无码专区动漫 | 狠狠亚洲超碰狼人久久 | 中文字幕乱码人妻无码久久 | 扒开双腿疯狂进出爽爽爽视频 | 青青久在线视频免费观看 | 在线看片无码永久免费视频 | 成人免费视频在线观看 | 久久精品人人做人人综合试看 | 99er热精品视频 | 377p欧洲日本亚洲大胆 | 97无码免费人妻超级碰碰夜夜 | 国产精品18久久久久久麻辣 | 玩弄少妇高潮ⅹxxxyw | 领导边摸边吃奶边做爽在线观看 | 熟女少妇人妻中文字幕 | 日本一区二区更新不卡 | 国产97色在线 | 免 | 清纯唯美经典一区二区 | 无码av免费一区二区三区试看 | 成 人 免费观看网站 | 乱码av麻豆丝袜熟女系列 | 精品国偷自产在线视频 | 男女下面进入的视频免费午夜 | 亚洲精品综合一区二区三区在线 | 人妻与老人中文字幕 | 牲交欧美兽交欧美 | 亚洲成av人片天堂网无码】 | 女人被男人爽到呻吟的视频 | 正在播放老肥熟妇露脸 | 国产乱子伦视频在线播放 | 久久久久久久人妻无码中文字幕爆 | 风流少妇按摩来高潮 | 亚洲一区二区三区国产精华液 | 曰韩无码二三区中文字幕 | 国产九九九九九九九a片 | 亚洲s色大片在线观看 | 99久久久国产精品无码免费 | 亚洲色偷偷偷综合网 | 久久精品中文字幕一区 | 国产成人无码专区 | 高潮毛片无遮挡高清免费 | 无码福利日韩神码福利片 | 国产又爽又猛又粗的视频a片 | 日韩在线不卡免费视频一区 | 99er热精品视频 | 色情久久久av熟女人妻网站 | 成人三级无码视频在线观看 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 国产精品无码mv在线观看 | 99国产精品白浆在线观看免费 | 国产特级毛片aaaaaa高潮流水 | 大地资源中文第3页 | 中文字幕久久久久人妻 | 亚洲国产精品一区二区美利坚 | 亚洲の无码国产の无码影院 | 免费网站看v片在线18禁无码 | 亚洲va欧美va天堂v国产综合 | 久久99精品国产麻豆 | 欧美黑人性暴力猛交喷水 | 亚洲成色在线综合网站 | 影音先锋中文字幕无码 | 国产三级久久久精品麻豆三级 | 国产深夜福利视频在线 | 中文字幕无码日韩专区 | 久久亚洲国产成人精品性色 | 色婷婷欧美在线播放内射 | 久久这里只有精品视频9 | 美女黄网站人色视频免费国产 | 丰满少妇弄高潮了www | 久久久久99精品成人片 | 国产亚洲欧美在线专区 | 亚洲日本一区二区三区在线 | 男女爱爱好爽视频免费看 | 精品偷拍一区二区三区在线看 | 亚洲一区二区三区在线观看网站 | 日本一区二区更新不卡 | 宝宝好涨水快流出来免费视频 | 4hu四虎永久在线观看 | 一本无码人妻在中文字幕免费 | 3d动漫精品啪啪一区二区中 | 亚洲欧美精品aaaaaa片 | 亚洲国产精品久久久久久 | v一区无码内射国产 | 午夜福利一区二区三区在线观看 | 99视频精品全部免费免费观看 | 人人妻人人澡人人爽人人精品浪潮 | 亚洲国产成人a精品不卡在线 | 久久亚洲精品成人无码 | 亚洲午夜福利在线观看 | 日韩人妻系列无码专区 | 国产极品视觉盛宴 | 亲嘴扒胸摸屁股激烈网站 | 国产偷抇久久精品a片69 | 麻豆蜜桃av蜜臀av色欲av | 日本精品久久久久中文字幕 | 国产精品人妻一区二区三区四 | 性欧美疯狂xxxxbbbb | 亚洲人成影院在线观看 | 奇米影视888欧美在线观看 | 欧美人与牲动交xxxx | а√天堂www在线天堂小说 | 亚洲中文无码av永久不收费 | 九月婷婷人人澡人人添人人爽 | 国产明星裸体无码xxxx视频 | 欧美人妻一区二区三区 | 无码国产乱人伦偷精品视频 | 久久99精品久久久久久 | 日韩欧美成人免费观看 | 欧美怡红院免费全部视频 | 亚洲日韩中文字幕在线播放 | 伊人久久大香线蕉午夜 | 亚洲国产精华液网站w | 疯狂三人交性欧美 | 无码成人精品区在线观看 | 国产真实伦对白全集 | 亚洲精品国产精品乱码不卡 | 性欧美牲交xxxxx视频 | 久久zyz资源站无码中文动漫 | 国产热a欧美热a在线视频 | 动漫av网站免费观看 | 国产亚洲人成在线播放 | 女人被男人爽到呻吟的视频 | 性欧美疯狂xxxxbbbb | 国产特级毛片aaaaaa高潮流水 | 无码吃奶揉捏奶头高潮视频 | 亚洲毛片av日韩av无码 | 狠狠色噜噜狠狠狠狠7777米奇 | 色综合视频一区二区三区 | 中文字幕av日韩精品一区二区 | 中文字幕乱码中文乱码51精品 | 骚片av蜜桃精品一区 | 国语精品一区二区三区 | 对白脏话肉麻粗话av | 久久久久免费精品国产 | 性欧美牲交xxxxx视频 | 日韩精品a片一区二区三区妖精 | 久久久久亚洲精品中文字幕 | 国产人妻精品一区二区三区不卡 | 呦交小u女精品视频 | 骚片av蜜桃精品一区 | 性开放的女人aaa片 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 欧美精品无码一区二区三区 | 牲欲强的熟妇农村老妇女 | 亚洲精品一区二区三区大桥未久 | 乱人伦人妻中文字幕无码久久网 | 中文字幕人妻无码一夲道 | 日日噜噜噜噜夜夜爽亚洲精品 | 中文字幕无线码免费人妻 | 丰满人妻被黑人猛烈进入 | 蜜桃视频韩日免费播放 | 中文字幕av日韩精品一区二区 | 亚洲欧美色中文字幕在线 | 久久人人爽人人爽人人片ⅴ | 成人无码影片精品久久久 | 亚洲精品国产精品乱码不卡 | 欧美三级a做爰在线观看 | 亚洲七七久久桃花影院 | 国产一区二区不卡老阿姨 | 小泽玛莉亚一区二区视频在线 | 偷窥村妇洗澡毛毛多 | 俺去俺来也在线www色官网 | 国产成人综合色在线观看网站 | 成人综合网亚洲伊人 | 51国偷自产一区二区三区 | 久久午夜无码鲁丝片 | 国产精品久久久久无码av色戒 | 久久zyz资源站无码中文动漫 | 女人色极品影院 | 日日干夜夜干 | 成年女人永久免费看片 | 乱人伦人妻中文字幕无码 | www国产精品内射老师 | 久久精品国产亚洲精品 | 妺妺窝人体色www婷婷 | 人人爽人人澡人人人妻 | 少妇的肉体aa片免费 | 亚洲国产精品久久久久久 | 国产精品人妻一区二区三区四 | 暴力强奷在线播放无码 | 久热国产vs视频在线观看 | 中国女人内谢69xxxxxa片 | 亚洲成a人片在线观看无码 | 欧美性生交xxxxx久久久 | 熟妇人妻中文av无码 | 无码人妻少妇伦在线电影 | 日本高清一区免费中文视频 | 国产免费久久久久久无码 | 欧美精品无码一区二区三区 | 亚洲乱亚洲乱妇50p | 成人性做爰aaa片免费看 | 两性色午夜视频免费播放 | 在线观看免费人成视频 | 在线成人www免费观看视频 | 秋霞成人午夜鲁丝一区二区三区 | 成人性做爰aaa片免费看不忠 | 久久久久av无码免费网 | 亚洲综合精品香蕉久久网 | 免费无码的av片在线观看 | 精品偷自拍另类在线观看 | 18禁止看的免费污网站 | 欧美熟妇另类久久久久久不卡 | 啦啦啦www在线观看免费视频 | 粗大的内捧猛烈进出视频 | 好屌草这里只有精品 | 久精品国产欧美亚洲色aⅴ大片 | 无码午夜成人1000部免费视频 | 在线精品国产一区二区三区 | 永久免费观看国产裸体美女 | 无码av中文字幕免费放 | 国产午夜福利100集发布 | 欧美真人作爱免费视频 | 亚洲va中文字幕无码久久不卡 | 在教室伦流澡到高潮hnp视频 | 中文字幕久久久久人妻 | 亚洲国产欧美日韩精品一区二区三区 | 人妻尝试又大又粗久久 | 国产乱人伦av在线无码 | 亚洲国产午夜精品理论片 | 欧美精品在线观看 | 欧美freesex黑人又粗又大 | 欧美人与善在线com | 久久精品国产日本波多野结衣 | 亚洲国产欧美在线成人 | 国产九九九九九九九a片 | 亚洲精品久久久久中文第一幕 | 午夜精品久久久内射近拍高清 | 天堂久久天堂av色综合 | 性欧美videos高清精品 | 一个人看的www免费视频在线观看 | 精品国产一区二区三区四区 | 日韩无套无码精品 | 内射爽无广熟女亚洲 | 蜜桃视频插满18在线观看 | 欧美人与禽猛交狂配 | 欧美人与牲动交xxxx | 成熟妇人a片免费看网站 | 成人无码精品1区2区3区免费看 | 亚洲综合伊人久久大杳蕉 | 一区二区三区高清视频一 | 欧美性猛交xxxx富婆 | 强伦人妻一区二区三区视频18 | 精品国产精品久久一区免费式 | 精品夜夜澡人妻无码av蜜桃 | 曰本女人与公拘交酡免费视频 | 午夜福利电影 | 99久久精品日本一区二区免费 | 中文字幕 人妻熟女 | 亚洲欧美精品aaaaaa片 | 国产一区二区三区四区五区加勒比 | 国产无套内射久久久国产 | 东北女人啪啪对白 | 欧美日本精品一区二区三区 | 亚洲精品鲁一鲁一区二区三区 | 无码国产色欲xxxxx视频 | 国产精品永久免费视频 | 水蜜桃亚洲一二三四在线 | av无码久久久久不卡免费网站 | 精品偷自拍另类在线观看 | 人妻尝试又大又粗久久 | 欧美日韩色另类综合 | 亚洲娇小与黑人巨大交 | 欧美三级不卡在线观看 | 曰韩无码二三区中文字幕 | 亚洲爆乳精品无码一区二区三区 | 成年女人永久免费看片 | 午夜性刺激在线视频免费 | 任你躁国产自任一区二区三区 | 粉嫩少妇内射浓精videos | 波多野结衣高清一区二区三区 | 少妇久久久久久人妻无码 | 日韩欧美群交p片內射中文 | 99er热精品视频 | 女人和拘做爰正片视频 | 午夜福利试看120秒体验区 | 欧美午夜特黄aaaaaa片 | 亚洲精品中文字幕久久久久 | 日韩av无码一区二区三区 | 强开小婷嫩苞又嫩又紧视频 | 中国女人内谢69xxxx | 久久精品视频在线看15 | 精品国精品国产自在久国产87 | 永久免费观看美女裸体的网站 | 欧美日韩久久久精品a片 | 精品熟女少妇av免费观看 | 真人与拘做受免费视频一 | 亚洲爆乳无码专区 | 国产69精品久久久久app下载 | 婷婷五月综合激情中文字幕 | 无套内谢的新婚少妇国语播放 | 精品一区二区不卡无码av | 伊人色综合久久天天小片 | 国产精品沙发午睡系列 | 一个人看的www免费视频在线观看 | 国产亚洲精品久久久久久 | 精品无码国产自产拍在线观看蜜 | 夜夜高潮次次欢爽av女 | 国产欧美熟妇另类久久久 | 国产精品无码一区二区桃花视频 | 在线视频网站www色 | 日产精品99久久久久久 | 国产精品高潮呻吟av久久 | 欧美日韩久久久精品a片 | 欧美老熟妇乱xxxxx | 性欧美牲交xxxxx视频 | 中国女人内谢69xxxx | 免费视频欧美无人区码 | 欧美熟妇另类久久久久久多毛 | 国产精品视频免费播放 | 欧美精品国产综合久久 | 中文字幕+乱码+中文字幕一区 | 国产免费无码一区二区视频 | а天堂中文在线官网 | 亚洲欧美中文字幕5发布 | 精品欧美一区二区三区久久久 | 美女扒开屁股让男人桶 | 九九综合va免费看 | 日本xxxx色视频在线观看免费 | 国产亚洲人成a在线v网站 | 国产成人综合色在线观看网站 | 国产激情综合五月久久 | 国语精品一区二区三区 | 亚洲理论电影在线观看 | 精品一区二区三区无码免费视频 | 国产成人无码av片在线观看不卡 | 四十如虎的丰满熟妇啪啪 | 熟妇女人妻丰满少妇中文字幕 | 98国产精品综合一区二区三区 | 久久久婷婷五月亚洲97号色 | 免费观看的无遮挡av | 亚洲成a人片在线观看日本 | 欧美亚洲国产一区二区三区 | 久久久精品成人免费观看 | 宝宝好涨水快流出来免费视频 | 奇米影视7777久久精品 | 丰满岳乱妇在线观看中字无码 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 亚洲色大成网站www国产 | 婷婷综合久久中文字幕蜜桃三电影 | 国产亚洲精品久久久久久大师 | 欧美第一黄网免费网站 | 欧美 亚洲 国产 另类 | 亚无码乱人伦一区二区 | 亚洲欧美精品伊人久久 | 国产精品第一区揄拍无码 | 欧美激情一区二区三区成人 | 国产欧美精品一区二区三区 | 东京热一精品无码av | 天天拍夜夜添久久精品大 | 亚洲aⅴ无码成人网站国产app | 亚洲成av人综合在线观看 | 日韩少妇内射免费播放 | 无码一区二区三区在线观看 | 激情亚洲一区国产精品 | 日韩亚洲欧美精品综合 | 内射巨臀欧美在线视频 | 水蜜桃色314在线观看 | 中文字幕 亚洲精品 第1页 | 搡女人真爽免费视频大全 | 俺去俺来也www色官网 | 麻花豆传媒剧国产免费mv在线 | 久久综合久久自在自线精品自 | 欧美性黑人极品hd | 爱做久久久久久 | 亚洲中文无码av永久不收费 | 又紧又大又爽精品一区二区 | 中文字幕无码视频专区 | 东京无码熟妇人妻av在线网址 | 亚洲精品美女久久久久久久 | 一本无码人妻在中文字幕免费 | 国产亚洲日韩欧美另类第八页 | 最新国产麻豆aⅴ精品无码 | 精品一区二区三区波多野结衣 | 欧美成人午夜精品久久久 | 亚洲一区二区观看播放 | 国产精品自产拍在线观看 | 国产午夜精品一区二区三区嫩草 | 中文字幕无线码 | 国产av一区二区三区最新精品 | 日本肉体xxxx裸交 | 国产成人无码av片在线观看不卡 | 日本又色又爽又黄的a片18禁 | 亚洲狠狠色丁香婷婷综合 | 捆绑白丝粉色jk震动捧喷白浆 | 亚洲码国产精品高潮在线 | 日日噜噜噜噜夜夜爽亚洲精品 | 天堂无码人妻精品一区二区三区 | 久久亚洲日韩精品一区二区三区 | 激情亚洲一区国产精品 | 99久久精品无码一区二区毛片 | 婷婷五月综合激情中文字幕 | 丁香啪啪综合成人亚洲 | 无码人妻丰满熟妇区毛片18 | 亚洲中文字幕无码一久久区 | 亚洲国产精品一区二区第一页 | 亚洲狠狠色丁香婷婷综合 | 人人爽人人澡人人高潮 | 无码午夜成人1000部免费视频 | 精品久久久无码人妻字幂 | 丰满人妻翻云覆雨呻吟视频 | 曰韩无码二三区中文字幕 | 欧美丰满熟妇xxxx性ppx人交 | 自拍偷自拍亚洲精品被多人伦好爽 | 岛国片人妻三上悠亚 | 亚洲区欧美区综合区自拍区 | 亚洲精品一区三区三区在线观看 | 日韩无码专区 | 久久综合久久自在自线精品自 | 久久精品国产一区二区三区肥胖 | 日本丰满护士爆乳xxxx | 狂野欧美激情性xxxx | 久久午夜无码鲁丝片 | 精品久久久久久人妻无码中文字幕 | 国产热a欧美热a在线视频 | 亚洲一区二区三区在线观看网站 | 国产口爆吞精在线视频 | 最新版天堂资源中文官网 | 精品国偷自产在线视频 | 国产精品.xx视频.xxtv | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 中文字幕中文有码在线 | 国产9 9在线 | 中文 | 亚洲一区二区三区无码久久 | 少妇被粗大的猛进出69影院 | 97精品人妻一区二区三区香蕉 | 久久99精品国产麻豆 | 特级做a爰片毛片免费69 | 亚洲爆乳精品无码一区二区三区 | 玩弄人妻少妇500系列视频 | a在线观看免费网站大全 | 高潮毛片无遮挡高清免费 | 精品水蜜桃久久久久久久 | 熟妇人妻无码xxx视频 | 人妻少妇精品无码专区二区 | 成年美女黄网站色大免费全看 | 在线播放亚洲第一字幕 | 熟妇女人妻丰满少妇中文字幕 | 黑人大群体交免费视频 | 中文字幕无码人妻少妇免费 | 中文字幕无码人妻少妇免费 | 男女下面进入的视频免费午夜 | 精品无人区无码乱码毛片国产 | 亚洲自偷自偷在线制服 | 国产精品久久国产三级国 | 欧美日韩亚洲国产精品 | 给我免费的视频在线观看 | 国产成人av免费观看 | 熟女少妇人妻中文字幕 | 国产精品亚洲а∨无码播放麻豆 | 国产精品香蕉在线观看 | 国产午夜手机精彩视频 | 好男人www社区 | 国产午夜手机精彩视频 | 激情内射亚州一区二区三区爱妻 | 久久无码专区国产精品s | 高潮毛片无遮挡高清免费视频 | 玩弄少妇高潮ⅹxxxyw | 国产国语老龄妇女a片 | 欧美老熟妇乱xxxxx | 少妇无码av无码专区在线观看 | 亚洲aⅴ无码成人网站国产app | 一本无码人妻在中文字幕免费 | 日韩欧美中文字幕公布 | 欧美国产日韩久久mv | 2020最新国产自产精品 | 无码精品国产va在线观看dvd | 久久99精品国产麻豆蜜芽 | 全球成人中文在线 | 日本高清一区免费中文视频 | 精品无码国产自产拍在线观看蜜 | 少妇太爽了在线观看 | 国产人成高清在线视频99最全资源 | 久久精品99久久香蕉国产色戒 | 亚洲中文字幕久久无码 | 国产精品99爱免费视频 | 国产欧美亚洲精品a | 东京热无码av男人的天堂 | 国产精品美女久久久久av爽李琼 | 少妇太爽了在线观看 | 又大又紧又粉嫩18p少妇 | 成人女人看片免费视频放人 | 久久www免费人成人片 | 亚洲精品鲁一鲁一区二区三区 | 国产在线aaa片一区二区99 | 女人被男人爽到呻吟的视频 | 啦啦啦www在线观看免费视频 | 人人妻人人澡人人爽人人精品 | 亚洲午夜福利在线观看 | 欧美 丝袜 自拍 制服 另类 | 精品久久8x国产免费观看 | 丝袜 中出 制服 人妻 美腿 | 在线a亚洲视频播放在线观看 | 中文字幕无码av波多野吉衣 | 久久99精品久久久久婷婷 | a国产一区二区免费入口 | 日韩精品一区二区av在线 | 波多野结衣一区二区三区av免费 | 国产综合久久久久鬼色 | 国产成人精品无码播放 | 一区二区三区高清视频一 | 狠狠色噜噜狠狠狠7777奇米 | 澳门永久av免费网站 | 亚洲一区二区三区国产精华液 | 东京热无码av男人的天堂 | 国内精品一区二区三区不卡 | 欧美性生交xxxxx久久久 | 狠狠色噜噜狠狠狠狠7777米奇 | 国产精品久久久久久亚洲影视内衣 | 欧美乱妇无乱码大黄a片 | 亚洲精品国产品国语在线观看 | 捆绑白丝粉色jk震动捧喷白浆 | 狠狠躁日日躁夜夜躁2020 | 青青青手机频在线观看 | 精品人妻中文字幕有码在线 | 亚洲人交乣女bbw | 亚洲乱亚洲乱妇50p | 国产又爽又猛又粗的视频a片 | 国产成人无码a区在线观看视频app | а√资源新版在线天堂 | 国精产品一区二区三区 | 亚洲热妇无码av在线播放 | 少妇激情av一区二区 | 黑人粗大猛烈进出高潮视频 | 中文字幕无码av激情不卡 | 亚洲国产精华液网站w | 丰满妇女强制高潮18xxxx | 亚洲一区二区三区无码久久 | 精品国产一区二区三区四区在线看 | 久久久精品成人免费观看 | 久久天天躁狠狠躁夜夜免费观看 | 亚洲自偷自偷在线制服 | 一本精品99久久精品77 | 国产成人综合色在线观看网站 | 久久精品99久久香蕉国产色戒 | 久久亚洲中文字幕精品一区 | 欧美 丝袜 自拍 制服 另类 | 99riav国产精品视频 | 正在播放老肥熟妇露脸 | 自拍偷自拍亚洲精品被多人伦好爽 | 亚洲欧洲日本综合aⅴ在线 | 白嫩日本少妇做爰 | 亚洲 激情 小说 另类 欧美 | 中文字幕乱妇无码av在线 | 婷婷综合久久中文字幕蜜桃三电影 | 国产热a欧美热a在线视频 | 波多野结衣av一区二区全免费观看 | 暴力强奷在线播放无码 | 国产亚洲日韩欧美另类第八页 | 亚洲精品无码国产 | 欧美人与禽zoz0性伦交 | 国产成人人人97超碰超爽8 | 黑人巨大精品欧美黑寡妇 | 国产内射老熟女aaaa | 妺妺窝人体色www在线小说 | 久久久久久久人妻无码中文字幕爆 | 天堂一区人妻无码 | 熟妇人妻无乱码中文字幕 | 久久国产36精品色熟妇 | 1000部夫妻午夜免费 | 日韩精品成人一区二区三区 | 樱花草在线社区www | 成人无码精品1区2区3区免费看 | 狂野欧美性猛xxxx乱大交 | aa片在线观看视频在线播放 | 成年美女黄网站色大免费视频 | 女人被男人躁得好爽免费视频 | 精品水蜜桃久久久久久久 | 波多野结衣乳巨码无在线观看 | 欧美精品免费观看二区 | 国产成人综合色在线观看网站 | 日韩成人一区二区三区在线观看 | 国内综合精品午夜久久资源 | 狂野欧美激情性xxxx | 欧美日韩在线亚洲综合国产人 | 欧美高清在线精品一区 | 国产精品久久久久久亚洲毛片 | 成人精品视频一区二区三区尤物 | 中文字幕亚洲情99在线 | 国产高清av在线播放 | 日韩精品无码一区二区中文字幕 | 无码人妻少妇伦在线电影 | 久久久久99精品国产片 | 无遮挡国产高潮视频免费观看 | 亚洲理论电影在线观看 | 亚洲国产精品一区二区第一页 | 亚洲理论电影在线观看 | 亚洲乱码日产精品bd | 免费无码av一区二区 | 草草网站影院白丝内射 | 国产亚洲精品久久久久久国模美 | 亚洲精品午夜国产va久久成人 | 午夜男女很黄的视频 | 成人影院yy111111在线观看 | 中文字幕乱码中文乱码51精品 | 又粗又大又硬毛片免费看 | 中文字幕无码热在线视频 | 国产真实伦对白全集 | 国产精品第一国产精品 | 亚洲人亚洲人成电影网站色 | 无码人妻黑人中文字幕 | 国内精品人妻无码久久久影院蜜桃 | 久久综合激激的五月天 | 无码人妻精品一区二区三区下载 | 无码人妻少妇伦在线电影 | 精品日本一区二区三区在线观看 | 无套内谢的新婚少妇国语播放 | 天堂а√在线地址中文在线 | 精品日本一区二区三区在线观看 | 人妻中文无码久热丝袜 | 亚洲成a人片在线观看日本 | 伊人久久大香线蕉亚洲 | 亚洲中文字幕乱码av波多ji | 国产精品.xx视频.xxtv | 国产肉丝袜在线观看 | 国产超碰人人爽人人做人人添 | 国产成人综合美国十次 | 正在播放东北夫妻内射 | 免费国产成人高清在线观看网站 | 欧美黑人巨大xxxxx | 丰满妇女强制高潮18xxxx | 欧美变态另类xxxx | 1000部啪啪未满十八勿入下载 | 最近的中文字幕在线看视频 | 荫蒂被男人添的好舒服爽免费视频 | 亚洲综合在线一区二区三区 | 毛片内射-百度 | 3d动漫精品啪啪一区二区中 | 国产办公室秘书无码精品99 | 欧美日韩一区二区三区自拍 | 漂亮人妻洗澡被公强 日日躁 | 爆乳一区二区三区无码 | 国产成人无码a区在线观看视频app | 国产亚洲精品精品国产亚洲综合 | 亚洲a无码综合a国产av中文 | 欧美亚洲国产一区二区三区 | 久久www免费人成人片 | 欧美国产日产一区二区 | 欧美丰满熟妇xxxx性ppx人交 | 亚洲国产欧美国产综合一区 | 中文字幕乱码亚洲无线三区 | 久久综合网欧美色妞网 | 日日碰狠狠躁久久躁蜜桃 | 1000部夫妻午夜免费 | 国产av剧情md精品麻豆 | 麻豆国产人妻欲求不满 | 日本肉体xxxx裸交 | 亚洲中文字幕无码一久久区 | 欧美丰满熟妇xxxx | 少妇被粗大的猛进出69影院 | 国产精品无码久久av | 亚洲一区二区三区香蕉 | 久热国产vs视频在线观看 | 福利一区二区三区视频在线观看 | 在线播放免费人成毛片乱码 | 久久婷婷五月综合色国产香蕉 | 午夜精品一区二区三区在线观看 | 亚洲精品一区二区三区大桥未久 | 一本无码人妻在中文字幕免费 | 天天做天天爱天天爽综合网 | 久久精品人人做人人综合 | 在线播放亚洲第一字幕 | 国产人妖乱国产精品人妖 | 欧美成人高清在线播放 | 在线视频网站www色 | 国产欧美熟妇另类久久久 | 亚洲成av人片在线观看无码不卡 | 国产成人精品三级麻豆 | aa片在线观看视频在线播放 | 波多野42部无码喷潮在线 | 国产精品嫩草久久久久 | 久久国产36精品色熟妇 | 宝宝好涨水快流出来免费视频 | 亚洲一区二区三区香蕉 | 日韩成人一区二区三区在线观看 | 亚洲热妇无码av在线播放 | 国产97人人超碰caoprom | 久久久中文久久久无码 | 国产av人人夜夜澡人人爽麻豆 | 未满成年国产在线观看 | 日韩精品乱码av一区二区 | 日本一区二区三区免费高清 | 免费看少妇作爱视频 | 国内少妇偷人精品视频免费 | 东京热无码av男人的天堂 | 天干天干啦夜天干天2017 | 老子影院午夜精品无码 | 国产麻豆精品精东影业av网站 | 亚洲中文字幕久久无码 | 久久久精品成人免费观看 | 国产黄在线观看免费观看不卡 | 国产又爽又黄又刺激的视频 | 国产精品人妻一区二区三区四 | 又紧又大又爽精品一区二区 | 日韩成人一区二区三区在线观看 | 亚洲色www成人永久网址 | 中国女人内谢69xxxxxa片 | 日韩人妻无码中文字幕视频 | 人人爽人人澡人人人妻 | 一本色道久久综合狠狠躁 | 久久久国产精品无码免费专区 | 一本大道伊人av久久综合 | 国产一区二区三区日韩精品 | 亚洲中文字幕va福利 | 亚洲人亚洲人成电影网站色 | 2020久久超碰国产精品最新 | 国产成人人人97超碰超爽8 | 无遮无挡爽爽免费视频 | 久久99热只有频精品8 | 无码午夜成人1000部免费视频 | 疯狂三人交性欧美 | 亚洲自偷自拍另类第1页 | 亚洲午夜久久久影院 | 亚洲s码欧洲m码国产av | 夜夜高潮次次欢爽av女 | 无码午夜成人1000部免费视频 | 日韩av激情在线观看 | 俺去俺来也在线www色官网 | 综合激情五月综合激情五月激情1 | 一个人看的www免费视频在线观看 | 久久精品一区二区三区四区 | 国产成人综合美国十次 | 人人妻人人澡人人爽人人精品 | a片免费视频在线观看 | 国内揄拍国内精品少妇国语 | 国产在线精品一区二区高清不卡 | 国产黑色丝袜在线播放 | 亚洲精品一区二区三区大桥未久 | 国产午夜亚洲精品不卡下载 | 又湿又紧又大又爽a视频国产 | 国产舌乚八伦偷品w中 | 亚洲国产成人av在线观看 | 成人女人看片免费视频放人 | 亚洲国产精品一区二区美利坚 | 午夜成人1000部免费视频 | 成 人 网 站国产免费观看 | 成人精品视频一区二区 | 97久久国产亚洲精品超碰热 | 日韩欧美群交p片內射中文 | 在线播放免费人成毛片乱码 | 又黄又爽又色的视频 | 动漫av一区二区在线观看 | 日日躁夜夜躁狠狠躁 | 特黄特色大片免费播放器图片 | 亚洲一区二区三区播放 | 国产激情精品一区二区三区 | 国内精品久久毛片一区二区 | 国产av无码专区亚洲a∨毛片 | 久久国产精品偷任你爽任你 | 99久久婷婷国产综合精品青草免费 | 玩弄少妇高潮ⅹxxxyw | 久久午夜无码鲁丝片 | 亚洲国产精品一区二区美利坚 | 无码午夜成人1000部免费视频 | 给我免费的视频在线观看 | 国产亚洲欧美在线专区 | 久9re热视频这里只有精品 | 亚洲人成网站色7799 | 亚洲日本va午夜在线电影 | 黄网在线观看免费网站 | 国产人妻大战黑人第1集 | 国产在线精品一区二区高清不卡 | 又湿又紧又大又爽a视频国产 | 人妻与老人中文字幕 | 久久久久成人精品免费播放动漫 | 午夜熟女插插xx免费视频 | www国产精品内射老师 | 中国大陆精品视频xxxx | 伊人久久婷婷五月综合97色 | 伊人久久大香线蕉av一区二区 | 夜夜躁日日躁狠狠久久av | 亚洲性无码av中文字幕 | 97se亚洲精品一区 | a在线观看免费网站大全 | 国产明星裸体无码xxxx视频 | 久久国内精品自在自线 | 300部国产真实乱 | 国产在线aaa片一区二区99 | 无码人妻精品一区二区三区不卡 | 久久国产精品精品国产色婷婷 | 色一情一乱一伦 | 日韩少妇白浆无码系列 | 特黄特色大片免费播放器图片 | 国产国产精品人在线视 | 国产激情精品一区二区三区 | 欧美变态另类xxxx | 精品国产乱码久久久久乱码 | 中文字幕无码人妻少妇免费 | 午夜精品久久久久久久 | 啦啦啦www在线观看免费视频 | 两性色午夜免费视频 | 久久亚洲国产成人精品性色 | 成人欧美一区二区三区 | 亚洲成av人综合在线观看 | 亚洲区欧美区综合区自拍区 | 国产黑色丝袜在线播放 | 少妇性l交大片欧洲热妇乱xxx | 真人与拘做受免费视频一 | 中文字幕乱妇无码av在线 | 精品久久久无码人妻字幂 | 天天燥日日燥 | 内射爽无广熟女亚洲 | 色综合久久中文娱乐网 | 成人免费视频视频在线观看 免费 | 精品一区二区不卡无码av | 国产片av国语在线观看 | 日日摸天天摸爽爽狠狠97 | 亚洲中文字幕在线观看 | 亚洲の无码国产の无码影院 | 理论片87福利理论电影 | 精品少妇爆乳无码av无码专区 | 国产麻豆精品精东影业av网站 | 久久精品人人做人人综合试看 | 大乳丰满人妻中文字幕日本 | 国产成人无码专区 | 国产成人精品久久亚洲高清不卡 | 人妻与老人中文字幕 | 在线看片无码永久免费视频 | 国产一区二区三区精品视频 | 亚洲国产午夜精品理论片 | 免费视频欧美无人区码 | 自拍偷自拍亚洲精品10p | 国产午夜精品一区二区三区嫩草 | 偷窥日本少妇撒尿chinese | 久久精品人人做人人综合 | 国内老熟妇对白xxxxhd | 少妇无码一区二区二三区 | 日韩在线不卡免费视频一区 | 99久久亚洲精品无码毛片 | aa片在线观看视频在线播放 | 亚洲无人区午夜福利码高清完整版 | 动漫av网站免费观看 | 亚洲の无码国产の无码影院 | 红桃av一区二区三区在线无码av | 日日摸日日碰夜夜爽av | 无码av岛国片在线播放 | 国产午夜精品一区二区三区嫩草 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 亚洲欧美日韩成人高清在线一区 | 国产免费无码一区二区视频 | 亚洲日本va午夜在线电影 | 少妇高潮喷潮久久久影院 | 又湿又紧又大又爽a视频国产 | 国产激情艳情在线看视频 | 女人被男人爽到呻吟的视频 | 最新版天堂资源中文官网 | 国产激情无码一区二区app | 亚洲色大成网站www国产 | 国精产品一区二区三区 | 香港三级日本三级妇三级 | 成人免费视频一区二区 | 久久99热只有频精品8 | 亚洲 欧美 激情 小说 另类 | 亚洲爆乳精品无码一区二区三区 | 亚洲一区二区三区在线观看网站 | 草草网站影院白丝内射 | 76少妇精品导航 | 国产电影无码午夜在线播放 | 久久综合给合久久狠狠狠97色 | 精品无码成人片一区二区98 | 亚洲国产精品成人久久蜜臀 | 国产成人无码av在线影院 | а√资源新版在线天堂 | 久久精品国产精品国产精品污 | 欧美人与物videos另类 | 好男人社区资源 | 久久精品国产一区二区三区肥胖 | 精品国产麻豆免费人成网站 | 18精品久久久无码午夜福利 | 2019nv天堂香蕉在线观看 | 精品国产青草久久久久福利 | 六月丁香婷婷色狠狠久久 | 内射白嫩少妇超碰 | 色欲av亚洲一区无码少妇 | a片免费视频在线观看 | 国产无套内射久久久国产 | 无码av免费一区二区三区试看 | 亚洲一区二区三区四区 | 麻豆国产97在线 | 欧洲 | 日本欧美一区二区三区乱码 | 日本丰满熟妇videos | 国产内射老熟女aaaa | 蜜桃无码一区二区三区 | 露脸叫床粗话东北少妇 | 亚洲 a v无 码免 费 成 人 a v | 十八禁视频网站在线观看 | 欧美性生交活xxxxxdddd | 美女扒开屁股让男人桶 | 日日碰狠狠丁香久燥 | 无码纯肉视频在线观看 | 国产精华av午夜在线观看 | 88国产精品欧美一区二区三区 | 久久综合久久自在自线精品自 | 九九热爱视频精品 | 一个人看的www免费视频在线观看 | a在线观看免费网站大全 | 精品久久久中文字幕人妻 | 亚洲の无码国产の无码步美 | 国产办公室秘书无码精品99 | 亚洲一区二区三区在线观看网站 | 国产精品亚洲lv粉色 | 国产黄在线观看免费观看不卡 | 人妻无码久久精品人妻 | 无码人妻丰满熟妇区毛片18 | 午夜成人1000部免费视频 | 麻豆国产97在线 | 欧洲 | 丝袜美腿亚洲一区二区 | 国产人妻精品午夜福利免费 | 在线观看国产午夜福利片 | 欧美日韩在线亚洲综合国产人 | 国产精品国产自线拍免费软件 | 一区二区三区乱码在线 | 欧洲 | 丰满人妻一区二区三区免费视频 | 大屁股大乳丰满人妻 | 无码中文字幕色专区 | 无码av最新清无码专区吞精 | 国内少妇偷人精品视频 | 无码播放一区二区三区 | 丰满妇女强制高潮18xxxx | 亚洲综合色区中文字幕 | √天堂中文官网8在线 | 99久久久无码国产精品免费 | 青青久在线视频免费观看 | 亚洲欧美国产精品专区久久 | 丝袜人妻一区二区三区 | 亚洲精品久久久久久久久久久 | 任你躁国产自任一区二区三区 | 精品日本一区二区三区在线观看 | 精品国偷自产在线视频 | 牲欲强的熟妇农村老妇女 | 日本熟妇乱子伦xxxx | 久久亚洲中文字幕精品一区 | 丰满肥臀大屁股熟妇激情视频 | 亚洲小说图区综合在线 | 亚洲人交乣女bbw | 国产精华av午夜在线观看 | 四虎国产精品免费久久 | 午夜无码人妻av大片色欲 | 国产av人人夜夜澡人人爽麻豆 | 亚洲第一网站男人都懂 | 国产人妻久久精品二区三区老狼 | 无码国内精品人妻少妇 | 色五月丁香五月综合五月 | 婷婷六月久久综合丁香 | 亚洲日韩av一区二区三区中文 | 国产精品毛多多水多 | 性欧美疯狂xxxxbbbb | 中文字幕中文有码在线 | 亚洲欧美综合区丁香五月小说 | 色五月丁香五月综合五月 | 精品久久综合1区2区3区激情 | 亚欧洲精品在线视频免费观看 | 人妻互换免费中文字幕 | 学生妹亚洲一区二区 | 乱人伦中文视频在线观看 | 丰腴饱满的极品熟妇 | 在线观看国产一区二区三区 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 国产人妻精品一区二区三区 | 男人扒开女人内裤强吻桶进去 | 国产农村乱对白刺激视频 | 女人和拘做爰正片视频 | 亚洲а∨天堂久久精品2021 | 日韩精品成人一区二区三区 | 国产在热线精品视频 | 小sao货水好多真紧h无码视频 | 久久久中文久久久无码 | 成人女人看片免费视频放人 | 国内综合精品午夜久久资源 | 377p欧洲日本亚洲大胆 | 成人精品一区二区三区中文字幕 | 亚洲中文字幕无码一久久区 | 人人妻人人澡人人爽欧美精品 | 久久国产精品偷任你爽任你 | 久久精品国产一区二区三区 | 久久国语露脸国产精品电影 | 亚洲熟女一区二区三区 | 国产亚洲人成在线播放 | 国产精品对白交换视频 | a在线观看免费网站大全 | 99久久人妻精品免费一区 | 97人妻精品一区二区三区 | 免费网站看v片在线18禁无码 | 97无码免费人妻超级碰碰夜夜 | 黑人大群体交免费视频 | 久久久久久久女国产乱让韩 | 无码人妻黑人中文字幕 | 亚洲成av人片在线观看无码不卡 | 蜜臀av在线播放 久久综合激激的五月天 | 国产三级精品三级男人的天堂 | 伊在人天堂亚洲香蕉精品区 | 一个人看的www免费视频在线观看 | 亚洲一区二区三区香蕉 | 丰腴饱满的极品熟妇 | 久久精品丝袜高跟鞋 | 美女毛片一区二区三区四区 | 亚洲乱亚洲乱妇50p | 国产热a欧美热a在线视频 | 又粗又大又硬又长又爽 | 亚洲精品国产a久久久久久 | 窝窝午夜理论片影院 | 精品久久久中文字幕人妻 | www成人国产高清内射 | 国产午夜无码视频在线观看 | 欧美激情内射喷水高潮 | 久久综合网欧美色妞网 | 亚洲日本在线电影 | 在线观看国产午夜福利片 | 人妻人人添人妻人人爱 | 国产精品高潮呻吟av久久4虎 | 国产精品亚洲а∨无码播放麻豆 | 日韩亚洲欧美精品综合 | 国产香蕉97碰碰久久人人 | 天堂无码人妻精品一区二区三区 | 国产欧美精品一区二区三区 | 一本色道久久综合亚洲精品不卡 | 日本熟妇人妻xxxxx人hd | 丰满人妻被黑人猛烈进入 | 亚洲国产一区二区三区在线观看 | 婷婷色婷婷开心五月四房播播 | 97夜夜澡人人爽人人喊中国片 | 国产97人人超碰caoprom | 思思久久99热只有频精品66 | 免费人成在线视频无码 | 久久久久免费看成人影片 | 98国产精品综合一区二区三区 | 青青青手机频在线观看 | 无码人妻久久一区二区三区不卡 | 国产精品亚洲一区二区三区喷水 | 久久国产精品精品国产色婷婷 | 中文字幕精品av一区二区五区 | 无套内谢的新婚少妇国语播放 | 98国产精品综合一区二区三区 | 性欧美videos高清精品 | 爱做久久久久久 | 国产精品久久福利网站 | 久久精品无码一区二区三区 | 亚洲国产精品一区二区美利坚 | 国产美女精品一区二区三区 | 国产一区二区不卡老阿姨 | 日本乱偷人妻中文字幕 | 黄网在线观看免费网站 | 亚洲春色在线视频 | 色五月五月丁香亚洲综合网 | 国产97人人超碰caoprom | 丰满少妇高潮惨叫视频 | 国产无套粉嫩白浆在线 | 台湾无码一区二区 | 亚洲精品久久久久avwww潮水 | 色一情一乱一伦一区二区三欧美 | 国产激情精品一区二区三区 | 亚洲自偷自拍另类第1页 | 丰满少妇熟乱xxxxx视频 | 亚洲大尺度无码无码专区 | 亚洲 a v无 码免 费 成 人 a v | 久久国产劲爆∧v内射 | 日韩人妻无码中文字幕视频 | 又大又硬又爽免费视频 | 妺妺窝人体色www婷婷 | 婷婷五月综合激情中文字幕 | 老熟妇仑乱视频一区二区 | 亚洲熟妇色xxxxx亚洲 | 内射后入在线观看一区 | 国产精品成人av在线观看 | 国产片av国语在线观看 | 人妻体内射精一区二区三四 | 久久精品国产99久久6动漫 | 国产精品久久福利网站 | 麻豆国产人妻欲求不满 | 亚洲爆乳无码专区 | 国产办公室秘书无码精品99 | 又大又紧又粉嫩18p少妇 | 亚洲中文字幕久久无码 | 欧美人与动性行为视频 | 福利一区二区三区视频在线观看 | 国产成人无码区免费内射一片色欲 | 青青青手机频在线观看 | 欧美性生交xxxxx久久久 | 亚洲s码欧洲m码国产av | 久久久久se色偷偷亚洲精品av | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 亚洲成熟女人毛毛耸耸多 | 扒开双腿疯狂进出爽爽爽视频 | 丝袜 中出 制服 人妻 美腿 | 鲁大师影院在线观看 | 东京无码熟妇人妻av在线网址 | 久久成人a毛片免费观看网站 | 曰本女人与公拘交酡免费视频 | 日日摸日日碰夜夜爽av | 77777熟女视频在线观看 а天堂中文在线官网 | 久久99久久99精品中文字幕 | v一区无码内射国产 | 国精产品一品二品国精品69xx | 国产精品沙发午睡系列 | 久久久久成人片免费观看蜜芽 | 亚洲成av人片在线观看无码不卡 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 国产97在线 | 亚洲 | 中文字幕人妻丝袜二区 | 久久国内精品自在自线 | 娇妻被黑人粗大高潮白浆 | 亚洲国产精品无码一区二区三区 | 亚洲日本一区二区三区在线 | 一本大道久久东京热无码av | 图片小说视频一区二区 | 久久国产精品偷任你爽任你 | 亚洲熟妇色xxxxx亚洲 | 中文字幕乱码人妻无码久久 | 国精产品一品二品国精品69xx | 最新国产麻豆aⅴ精品无码 | 国产精品久久福利网站 | 亚洲 a v无 码免 费 成 人 a v | 人人妻人人藻人人爽欧美一区 | 久久www免费人成人片 | 无码播放一区二区三区 | 日本熟妇人妻xxxxx人hd | 国产香蕉尹人综合在线观看 | 少妇愉情理伦片bd | 亚洲成在人网站无码天堂 | 夜精品a片一区二区三区无码白浆 | 亚洲日韩av一区二区三区中文 | 国产精品久久久久9999小说 | 性欧美疯狂xxxxbbbb | 日本免费一区二区三区最新 | 日本xxxx色视频在线观看免费 | 亚洲午夜久久久影院 | 狠狠色噜噜狠狠狠7777奇米 | 亚洲国产综合无码一区 | 国产黄在线观看免费观看不卡 | 中文字幕av无码一区二区三区电影 | 久久99精品国产麻豆 | 国产香蕉97碰碰久久人人 | 亚洲小说图区综合在线 | 国产明星裸体无码xxxx视频 | 国精产品一品二品国精品69xx | 激情国产av做激情国产爱 | 无码av最新清无码专区吞精 | 日本爽爽爽爽爽爽在线观看免 | 老熟女重囗味hdxx69 | 国产午夜亚洲精品不卡下载 | 中文字幕无码人妻少妇免费 | 精品国产一区二区三区四区在线看 | 久久综合香蕉国产蜜臀av | 大肉大捧一进一出好爽视频 | 欧美性猛交内射兽交老熟妇 | 成人精品天堂一区二区三区 | 国产乱人伦偷精品视频 | 久久久精品456亚洲影院 | 亚洲熟妇色xxxxx欧美老妇y | 精品无人国产偷自产在线 | 亚洲精品国偷拍自产在线观看蜜桃 | 日韩av无码中文无码电影 | 蜜桃视频韩日免费播放 | 狠狠色噜噜狠狠狠狠7777米奇 | 国产凸凹视频一区二区 | 久久久久免费看成人影片 | 亚洲欧洲日本无在线码 | 亚洲一区二区三区在线观看网站 | 亚洲综合无码一区二区三区 | 中文字幕无码日韩欧毛 | 欧美日韩一区二区免费视频 | 国内精品久久久久久中文字幕 | 国产av久久久久精东av | 亚洲国产精品无码久久久久高潮 | 纯爱无遮挡h肉动漫在线播放 | 国产97在线 | 亚洲 | 精品日本一区二区三区在线观看 | 秋霞成人午夜鲁丝一区二区三区 | 国产精品无码mv在线观看 | 中文字幕无码av波多野吉衣 | 久久人人爽人人爽人人片av高清 | 水蜜桃色314在线观看 | 无码一区二区三区在线观看 | 中文字幕+乱码+中文字幕一区 | 全黄性性激高免费视频 | 久久精品人妻少妇一区二区三区 | 色综合久久久久综合一本到桃花网 | 欧美freesex黑人又粗又大 | 无码人妻出轨黑人中文字幕 | 日韩视频 中文字幕 视频一区 | 国产成人精品久久亚洲高清不卡 | 日本免费一区二区三区最新 | 欧美精品国产综合久久 | 亚洲无人区一区二区三区 | 东京无码熟妇人妻av在线网址 | 久久久久国色av免费观看性色 | 久久久久成人片免费观看蜜芽 | 亚洲精品成人av在线 | 无码人中文字幕 | 国内丰满熟女出轨videos | 国产麻豆精品一区二区三区v视界 | 国产精品久免费的黄网站 | 日产精品高潮呻吟av久久 | 亚洲s色大片在线观看 | 无码人妻少妇伦在线电影 | 亲嘴扒胸摸屁股激烈网站 | 欧美精品无码一区二区三区 | 国产亚洲精品精品国产亚洲综合 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 天天躁日日躁狠狠躁免费麻豆 | 波多野结衣一区二区三区av免费 | 熟妇人妻无码xxx视频 | 国产人妻久久精品二区三区老狼 | 性欧美videos高清精品 | 2019nv天堂香蕉在线观看 | 2019午夜福利不卡片在线 | 亚洲精品久久久久久一区二区 | 日本免费一区二区三区最新 | 国产成人无码av片在线观看不卡 | 欧美阿v高清资源不卡在线播放 | 欧美丰满熟妇xxxx性ppx人交 | 一本久久伊人热热精品中文字幕 | 精品一区二区不卡无码av | 蜜桃视频插满18在线观看 | 久久久精品欧美一区二区免费 | 国产激情综合五月久久 | 性色欲情网站iwww九文堂 | 乱码午夜-极国产极内射 | 东京无码熟妇人妻av在线网址 | 青春草在线视频免费观看 | 精品一二三区久久aaa片 | 少妇激情av一区二区 | 成人欧美一区二区三区黑人免费 | 国产69精品久久久久app下载 | 丰满人妻翻云覆雨呻吟视频 | 亚洲综合另类小说色区 | 亚洲国产午夜精品理论片 | 粉嫩少妇内射浓精videos | 无码人妻久久一区二区三区不卡 | 乌克兰少妇xxxx做受 | 国产猛烈高潮尖叫视频免费 | 日本又色又爽又黄的a片18禁 | 精品久久久久香蕉网 | 18禁黄网站男男禁片免费观看 | 无码av免费一区二区三区试看 | 一本久道高清无码视频 | 国产成人精品久久亚洲高清不卡 | 国产激情综合五月久久 | 国产乱人伦av在线无码 | 无遮挡国产高潮视频免费观看 | 无码福利日韩神码福利片 | 乱码午夜-极国产极内射 | 狠狠躁日日躁夜夜躁2020 | 久久久久久亚洲精品a片成人 | 55夜色66夜色国产精品视频 | 亚洲欧洲日本综合aⅴ在线 | 亚洲一区二区三区香蕉 | 中文字幕无码乱人伦 | 玩弄中年熟妇正在播放 | 丰腴饱满的极品熟妇 | 国产午夜福利100集发布 | 国产亚洲精品久久久久久久 | 国产手机在线αⅴ片无码观看 | 樱花草在线社区www | 国产69精品久久久久app下载 | 正在播放老肥熟妇露脸 | 男女作爱免费网站 | 蜜臀av无码人妻精品 | 日韩无码专区 | 三上悠亚人妻中文字幕在线 | 97资源共享在线视频 | 2019nv天堂香蕉在线观看 | 男女下面进入的视频免费午夜 | 久久国产自偷自偷免费一区调 | 国产一区二区三区影院 | 久久久久国色av免费观看性色 | 亚洲七七久久桃花影院 | 国产精品无码mv在线观看 | 无码福利日韩神码福利片 | 亚洲欧美日韩综合久久久 | 99久久久国产精品无码免费 | 午夜无码人妻av大片色欲 | 正在播放东北夫妻内射 | 99视频精品全部免费免费观看 | 强奷人妻日本中文字幕 | 国产又爽又猛又粗的视频a片 | 国产成人精品优优av | 乱人伦人妻中文字幕无码 | 男人的天堂av网站 | 亚无码乱人伦一区二区 | 99视频精品全部免费免费观看 | 俺去俺来也在线www色官网 | 性欧美牲交在线视频 | av无码久久久久不卡免费网站 | 俄罗斯老熟妇色xxxx | 久久久精品欧美一区二区免费 | 免费乱码人妻系列无码专区 | 国产精品久久久久久久影院 | 香港三级日本三级妇三级 | ass日本丰满熟妇pics | 国产精品人人爽人人做我的可爱 | 国产凸凹视频一区二区 | 夜夜高潮次次欢爽av女 | 久久久久久久久蜜桃 | 日韩 欧美 动漫 国产 制服 | av无码久久久久不卡免费网站 | 欧美亚洲国产一区二区三区 | 亚洲熟妇色xxxxx欧美老妇y | 啦啦啦www在线观看免费视频 | 西西人体www44rt大胆高清 | 亚洲欧美中文字幕5发布 | 麻豆人妻少妇精品无码专区 | 亚洲日本va午夜在线电影 | 久久国产自偷自偷免费一区调 | 麻豆精品国产精华精华液好用吗 | 日本大香伊一区二区三区 | 波多野结衣aⅴ在线 | 精品国产av色一区二区深夜久久 | 天下第一社区视频www日本 | 少妇被粗大的猛进出69影院 | 亚洲成av人影院在线观看 | 双乳奶水饱满少妇呻吟 | 高清国产亚洲精品自在久久 | ass日本丰满熟妇pics | 好爽又高潮了毛片免费下载 | 国产成人综合色在线观看网站 | 99久久无码一区人妻 | 国产精品a成v人在线播放 | 高潮毛片无遮挡高清免费视频 | 伊人色综合久久天天小片 | 中文字幕无线码免费人妻 | 蜜桃臀无码内射一区二区三区 | 美女黄网站人色视频免费国产 | 亚洲精品一区三区三区在线观看 | 无码吃奶揉捏奶头高潮视频 | 国产精品久久久久久亚洲影视内衣 | 一个人看的www免费视频在线观看 | 国产人成高清在线视频99最全资源 | 亚洲一区二区三区无码久久 | 狠狠色色综合网站 | 无码人妻精品一区二区三区不卡 | 九九综合va免费看 | 无码精品国产va在线观看dvd | 性色欲网站人妻丰满中文久久不卡 | 亚洲精品国偷拍自产在线观看蜜桃 | 人人澡人摸人人添 | 亚洲热妇无码av在线播放 | 国产亲子乱弄免费视频 | 久久久精品欧美一区二区免费 |