久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

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

编程问答

JVM核心知识体系

發布時間:2025/3/21 编程问答 12 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JVM核心知识体系 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.問題

  • 1、如何理解類文件結構布局?

  • 2、如何應用類加載器的工作原理進行將應用輾轉騰挪?

  • 3、熱部署與熱替換有何區別,如何隔離類沖突?

  • 4、JVM如何管理內存,有何內存淘汰機制?

  • 5、JVM執行引擎的工作機制是什么?

  • 6、JVM調優應該遵循什么原則,使用什么工具?

  • 7、JPDA架構是什么,如何應用代碼熱替換?

  • 8、JVM字節碼增強技術有哪些?

2.關鍵詞

類結構,類加載器,加載,鏈接,初始化,雙親委派,熱部署,隔離,堆,棧,方法區,計數器,內存回收,執行引擎,調優工具,JVMTI,JDWP,JDI,熱替換,字節碼,ASM,CGLIB,DCEVM

3.全文概要(文末有驚喜,PC端閱讀代碼更佳)

作為三大工業級別語言之一的JAVA如此受企業青睞有加,離不開她背后JVM的默默復出。只是由于JAVA過于成功以至于我們常常忘了JVM平臺上還運行著像Clojure/Groovy/Kotlin/Scala/JRuby/Jython這樣的語言。我們享受著JVM帶來跨平臺“一次編譯到處執行”臺的便利和自動內存回收的安逸。本文從JVM的最小元素類的結構出發,介紹類加載器的工作原理和應用場景,思考類加載器存在的意義。進而描述JVM邏輯內存的分布和管理方式,同時列舉常用的JVM調優工具和使用方法,最后介紹高級特性JDPA框架和字節碼增強技術,實現熱替換。從微觀到宏觀,從靜態到動態,從基礎到高階介紹JVM的知識體系。

4.類的裝載

4.1類的結構

我們知道不只JAVA文本文件,像Clojure/Groovy/Kotlin/Scala這些文本文件也同樣會經過JDK的編譯器編程成class文件。進入到JVM領域后,其實就跟JAVA沒什么關系了,JVM只認得class文件,那么我們需要先了解class這個黑箱里面包含的是什么東西。

JVM規范嚴格定義了CLASS文件的格式,有嚴格的數據結構,下面我們可以觀察一個簡單CLASS文件包含的字段和數據類型。

ClassFile {u4 magic;u2 minor_version;u2 major_version;u2 constant_pool_count;cp_info constant_pool[constant_pool_count-1];u2 access_flags;u2 this_class;u2 super_class;u2 interfaces_count;u2 interfaces[interfaces_count];u2 fields_count;field_info fields[fields_count];u2 methods_count;method_info methods[methods_count];u2 attributes_count;attribute_info attributes[attributes_count]; }

詳細的描述我們可以從JVM規范說明書里面查閱類文件格式(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html),類的整體布局如下圖展示的。

在我的理解,我想把每個CLASS文件類別成一個一個的數據庫,里面包含的常量池/類索引/屬性表集合就像數據庫的表,而且表之間也有關聯,常量池則存放著其他表所需要的所有字面量。了解完類的數據結構后,我們需要來觀察JVM是如何使用這些從硬盤上或者網絡傳輸過來的CLASS文件。

4.2加載機制

4.2.1類的入口

在我們探究JVM如何使用CLASS文件之前,我們快速回憶一下編寫好的C語言文件是如何執行的?我們從C的HelloWorld入手看看先。

#include <stdio.h>int main() {/* my first program in C */printf("Hello, World! n");return 0; }

編輯完保存為hello.c文本文件,然后安裝gcc編譯器(GNU C/C++)

$ gcc hello.c $ ./a.out Hello, World!

這個過程就是gcc編譯器將hello.c文本文件編譯成機器指令集,然后讀取到內存直接在計算機的CPU運行。從操作系統層面看的話,就是一個進程的啟動到結束的生命周期。

下面我們看JAVA是怎么運行的。學習JAVA開發的第一件事就是先下載JDK安裝包,安裝完配置好環境變量,然后寫一個名字為helloWorld的類,然后編譯執行,我們來觀察一下發生了什么事情?

先看源碼,有夠簡單了吧。

package com.zooncool.example.theory.jvm; public class HelloWorld {public static void main(String[] args) {System.out.println("my classLoader is " + HelloWorld.class.getClassLoader());} }

編譯執行

$ javac src/main/java/com/zooncool/example/theory/jvm/HelloWorld.java $ java -cp src/main/java/ com.zooncool.example.theory.jvm.HelloWorld my classLoader is sun.misc.Launcher$AppClassLoader@2a139a55

對比C語言在命令行直接運行編譯后的a.out二進制文件,JAVA的則是在命令行執行java classFile,從命令的區別我們知道操作系統啟動的是java進程,而HelloWorld類只是命令行的入參,在操作系統來看java也就是一個普通的應用進程而已,而這個進程就是JVM的執行形態(JVM靜態就是硬盤里JDK包下的二進制文件集合)。

學習過JAVA的都知道入口方法是public static void main(String[] args),缺一不可,那我猜執行java命令時JVM對該入口方法做了唯一驗證,通過了才允許啟動JVM進程,下面我們來看這個入口方法有啥特點。

  • 去掉public限定

    $ javac src/main/java/com/zooncool/example/theory/jvm/HelloWorld.java $ java -cp src/main/java/ com.zooncool.example.theory.jvm.HelloWorld 錯誤: 在類 com.zooncool.example.theory.jvm.HelloWorld 中找不到 main 方法, 請將 main 方法定義為:public static void main(String[] args) 否則 JavaFX 應用程序類必須擴展javafx.application.Application

說名入口方法需要被public修飾,當然JVM調用main方法是底層的JNI方法調用不受修飾符影響。

  • 去掉static限定

    $ javac src/main/java/com/zooncool/example/theory/jvm/HelloWorld.java $ java -cp src/main/java/ com.zooncool.example.theory.jvm.HelloWorld 錯誤: main 方法不是類 com.zooncool.example.theory.jvm.HelloWorld 中的static, 請將 main 方法定義為:public static void main(String[] args)

我們是從類對象調用而不是類創建的對象才調用,索引需要靜態修飾

  • 返回類型改為int

    $ javac src/main/java/com/zooncool/example/theory/jvm/HelloWorld.java $ java -cp src/main/java/ com.zooncool.example.theory.jvm.HelloWorld 錯誤: main 方法必須返回類 com.zooncool.example.theory.jvm.HelloWorld 中的空類型值, 請 將 main 方法定義為:public static void main(String[] args)

void返回類型讓JVM調用后無需關心調用者的使用情況,執行完就停止,簡化JVM的設計。

  • 方法簽名改為main1

    $ javac src/main/java/com/zooncool/example/theory/jvm/HelloWorld.java $ java -cp src/main/java/ com.zooncool.example.theory.jvm.HelloWorld 錯誤: 在類 com.zooncool.example.theory.jvm.HelloWorld 中找不到 main 方法, 請將 main 方法定義為:public static void main(String[] args) 否則 JavaFX 應用程序類必須擴展javafx.application.Application

這個我也不清楚,可能是約定俗成吧,畢竟C/C++也是用main方法的。

說了這么多main方法的規則,其實我們關心的只有兩點:

  • HelloWorld類是如何被JVM使用的

  • HelloWorld類里面的main方法是如何被執行的

關于JVM如何使用HelloWorld下文我們會詳細講到。

我們知道JVM是由C/C++語言實現的,那么JVM跟CLASS打交道則需要JNI(Java Native Interface)這座橋梁,當我們在命令行執行java時,由C/C++實現的java應用通過JNI找到了HelloWorld里面符合規范的main方法,然后開始調用。我們來看下java命令的源碼就知道了

/* * Get the application's main class. */ if (jarfile != 0) { mainClassName = GetMainClassName(env, jarfile); ... ... mainClass = LoadClass(env, classname); if(mainClass == NULL) { /* exception occured */ ... ... /* Get the application's main method */ mainID = (*env)->GetStaticMethodID(env, mainClass, "main", "([Ljava/lang/String;)V"); ... ... {/* Make sure the main method is public */ jint mods; jmethodID mid; jobject obj = (*env)->ToReflectedMethod(env, mainClass, mainID, JNI_TRUE); ... ... /* Build argument array */ mainArgs = NewPlatformStringArray(env, argv, argc); if (mainArgs == NULL) { ReportExceptionDescription(env); goto leave; } /* Invoke main method. */ (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);

4.2.2類加載器

上一節我們留了一個核心的環節,就是JVM在執行類的入口之前,首先得找到類再然后再把類裝到JVM實例里面,也即是JVM進程維護的內存區域內。我們當然知道是一個叫做類加載器的工具把類加載到JVM實例里面,拋開細節從操作系統層面觀察,那么就是JVM實例在運行過程中通過IO從硬盤或者網絡讀取CLASS二進制文件,然后在JVM管轄的內存區域存放對應的文件。我們目前還不知道類加載器的實現,但是我們從功能上判斷無非就是讀取文件到內存,這個是很普通也很簡單的操作。

如果類加載器是C/C++實現的話,那么大概就是如下代碼就可以實現

char *fgets( char *buf, int n, FILE *fp );

如果是JAVA實現,那么也很簡單

InputStream f = new FileInputStream("theory/jvm/HelloWorld.class");

從操作系統層面看的話,如果只是加載,以上代碼就足以把類文件加載到JVM內存里面了。但是結果就是亂糟糟的把一堆毫無秩序的類文件往內存里面扔,沒有良好的管理也沒法用,所以需要我們需要設計一套規則來管理存放內存里面的CLASS文件,我們稱為類加載的設計模式或者類加載機制,這個下文會重點解釋。

根據官網的定義A class loader is an object that is responsible for loading classes. 類加載器就是負責加載類的。我們知道啟動JVM的時候會把JRE默認的一些類加載到內存,這部分類使用的加載器是JVM默認內置的由C/C++實現的,比如我們上文加載的HelloWorld.class。但是內置的類加載器有明確的范圍限定,也就是只能加載指定路徑下的jar包(類文件的集合)。如果只是加載JRE的類,那可玩的花樣就少很多,JRE只是提供了底層所需的類,更多的業務需要我們從外部加載類來支持,所以我們需要指定新的規則,以方便我們加載外部路徑的類文件。

系統默認加載器

  • Bootstrap class loader

    作用:啟動類加載器,加載JDK核心類

    類加載器:C/C++實現

    類加載路徑:?/jre/lib

    URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/resources.jar ... /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/rt.jar

    實現原理:本地方法由C++實現

  • Extensions class loader

    作用:擴展類加載器,加載JAVA擴展類庫。

    類加載器:JAVA實現

    類加載路徑:/jre/lib/ext

    System.out.println(System.getProperty("java.ext.dirs")); /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/lib/ext:

    實現原理:擴展類加載器ExtClassLoader本質上也是URLClassLoader

    Launcher.java

    //構造方法返回擴展類加載器 public Launcher() {//定義擴展類加載器Launcher.ExtClassLoader var1;try {//1、獲取擴展類加載器var1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}... }//擴展類加載器 static class ExtClassLoader extends URLClassLoader {private static volatile Launcher.ExtClassLoader instance;//2、獲取擴展類加載器實現public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {if (instance == null) {Class var0 = Launcher.ExtClassLoader.class;synchronized(Launcher.ExtClassLoader.class) {if (instance == null) {//3、構造擴展類加載器instance = createExtClassLoader();}}}return instance;} //4、構造擴展類加載器具體實現private static Launcher.ExtClassLoader createExtClassLoader() throws IOException {try {return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {public Launcher.ExtClassLoader run() throws IOException {//5、獲取擴展類加載器加載目標類的目錄File[] var1 = Launcher.ExtClassLoader.getExtDirs();int var2 = var1.length;for(int var3 = 0; var3 < var2; ++var3) {MetaIndex.registerDirectory(var1[var3]);}//7、構造擴展類加載器return new Launcher.ExtClassLoader(var1);}});} catch (PrivilegedActionException var1) {throw (IOException)var1.getException();}}//6、擴展類加載器目錄路徑private static File[] getExtDirs() {String var0 = System.getProperty("java.ext.dirs");File[] var1;if (var0 != null) {StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);int var3 = var2.countTokens();var1 = new File[var3];for(int var4 = 0; var4 < var3; ++var4) {var1[var4] = new File(var2.nextToken());}} else {var1 = new File[0];}return var1;}//8、擴展類加載器構造方法public ExtClassLoader(File[] var1) throws IOException {super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);} }
  • System class loader

    作用:系統類加載器,加載應用指定環境變量路徑下的類

    類加載器:sun.misc.Launcher$AppClassLoader

    類加載路徑:-classpath下面的所有類

    實現原理:系統類加載器AppClassLoader本質上也是URLClassLoader

    Launcher.java

    //構造方法返回系統類加載器 public Launcher() {try {//獲取系統類加載器this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);} } static class AppClassLoader extends URLClassLoader {final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);//系統類加載器實現邏輯public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {//類比擴展類加載器,相似的邏輯final String var1 = System.getProperty("java.class.path");final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {public Launcher.AppClassLoader run() {URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);return new Launcher.AppClassLoader(var1x, var0);}});}//系統類加載器構造方法AppClassLoader(URL[] var1, ClassLoader var2) {super(var1, var2, Launcher.factory);this.ucp.initLookupCache(this);} }

通過上文運行HelloWorld我們知道JVM系統默認加載的類大改是1560個,如下圖

自定義類加載器

內置類加載器只加載了最少需要的核心JAVA基礎類和環境變量下的類,但是我們應用往往需要依賴第三方中間件來完成額外的業務,那么如何把它們的類加載進來就顯得格外重要了。幸好JVM提供了自定義類加載器,可以很方便的完成自定義操作,最終目的也是把外部的類文件加載到JVM內存。通過繼承ClassLoader類并且復寫findClass和loadClass方法就可以達到自定義獲取CLASS文件的目的。

首先我們看ClassLoader的核心方法loadClass

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded,看緩存有沒有沒有才去找Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {//先看是不是最頂層,如果不是則parent為空,然后獲取父類if (parent != null) {c = parent.loadClass(name, false);} else {//如果為空則說明應用啟動類加載器,讓它去加載c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order//如果還是沒有就調用自己的方法,確保調用自己方法前都使用了父類方法,如此遞歸三次到頂long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;} } protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name); }

通過復寫loadClass方法,我們甚至可以讀取一份加了密的文件,然后在內存里面解密,這樣別人反編譯你的源碼也沒用,因為class是經過加密的,也就是理論上我們通過自定義類加載器可以做到為所欲為,但是有個重要的原則下文介紹類加載器設計模式會提到。

一下給出一個自定義類加載器極簡的案例,來說明自定義類加載器的實現。

package com.zooncool.example.theory.jvm; import java.io.FileInputStream; import static java.lang.System.out;public class ClassIsolationPrinciple {public static void main(String[] args) {try {String className = "com.zooncool.example.theory.jvm.ClassIsolationPrinciple$Demo"; //定義要加載類的全限定名Class<?> class1 = Demo.class; //第一個類又系統默認類加載器加載//第二個類MyClassLoader為自定義類加載器,自定義的目的是覆蓋加載類的邏輯Class<?> class2 = new MyClassLoader("target/classes").loadClass(className);out.println("-----------------class name-----------------");out.println(class1.getName());out.println(class2.getName());out.println("-----------------classLoader name-----------------");out.println(class1.getClassLoader());out.println(class2.getClassLoader());Demo.example = 1;//這里修改的系統類加載器加載的那個類的對象,而自定義加載器加載進去的類的對象保持不變,也即是同時存在內存,但沒有修改example的值。out.println("-----------------field value-----------------");out.println(class1.getDeclaredField("example").get(null));out.println(class2.getDeclaredField("example").get(null));} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}public static class Demo {public static int example = 0;}public static class MyClassLoader extends ClassLoader{private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}//自定義類加載器繼承了ClassLoader,稱為一個可以加載類的加載器,同時覆蓋了loadClass方法,實現自己的邏輯@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {if(!name.contains("java.lang")){//排除掉加載系統默認需要加載的內心類,因為些類只能又默認類加載器去加載,第三方加載會拋異常,具體原因下文解釋byte[] data = new byte[0];try {data = loadByte(name);} catch (Exception e) {e.printStackTrace();}return defineClass(name,data,0,data.length);}else{return super.loadClass(name);}}//把影片的二進制類文件讀入內存字節流private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\.", "/");String dir = classPath + "/" + name + ".class";FileInputStream fis = new FileInputStream(dir);int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}} }

執行結果如下,我們可以看到加載到內存方法區的兩個類的包名+名稱是一樣的,而對應的類加載器卻不一樣,而且輸出被加載類的值也是不一樣的。

-----------------class name----------------- com.zooncool.example.theory.jvm.ClassIsolationPrinciple2$Demo com.zooncool.example.theory.jvm.ClassIsolationPrinciple2$Demo -----------------classLoader name----------------- sun.misc.Launcher$AppClassLoader@18b4aac2 com.zooncool.example.theory.jvm.ClassIsolationPrinciple2$MyClassLoader@511d50c0 -----------------field value----------------- 1 0

4.2.3設計模式

現有的加載器分為內置類加載器和自定義加載器,不管它們是通過C或者JAVA實現的最終都是為了把外部的CLASS文件加載到JVM內存里面。那么我們就需要設計一套規則來管理組織內存里面的CLASS文件,下面我們就來介紹下通過這套規則如何來協調好內置類加載器和自定義類加載器之間的權責。

我們知道通過自定義類加載器可以干出很多黑科技,但是有個基本的雷區就是,不能隨便替代JAVA的核心基礎類,或者說即是你寫了一個跟核心類一模一樣的類,JVM也不會使用。你想一下,如果為所欲為的你可以把最基礎本的java.lang.Object都換成你自己定義的同名類,然后搞個后門進去,而且JVM還使用的話,那誰還敢用JAVA了是吧,所以我們會介紹一個重要的原則,在此之前我們先介紹一下內置類加載器和自定義類加載器是如何協同的。

  • 雙親委派機制

    定義:某個特定的類加載器在接到加載類的請求時,首先將加載任務委托給父類加載器,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加載器無法完成此加載任務時,才自己去加載。

    實現:參考上文loadClass方法的源碼和注釋,通過最多三次遞歸可以到啟動類加載器,如果還是找不到這調用自定義方法。

雙親委派機制很好理解,目的就是為了不重復加載已有的類,提高效率,還有就是強制從父類加載器開始逐級搜索類文件,確保核心基礎類優先加載。下面介紹的是破壞雙親委派機制,了解為什么要破壞這種看似穩固的雙親委派機制。

  • 破壞委派機制

    定義:打破類加載自上而上委托的約束。

    實現:1、繼承ClassLoader并且重寫loadClass方法體,覆蓋依賴上層類加載器的邏輯;

    2、”啟動類加載器”可以指定“線程上下文類加載器”為任意類加載器,即是“父類加載器”委托“子類加載器”去加載不屬于它加載范圍的類文件;

    說明:雙親委派機制的好處上面我們已經提過了,但是由于一些歷史原因(JDK1.2加上雙親委派機制前的JDK1.1就已經存在,為了向前兼容不得不開這個后門讓1.2版本的類加載器擁有1.1隨意加載的功能)。還有就是JNDI的服務調用機制,例如調用JDBC需要從外部加載相關類到JVM實例的內存空間。

介紹完內置類加載器和自定義類加載器的協同關系后,我們要重點強調上文提到的重要原則。

  • 唯一標識

    定義:JVM實例由類加載器+類的全限定包名和類名組成類的唯一標志。

    實現:加載類的時候,JVM 判斷類是否來自相同的加載器,如果相同而且全限定名則直接返回內存已有的類。

    說明:上文我們提到如何防止相同類的后門問題,有了這個黃金法則,即使相同的類路徑和類,但是由于是由自定義類加載器加載的,即使編譯通過能被加載到內存,也無法使用,因為JVM核心類是由內置類加載器加載標志和使用的,從而保證了JVM的安全加載。通過緩存類加載器和全限定包名和類名作為類唯一索引,加載重復類則拋異常提示”attempted duplicate class definition for name”。

    原理:雙親委派機制父類檢查緩存,源碼我們介紹loadClass方法的時候已經講過,破壞雙親委派的自定義類加載器在加載類二進制字節碼后需要調用defineClass方法,而該方法同樣會從JVM方法區檢索緩存類,存在的話則提示重復定義。

4.2.4加載過程

至此我們已經深刻認識到類加載器的工作原理及其存在的意義,下面我們將介紹類從外部介質加載使用到卸載整個閉環的生命周期。

加載

上文花了不少的篇幅說明了類的結構和類是如何被加載到JVM內存里面的,那究竟什么時候JVM才會觸發類加載器去加載外部的CLASS文件呢?通常有如下四種情況會觸發到:

  • 顯式字節碼指令集(new/getstatic/putstatic/invokestatic):對應的場景就是創建對象或者調用到類文件的靜態變量/靜態方法/靜態代碼塊

  • 反射:通過對象反射獲取類對象時

  • 繼承:創建子類觸發父類加載

  • 入口:包含main方法的類首先被加載

JVM只定了類加載器的規范,但卻不明確規定類加載器的目標文件,把加載的具體邏輯充分交給了用戶,包括重硬盤加載的CLASS類到網絡,中間文件等,只要加載進去內存的二進制數據流符合JVM規定的格式,都是合法的。

鏈接

類加載器加載完類到JVM實例的指定內存區域(方法區下文會提到)后,是使用前會經過驗證,準備解析的階段。

  • 驗證:主要包含對類文件對應內存二進制數據的格式、語義關聯、語法邏輯和符合引用的驗證,如果驗證不通過則跑出VerifyError的錯誤。但是該階段并非強制執行,可以通過-Xverify:none來關閉,提高性能。

  • 準備:但我們驗證通過時,內存的方法區存放的是被“緊密壓縮”的數據段,這個時候會對static的變量進行內存分配,也就是擴展內存段的空間,為該變量匹配對應類型的內存空間,但還未初始化數據,也就是0或者null的值。

  • 解析:我們知道類的數據結構類似一個數據庫,里面多張不同類型的“表”緊湊的挨在一起,最大的節省類占用的空間。多數表都會應用到常量池表里面的字面量,這個時候就是把引用的字面量轉化為直接的變量空間。比如某一個復雜類變量字面量在類文件里只占2個字節,但是通過常量池引用的轉換為實際的變量類型,需要占用32個字節。所以經過解析階段后,類在方法區占用的空間就會膨脹,長得更像一個”類“了。

初始化

方法區經過解析后類已經為各個變量占好坑了,初始化就是把變量的初始值和構造方法的內容初始化到變量的空間里面。這時候我們介質的類二進制文件所定義的內容,已經完全被“翻譯”方法區的某一段內存空間了。萬事俱備只待使用了。

使用

使用呼應了我們加載類的觸發條件,也即是觸發類加載的條件也是類應用的條件,該操作會在初始化完成后進行。

卸載

我們知道JVM有垃圾回收機制(下文會詳細介紹),不需要我們操心,總體上有三個條件會觸發垃圾回收期清理方法區的空間:

  • 類對應實例被回收

  • 類對應加載器被回收

  • 類無反射引用

本節結束我們已經對整個類的生命周期爛熟于胸了,下面我們來介紹類加載機制最核心的幾種應用場景,來加深對類加載技術的認識。

4.3應用場景

通過前文的剖析我們已經非常清楚類加載器的工作原理,那么我們該如何利用類加載器的特點,最大限度的發揮它的作用呢?

4.3.1熱部署

背景

熱部署這個詞匯我們經常聽說也經常提起,但是卻很少能夠準確的描述出它的定義。說到熱部署我們第一時間想到的可能是生產上的機器更新代碼后無需重啟應用容器就能更新服務,這樣的好處就是服務無需中斷可持續運行,那么與之對應的冷部署當然就是要重啟應用容器實例了。還有可能會想到的是使用IDE工具開發時不需要重啟服務,修改代碼后即時生效,這看起來可能都是服務無需重啟,但背后的運行機制確截然不同,首先我們需要對熱部署下一個準確的定義。

  • 熱部署(Hot Deployment):熱部署是應用容器自動更新應用的一種能力。

首先熱部署應用容器擁有的一種能力,這種能力是容器本身設計出來的,跟具體的IDE開發工具無關。而且熱部署無需重啟服務器,應用可以保持用戶態不受影響。上文提到我們開發環境使用IDE工具通常也可以設置無需重啟的功能,有別于熱部署的是此時我們應用的是JVM的本身附帶的熱替換能力(HotSwap)。熱部署和熱替換是兩個完全不同概念,在開發過程中也常常相互配合使用,導致我們很多人經常混淆概念,所以接下來我們來剖析熱部署的實現原理,而熱替換的高級特性我們會在下文字節碼增強的章節中介紹。

原理

從熱部署的定義我們知道它是應用容器蘊含的一項能力,要達到的目的就是在服務沒有重啟的情況下更新應用,也就是把新的代碼編譯后產生的新類文件替換掉內存里的舊類文件。結合前文我們介紹的類加載器特性,這似乎也不是很難,分兩步應該可以完成。由于同一個類加載器只能加載一次類文件,那么新增一個類加載器把新的類文件加載進內存。此時內存里面同時存在新舊的兩個類(類名路徑一樣,但是類加載器不一樣),要做的就是如何使用新的類,同時卸載舊的類及其對象,完成這兩步其實也就是熱部署的過程了。也即是通過使用新的類加載器,重新加載應用的類,從而達到新代碼熱部署。

實現

理解了熱部署的工作原理,下面通過一系列極簡的例子來一步步實現熱部署,為了方便讀者演示,以下例子我盡量都在一個java文件里面完成所有功能,運行的時候復制下去就可以跑起來。

  • 實現自定義類加載器

參考4.2.2中自定義類加載器區別系統默認加載器的案例,從該案例實踐中我們可以將相同的類(包名+類名),不同”版本“(類加載器不一樣)的類同時加載進JVM內存方法區。

  • 替換自定義類加載器

既然一個類通過不同類加載器可以被多次加載到JVM內存里面,那么類的經過修改編譯后再加載進內存。有別于上一步給出的例子只是修改對象的值,這次我們是直接修改類的內容,從應用的視角看其實就是應用更新,那如何做到在線程運行不中斷的情況下更換新類呢?

下面給出的也是一個很簡單的例子,ClassReloading啟動main方法通過死循環不斷創建類加載器,同時不斷加載類而且執行類的方法。注意new MyClassLoader(“target/classes”)的路徑更加編譯的class路徑來修改,其他直接復制過去就可以執行演示了。

package com.zooncool.example.theory.jvm; import java.io.FileInputStream; import java.lang.reflect.InvocationTargetException; public class ClassReloading {public static void main(String[] args)throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException,InvocationTargetException, InterruptedException {for (;;){//用死循環讓線程持續運行未中斷狀態//通過反射調用目標類的入口方法String className = "com.zooncool.example.theory.jvm.ClassReloading$User";Class<?> target = new MyClassLoader("target/classes").loadClass(className);//加載進來的類,通過反射調用execute方法target.getDeclaredMethod("execute").invoke(targetClass.newInstance());//HelloWorld.class.getDeclaredMethod("execute").invoke(HelloWorld.class.newInstance());//如果換成系統默認類加載器的話,因為雙親委派原則,默認使用應用類加載器,而且能加載一次//休眠是為了在刪除舊類編譯新類的這段時間內不執行加載動作//不然會找不到類文件Thread.sleep(10000);}}//自定義類加載器加載的目標類public static class User {public void execute() throws InterruptedException {//say();ask();}public void ask(){System.out.println("what is your name");}public void say(){System.out.println("my name is lucy");}}//下面是自定義類加載器,跟第一個例子一樣,可略過public static class MyClassLoader extends ClassLoader{...} }

ClassReloading線程執行過程不斷輪流注釋say()和ask()代碼,然后編譯類,觀察程序輸出。

如下輸出結果,我們可以看出每一次循環調用都新創建一個自定義類加載器,然后通過反射創建對象調用方法,在修改代碼編譯后,新的類就會通過反射創建對象執行新的代碼業務,而主線程則一直沒有中斷運行。讀到這里,其實我們已經基本觸達了熱部署的本質了,也就是實現了手動無中斷部署。但是缺點就是需要我們手動編譯代碼,而且內存不斷新增類加載器和對象,如果速度過快而且頻繁更新,還可能造成堆溢出,下一個例子我們將增加一些機制來保證舊的類和對象能被垃圾收集器自動回收。

what is your name what is your name what is your name//修改代碼,編譯新類 my name is lucy my name is lucy what is your name//修改代碼,編譯新類
  • 回收自定義類加載器

通常情況下類加載器會持有該加載器加載過的所有類的引用,所有如果類是經過系統默認類加載器加載的話,那就很難被垃圾收集器回收,除非符合根節點不可達原則才會被回收。

下面繼續給出一個很簡單的例子,我們知道ClassReloading只是不斷創建新的類加載器來加載新類從而更新類的方法。下面的例子我們模擬WEB應用,更新整個應用的上下文Context。下面代碼本質上跟上個例子的功能是一樣的,只不過我們通過加載Model層、DAO層和Service層來模擬web應用,顯得更加真實。

package com.zooncool.example.theory.jvm; import java.io.FileInputStream; import java.lang.reflect.InvocationTargetException; //應用上下文熱加載 public class ContextReloading {public static void main(String[] args)throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException,InvocationTargetException, InterruptedException {for (;;){Object context = newContext();//創建應用上下文invokeContext(context);//通過上下文對象context調用業務方法Thread.sleep(5000);}}//創建應用的上下文,context是整個應用的GC roots,創建完返回對象之前調用init()初始化對象public static Object newContext()throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException,InvocationTargetException {String className = "com.zooncool.example.theory.jvm.ContextReloading$Context";//通過自定義類加載器加載Context類Class<?> contextClass = new MyClassLoader("target/classes").loadClass(className);Object context = contextClass.newInstance();//通過反射創建對象contextClass.getDeclaredMethod("init").invoke(context);//通過反射調用初始化方法init()return context;}//業務方法,調用context的業務方法showUser()public static void invokeContext(Object context)throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {context.getClass().getDeclaredMethod("showUser").invoke(context);}public static class Context{private UserService userService = new UserService();public String showUser(){return userService.getUserMessage();}//初始化對象public void init(){UserDao userDao = new UserDao();userDao.setUser(new User());userService.setUserDao(userDao);}}public static class UserService{private UserDao userDao;public String getUserMessage(){return userDao.getUserName();}public void setUserDao(UserDao userDao) {this.userDao = userDao;}}public static class UserDao{private User user;public String getUserName(){//關鍵操作,運行main方法后切換下面方法,編譯后下一次調用生效return user.getName();//return user.getFullName();}public void setUser(User user) {this.user = user;}}public static class User{private String name = "lucy";private String fullName = "hank.lucy";public String getName() {System.out.println("my name is " + name);return name;}public String getFullName() {System.out.println("my full name is " + fullName);return name;}}//跟之前的類加載器一模一樣,可以略過public static class MyClassLoader extends ClassLoader{...} }

輸出結果跟上一個例子相似,可以自己運行試試。我們更新業務方法編譯通過后,無需重啟main方法,新的業務就能生效,而且也解決了舊類卸載的核心問題,因為context的應用對象的跟節點,context是由我們自定義類加載器所加載,由于User/Dao/Service都是依賴context,所以其類也是又自定義類加載器所加載。根據GC roots原理,在創建新的自定義類加載器之后,舊的類加載器已經沒有任何引用鏈可訪達,符合GC回收規則,將會被GC收集器回收釋放內存。至此已經完成應用熱部署的流程,但是細心的朋友可能會發現,我們熱部署的策略是整個上下文context都替換成新的,那么用戶的狀態也將無法保留。而實際情況是我們只需要動態更新某些模塊的功能,而不是全局。這個其實也好辦,就是我們從業務上把需要熱部署的由自定義類加載器加載,而持久化的類資源則由系統默認類加載器去完成。

  • 自動加載類加載器

其實設計到代碼設計優雅問題,基本上我們拿出設計模式23章經對號入座基本可以解決問題,畢竟這是前人經過千萬實踐錘煉出來的軟件構建內功心法。那么針對我們熱部署的場景,如果想把熱部署細節封裝出來,那代理模式無疑是最符合要求的,也就是咱們弄出個代理對象來面向用戶,把類加載器的更替,回收,隔離等細節都放在代理對象里面完成,而對于用戶來說是透明無感知的,那么終端用戶體驗起來就是純粹的熱部署了。至于如何實現自動熱部署,方式也很簡單,監聽我們部署的目錄,如果文件時間和大小發生變化,則判斷應用需要更新,這時候就觸發類加載器的創建和舊對象的回收,這個時候也可以引入觀察者模式來實現。由于篇幅限制,本例子就留給讀者朋友自行設計,相信也是不難完成的。

案例

上一節我們深入淺出的從自定義類加載器的開始引入,到實現多個類加載器加載同個類文件,最后完成舊類加載器和對象的回收,整個流程闡述了熱部署的實現細節。那么這一節我們介紹現有實現熱部署的通用解決方案,本質就是對上文原理的實現,加上性能和設計上的優化,注意本節我們應用的只是類加載器的技術,后面章節還會介紹的字節碼層面的底層操作技術。

  • OSGI

OSGI(Open Service Gateway Initiative)是一套開發和部署應用程序的java框架。我們從官網可以看到OSGI其實是一套規范,好比Servlet定義了服務端對于處理來自網絡請求的一套規范,比如init,service,destroy的生命周期。然后我們通過實行這套規范來實現與客戶端的交互,在調用init初始化完Servlet對象后通過多線程模式使用service響應網絡請求。如果從響應模式比較我們還可以了解下Webflux的規范,以上兩種都是處理網絡請求的方式,當然你舉例說CGI也是一種處理網絡請求的規范,CGI采用的是多進程方式來處理網絡請求,我們暫時不對這兩種規范進行優劣評價,只是說明在處理網絡請求的場景下可以采用不同的規范來實現。

好了現在回到OSGi,有了上面的鋪墊,相信對我們理解OSGI大有幫助。我們說OSGI首先是一種規范,既然是規范我們就要看看都規范了啥,比如Servlet也是一種規范,它規范了生命周期,規定應用容器中WEB-INF/classes目錄或WEB-INF/lib目錄下的jar包才會被Web容器處理。同樣OSGI的實現框架對管轄的Bundle下面的目錄組織和文本格式也有嚴格規范,更重要的是OSGI對模塊化架構生命周期的管理。而模塊化也不只是把系統拆分成不同的JAR包形成模塊而已,真正的模塊化必須將模塊中類的引入/導出、隱藏、依賴、版本管理貫穿到生命周期管理中去。

定義:OSGI是脫胎于(OSGI Alliance)技術聯盟由一組規范和對應子規范共同定義的JAVA動態模塊化技術。實現該規范的OSGI框架(如Apache Felix)使應用程序的模塊能夠在本地或者網絡中實現端到端的通信,目前已經發布了第7版。OSGI有很多優點諸如熱部署,類隔離,高內聚,低耦合的優勢,但同時也帶來了性能損耗,而且基于OSGI目前的規范繁多復雜,開發門檻較高。

組成:執行環境,安全層,模塊層,生命周期層,服務層,框架API

核心服務:

事件服務(Event Admin Service),

包管理服務(Package Admin Service)

日志服務(Log Service)

配置管理服務(Configuration Admin Service)

HTTP服務(HTTP Service)

用戶管理服務(User Admin Service)

設備訪問服務(Device Access Service)

IO連接器服務(IO Connector Service)

聲明式服務(Declarative Services)

其他OSGi標準服務

本節我們討論的核心是熱部署,所以我們不打算在這里講解全部得OSGI技術,在上文實現熱部署后我們重點來剖析OSGI關于熱部署的機制。至于OSGI模塊化技術和java9的模塊化的對比和關聯,后面有時間會開個專題專門介紹模塊化技術。

從類加載器技術應用的角度切入我們知道OSGI規范也是打破雙親委派機制,除了框架層面需要依賴JVM默認類加載器之外,其他Bundle(OSGI定義的模塊單元)都是由各自的類加載器來加載,而OSGI框架就負責模塊生命周期,模塊交互這些核心功能,同時創建各個Bundle的類加載器,用于直接加載Bundle定義的jar包。由于打破雙親委派模式,Bundle類加載器不再是雙親委派模型中的樹狀結構,而是進一步發展為更加復雜的網狀結構(因為各個Bundle之間有相互依賴關系),當收到類加載請求時,OSGi將按照下面的順序進行類搜索:

1)將以java.*開頭的類委派給父類加載器加載。

2)否則,將委派列表名單內(比如sun或者javax這類核心類的包加入白名單)的類委派給父類加載器加載。

3)否則,將Import列表中的類委派給Export這個類的Bundle的類加載器加載。

4)否則,查找當前Bundle的ClassPath,使用自己的類加載器加載。

5)否則,查找類是否在自己的Fragment Bundle(OSGI框架緩存包)中,如果在,則委派給Fragment Bundle的類加載器加載。

6)否則,查找Dynamic Import列表的Bundle,委派給對應Bundle的類加載器加載。

7)否則,類查找失敗。

這一系列的類加載操作,其實跟我們上節實現的自定義類加載技術本質上是一樣的,只不過實現OSGI規范的框架需要提供模塊之間的注冊通信組件,還有模塊的生命周期管理,版本管理。OSGI也只是JVM上面運行的一個普通應用實例,只不過通過模塊內聚,版本管理,服務依賴一系列的管理,實現了模塊的即時更新,實現了熱部署。

其他熱部署解決方案多數也是利用類加載器的特點做文章,當然不止是類加載器,還會應用字節碼技術,下面我們主要簡單列舉應用類加載器實現的熱部署解決方案。

  • Groovy

Groovy兼顧動態腳本語言的功能,使用的時候無外乎也是通過GroovyClassLoader來加載腳本文件,轉為JVM的類對象。那么每次更新groovy腳本就可以動態更新應用,也就達到了熱部署的功能了。

Class groovyClass = classLoader.parseClass(new GroovyCodeSource(sourceFile)); GroovyObject instance = (GroovyObject)groovyClass.newInstance();//proxy
  • Clojure

  • JSP

    JSP其實翻譯為Servlet后也是由對應新的類加載器去加載,這跟我們上節講的流程一模一樣,所以這里就補展開講解了。

介紹完熱部署技術,可能很多同學對熱部署的需求已經沒有那么強烈,畢竟熱部署過程中帶來的弊端也不容忽視,比如替換舊的類加載器過程會產生大量的內存碎片,導致JVM進行高負荷的GC工作,反復進行熱部署還會導致JVM內存不足而導致內存溢出,有時候甚至還不如直接重啟應用來得更快一點,而且隨著分布式架構的演進和微服務的流行,應用重啟也早就實現服務編排化,配合豐富的部署策略,也可以同樣保證系統穩定持續服務,我們更多的是通過熱部署技術來深刻認識到JVM加載類的技術演進。

4.3.2類隔離

背景

先介紹一下類隔離的背景,我們費了那么大的勁設計出類加載器,如果只是用于加載外部類字節流那就過于浪費了。通常我們的應用依賴不同的第三方類庫經常會出現不同版本的類庫,如果只是使用系統內置的類加載器的話,那么一個類庫只能加載唯一的一個版本,想加載其他版本的時候會從緩存里面發現已經存在而停止加載。但是我們的不同業務以來的往往是不同版本的類庫,這時候就會出現ClassNotFoundException。為什么只有運行的是才會出現這個異常呢,因為編譯的時候我們通常會使用MAVEN等編譯工具把沖突的版本排除掉。另外一種情況是WEB容器的內核依賴的第三方類庫需要跟應用依賴的第三方類庫隔離開來,避免一些安全隱患,不然如果共用的話,應用升級依賴版本就會導致WEB容器不穩定。

基于以上的介紹我們知道類隔離實在是剛需,那么接下來介紹一下如何實現這個剛需。

原理

首先我們要了解一下原理,其實原理很簡單,真的很簡單,請允許我總結為“唯一標識原理”。我們知道內存里面定位類實例的坐標<類加載器,類全限定名>。那么由這兩個因子組合起來我們可以得出一種普遍的應用,用不同類加載器來加載類相同類(類全限定名一致,版本不一致)是可以實現的,也就是在JVM看來,有相同類全名的類是完全不同的兩個實例,但是在業務視角我們卻可以視為相同的類。

public static void main(String[] args) {Class<?> userClass1 = User.class;Class<?> userClass2 = new DynamicClassLoader("target/classes").load("qj.blog.classreloading.example1.StaticInt$User");out.println("Seems to be the same class:");out.println(userClass1.getName());out.println(userClass2.getName());out.println();out.println("But why there are 2 different class loaders:");out.println(userClass1.getClassLoader());out.println(userClass2.getClassLoader());out.println();User.age = 11;out.println("And different age values:");out.println((int) ReflectUtil.getStaticFieldValue("age", userClass1));out.println((int) ReflectUtil.getStaticFieldValue("age", userClass2)); }public static class User {public static int age = 10; }

實現

原理很簡單,比如我們知道Spring容器本質就是一個生產和管理bean的集合對象,但是卻包含了大量的優秀設計模式和復雜的框架實現。同理隔離容器雖然原理很簡單,但是要實現一個高性能可擴展的高可用隔離容器,卻不是那么簡單。我們上文談的場景是在內存運行的時候才發現問題,介紹內存隔離技術之前,我們先普及更為通用的沖突解決方法。

  • 沖突排除

    沖突總是先發生在編譯時期,那么基本Maven工具可以幫我們完成大部分的工作,Maven的工作模式就是將我們第三方類庫的所有依賴都依次檢索,最終排除掉產生沖突的jar包版本。

  • 沖突適配

    當我們無法通過簡單的排除來解決的時候,另外一個方法就是重新裝配第三方類庫,這里我們要介紹一個開源工具jarjar (https://github.com/shevek/jarjar)。該工具包可以通過字節碼技術將我們依賴的第三方類庫重命名,同時修改代碼里面對第三方類庫引用的路徑。這樣如果出現同名第三方類庫的話,通過該“硬編碼”的方式修改其中一個類庫,從而消除了沖突。

  • 沖突隔離

    上面兩種方式在小型系統比較適合,也比較敏捷高效。但是對于分布式大型系統的話,通過硬編碼方式來解決沖突就難以完成了。辦法就是通過隔離容器,從邏輯上區分類庫的作用域,從而對內存的類進行隔離。

5.內存管理

5.1內存結構

5.1.1邏輯分區

JVM內存從應用邏輯上可分為如下區域。

  • 程序計數器:字節碼行號指示器,每個線程需要一個程序計數器

  • 虛擬機棧:方法執行時創建棧幀(存儲局部變量,操作棧,動態鏈接,方法出口)編譯時期就能確定占用空間大小,線程請求的棧深度超過jvm運行深度時拋StackOverflowError,當jvm棧無法申請到空閑內存時拋OutOfMemoryError,通過-Xss,-Xsx來配置初始內存

  • 本地方法棧:執行本地方法,如操作系統native接口

  • 堆:存放對象的空間,通過-Xmx,-Xms配置堆大小,當堆無法申請到內存時拋OutOfMemoryError

  • 方法區:存儲類數據,常量,常量池,靜態變量,通過MaxPermSize參數配置

  • 對象訪問:初始化一個對象,其引用存放于棧幀,對象存放于堆內存,對象包含屬性信息和該對象父類、接口等類型數據(該類型數據存儲在方法區空間,對象擁有類型數據的地址)

而實際上JVM內存分類實際上的物理分區還有更為詳細,整體上分為堆內存和非堆內存,具體介紹如下。

5.1.2 內存模型

堆內存

堆內存是運行時的數據區,從中分配所有java類實例和數組的內存,可以理解為目標應用依賴的對象。堆在JVM啟動時創建,并且在應用程序運行時可能會增大或減小。可以使用-Xms 選項指定堆的大小。堆可以是固定大小或可變大小,具體取決于垃圾收集策略。可以使用-Xmx選項設置最大堆大小。默認情況下,最大堆大小設置為64 MB。

JVM堆內存在物理上分為兩部分:新生代和老年代。新生代是為分配新對象而保留堆空間。當新生代占用完時,Minor GC垃圾收集器會對新生代區域執行垃圾回收動作,其中在新生代中生活了足夠長的所有對象被遷移到老年代,從而釋放新生代空間以進行更多的對象分配。此垃圾收集稱為 Minor GC。新生代分為三個子區域:伊甸園Eden區和兩個幸存區S0和S1。

關于新生代內存空間:

  • 大多數新創建的對象都位于Eden區內存空間

  • 當Eden區填滿對象時,執行Minor GC并將所有幸存對象移動到其中一個幸存區空間

  • Minor GC還會檢查幸存區對象并將其移動到其他幸存者空間,也即是幸存區總有一個是空的

  • 在多次GC后還存活的對象被移動到老年代內存空間。至于經過多少次GC晉升老年代則由參數配置,通常為15

當老年區填滿時,老年區同樣會執行垃圾回收,老年區還包含那些經過多Minor GC后還存活的長壽對象。垃圾收集器在老年代內存中執行的回收稱為Major GC,通常需要更長的時間。

非堆內存

JVM的堆以外內存稱為非堆內存。也即是JVM自身預留的內存區域,包含JVM緩存空間,類結構如常量池、字段和方法數據,方法,構造方法。類非堆內存的默認最大大小為64 MB。可以使用-XX:MaxPermSize VM選項更改此選項,非堆內存通常包含如下性質的區域空間:

  • 元空間(Metaspace)

在Java 8以上版本已經沒有Perm Gen這塊區域了,這也意味著不會再由關于“java.lang.OutOfMemoryError:PermGen”內存問題存在了。與駐留在Java堆中的Perm Gen不同,Metaspace不是堆的一部分。類元數據多數情況下都是從本地內存中分配的。默認情況下,元空間會自動增加其大小(直接又底層操作系統提供),而Perm Gen始終具有固定的上限。可以使用兩個新標志來設置Metaspace的大小,它們是:“ -?XX:MetaspaceSize?”和“?-XX:MaxMetaspaceSize?”。Metaspace背后的含義是類的生命周期及其元數據與類加載器的生命周期相匹配。也就是說,只要類加載器處于活動狀態,元數據就會在元數據空間中保持活動狀態,并且無法釋放。

  • 代碼緩存

運行Java程序時,它以分層方式執行代碼。在第一層,它使用客戶端編譯器(C1編譯器)來編譯代碼。分析數據用于服務器編譯的第二層(C2編譯器),以優化的方式編譯該代碼。默認情況下,Java 7中未啟用分層編譯,但在Java 8中啟用了分層編譯。實時(JIT)編譯器將編譯的代碼存儲在稱為代碼緩存的區域中。它是一個保存已編譯代碼的特殊堆。如果該區域的大小超過閾值,則該區域將被刷新,并且GC不會重新定位這些對象。Java 8中已經解決了一些性能問題和編譯器未重新啟用的問題,并且在Java 7中避免這些問題的解決方案之一是將代碼緩存的大小增加到一個永遠不會達到的程度。

  • 方法區

方法區域是Perm Gen中空間的一部分,用于存儲類結構(運行時常量和靜態變量)以及方法和構造函數的代碼。

  • 內存池

內存池由JVM內存管理器創建,用于創建不可變對象池。內存池可以屬于Heap或Perm Gen,具體取決于JVM內存管理器實現。

  • 常量池

常量包含類運行時常量和靜態方法,常量池是方法區域的一部分。

  • Java堆棧內存

Java堆棧內存用于執行線程。它們包含特定于方法的特定值,以及對從該方法引用的堆中其他對象的引用。

  • Java堆內存配置項

Java提供了許多內存配置項,我們可以使用它們來設置內存大小及其比例,常用的如下:

VM Switch描述
-?Xms用于在JVM啟動時設置初始堆大小
-Xmx用于設置最大堆大小
-Xmn設置新生區的大小,剩下的空間用于老年區
-XX:PermGen用于設置永久區存初始大小
-XX:MaxPermGen用于設置Perm Gen的最大尺寸
-XX:SurvivorRatio提供Eden區域的比例
-XX:NewRatio用于提供老年代/新生代大小的比例,默認值為2

5.2垃圾回收

5.2.1垃圾回收策略

流程

垃圾收集是釋放堆中的空間以分配新對象的過程。垃圾收集器是JVM管理的進程,它可以查看內存中的所有對象,并找出程序任何部分未引用的對象,刪除并回收空間以分配給其他對象。通常會經過如下步驟:

  • 標記:標記哪些對象被使用,哪些已經是無法觸達的無用對象

  • 刪除:刪除無用對象并回收要分配給其他對象

  • 壓縮:性能考慮,在刪除無用的對象后,會將所有幸存對象集中移動到一起,騰出整段空間

策略

虛擬機棧、本地棧和程序計數器在編譯完畢后已經可以確定所需內存空間,程序執行完畢后也會自動釋放所有內存空間,所以不需要進行動態回收優化。JVM內存調優主要針對堆和方法區兩大區域的內存。通常對象分為Strong、sfot、weak和phantom四種類型,強引用不會被回收,軟引用在內存達到溢出邊界時回收,弱引用在每次回收周期時回收,虛引用專門被標記為回收對象,具體回收策略如下:

  • 對象優先在Eden區分配:

  • 新生對象回收策略Minor GC(頻繁)

  • 老年代對象回收策略Full GC/Major GC(慢)

  • 大對象直接進入老年代:超過3m的對象直接進入老年區 -XX:PretenureSizeThreshold=3145728(3M)

  • 長期存貨對象進入老年區:
    Survivor區中的對象經歷一次Minor GC年齡增加一歲,超過15歲進入老年區
    -XX:MaxTenuringThreshold=15

  • 動態對象年齡判定:設置Survivor區對象占用一半空間以上的對象進入老年區

算法

垃圾收集有如下常用的算法:

  • 標記-清除

  • 復制

  • 標記-整理

  • 分代收集(新生用復制,老年用標記-整理)

5.2.2 垃圾回收器

分類

  • serial收集器:單線程,主要用于client模式

  • ParNew收集器:多線程版的serial,主要用于server模式

  • Parallel Scavenge收集器:線程可控吞吐量(用戶代碼時間/用戶代碼時間+垃圾收集時間),自動調節吞吐量,用戶新生代內存區

  • Serial Old收集器:老年版本serial

  • Parallel Old收集器:老年版本Parallel Scavenge

  • CMS(Concurrent Mark Sweep)收集器:停頓時間短,并發收集

  • G1收集器:分塊標記整理,不產生碎片

配置

  • 串行GC(-XX:+ UseSerialGC):串行GC使用簡單的標記-掃描-整理方法,用于新生代和老年代的垃圾收集,即Minor和Major GC

  • 并行GC(-XX:+ UseParallelGC):并行GC與串行GC相同,不同之處在于它為新生代垃圾收集生成N個線程,其中N是系統中的CPU核心數。我們可以使用-XX:ParallelGCThreads = n JVM選項來控制線程數

  • 并行舊GC(-XX:+ UseParallelOldGC):這與Parallel GC相同,只是它為新生代和老年代垃圾收集使用多個線程

  • 并發標記掃描(CMS)收集器(-XX:+ UseConcMarkSweepGC):CMS也稱為并發低暫停收集器。它為老年代做垃圾收集。CMS收集器嘗試通過在應用程序線程內同時執行大多數垃圾收集工作來最小化由于垃圾收集而導致的暫停。年輕一代的CMS收集器使用與并行收集器相同的算法。我們可以使用-XX限制CMS收集器中的線程數 :ParallelCMSThreads = n

  • G1垃圾收集器(-XX:+ UseG1GC):G1從長遠看要是替換CMS收集器。G1收集器是并行,并發和遞增緊湊的低暫停垃圾收集器。G1收集器不像其他收集器那樣工作,并且沒有年輕和老一代空間的概念。它將堆空間劃分為多個大小相等的堆區域。當調用垃圾收集器時,它首先收集具有較少實時數據的區域,因此稱為“Garbage First”也即是G1

6.執行引擎

6.1執行流程

類加載器加載的類文件字節碼數據流由基于JVM指令集架構的執行引擎來執行。執行引擎以指令為單位讀取Java字節碼。我們知道匯編執行的流程是CPU執行每一行的匯編指令,同樣JVM執行引擎就像CPU一個接一個地執行機器命令。字節碼的每個命令都包含一個1字節的OpCode和附加的操作數。執行引擎獲取一個OpCode并使用操作數執行任務,然后執行下一個OpCode。但Java是用人們可以理解的語言編寫的,而不是用機器直接執行的語言編寫的。因此執行引擎必須將字節碼更改為JVM中的機器可以執行的語言。字節碼可以通過以下兩種方式之一轉化為合適的語言。

  • 解釋器:逐個讀取,解釋和執行字節碼指令。當它逐個解釋和執行指令時,它可以快速解釋一個字節碼,但是同時也只能相對緩慢的地執行解釋結果,這是解釋語言的缺點。

  • JIT(實時)編譯器:引入了JIT編譯器來彌補解釋器的缺點。執行引擎首先作為解釋器運行,并在適當的時候,JIT編譯器編譯整個字節碼以將其更改為本機代碼。之后,執行引擎不再解釋該方法,而是直接使用本機代碼執行。本地代碼中的執行比逐個解釋指令要快得多。由于本機代碼存儲在高速緩存中,因此可以快速執行編譯的代碼。

但是,JIT編譯器編譯代碼需要花費更多的時間,而不是解釋器逐個解釋代碼。因此,如果代碼只執行一次,最好是選擇解釋而不是編譯。因此,使用JIT編譯器的JVM在內部檢查方法執行的頻率,并僅在頻率高于某個級別時編譯方法。

JVM規范中未定義執行引擎的運行方式。因此,JVM廠商使用各種技術改進其執行引擎,并引入各種類型的JIT編譯器。 大多數JIT編譯器運行如下圖所示:

JIT編譯器將字節碼轉換為中間級表達式IR,以執行優化,然后將表達式轉換為本機代碼。Oracle Hotspot VM使用名為Hotspot Compiler的JIT編譯器。它被稱為Hotspot,因為Hotspot Compiler通過分析搜索需要以最高優先級進行編譯的“Hotspot”,然后將熱點編譯為本機代碼。如果不再頻繁調用編譯了字節碼的方法,換句話說,如果該方法不再是熱點,則Hotspot VM將從緩存中刪除本機代碼并以解釋器模式運行。Hotspot VM分為服務器VM和客戶端VM,兩個VM使用不同的JIT編譯器。

大多數Java性能改進都是通過改進執行引擎來實現的。除了JIT編譯器之外,還引入了各種優化技術,因此可以不斷改進JVM性能。初始JVM和最新JVM之間的最大區別是執行引擎。

下面我們通過下圖可以看出JAVA執行的流程。

6.2棧幀結構

每個方法調用開始到執行完成的過程,對應這一個棧幀在虛擬機棧里面從入棧到出棧的過程。

  • 棧幀包含:局部變量表,操作數棧,動態連接,方法返回

  • 方法調用:方法調用不等于方法執行,而且確定調用方法的版本。

  • 方法調用字節碼指令:invokestatic,invokespecial,invokevirtual,invokeinterface

  • 靜態分派:靜態類型,實際類型,編譯器重載時通過參數的靜態類型來確定方法的版本。(選方法)

  • 動態分派:invokevirtual指令把類方法符號引用解析到不同直接引用上,來確定棧頂的實際對象(選對象)

  • 單分派:靜態多分派,相同指令有多個方法版本。

  • 多分派:動態單分派,方法接受者只能確定唯一一個。

下圖是JVM實例執行方法是的內存布局。

6.3早期編譯

  • javac編譯器:解析與符號表填充,注解處理,生成字節碼

  • java語法糖:語法糖有助于代碼開發,但是編譯后就會解開糖衣,還原到基礎語法的class二進制文件
    重載要求方法具備不同的特征簽名(不包括返回值),但是class文件中,只要描述不是完全一致的方法就可以共存。

6.4晚期編譯

HotSpot虛擬機內的即時編譯
解析模式 -Xint
編譯模式 -Xcomp
混合模式 Mixed mode
分層編譯:解釋執行 -> C1(Client Compiler)編譯 -> C2編譯(Server Compiler)
觸發條件:基于采樣的熱點探測,基于計數器的熱點探測

7.性能調優

7.1調優原則

我們知道調優的前提是,程序沒有達到我們的預期要求,那么第一步要做的是衡量我們的預期。程序不可能十全十美,我們要做的是通過各種指標來衡量系統的性能,最終整體達到我們的要求。

7.1.1 環境

首先我們要了解系統的運行環境,包括操作系統層面的差異,JVM版本,位數,乃至于硬件的時鐘周期,總線設計甚至機房溫度,都可能是我們需要考慮的前置條件。

7.1.2 度量

首先我們要先給出系統的預期指標,在特定的硬件/軟件的配置,然后給出目標指標,比如系統整體輸出接口的QPS,RT,或者更進一層,IO讀寫,cpu的load指標,內存的使用率,GC情況都是我們需要預先考察的對象。

7.1.3 監測

確定了環境前置條件,分析了度量指標,第三步是通過工具來監測指標,下一節提供了常用JVM調優工具,可以通過不同工具的組合來發現定位問題,結合JVM的工作機制已經操作系統層面的調度流程,按圖索驥來發現問題,找出問題后才能進行優化。

7.1.4 原則

總體的調優原則如下圖

圖片來源《Java Performance》

7.2 調優參數

上節給出了JVM性能調優的原則,我們理清思路后應用不同的JVM工具來發現系統存在的問題,下面列舉的是常用的JVM參數,通過這些參數指標可以更快的幫助我們定位出問題所在。

7.2.1內存查詢

最常見的與性能相關的做法之一是根據應用程序要求初始化堆內存。這就是我們應該指定最小和最大堆大小的原因。以下參數可用于實現它:

-Xms<heap size>[unit] -Xmx<heap size>[unit]

unit表示要初始化內存(由堆大小表示)的單元。單位可以標記為GB的“g”,MB的“m”和KB的“k”。例如JVM分配最小2 GB和最大5 GB:

-Xms2G -Xmx5G

從Java 8開始Metaspace的大小未被定義,一旦達到限制JVM會自動增加它,為了避免不必要的不穩定性,我們可以設置Metaspace大小:

-XX:MaxMetaspaceSize=<metaspace size>[unit]

默認情況下YG的最小大小為1310?MB,最大大小不受限制,我們可以明確地指定它們:

-XX:NewSize=<young size>[unit] -XX:MaxNewSize=<young size>[unit]

7.2.2垃圾回收

JVM有四種類型的GC實現:

  • 串行垃圾收集器

  • 并行垃圾收集器

  • CMS垃圾收集器

  • G1垃圾收集器

可以使用以下參數聲明這些實現:

-XX:+UseSerialGC -XX:+UseParallelGC -XX:+USeParNewGC -XX:+UseG1GC

7.2.3GC記錄

要嚴格監視應用程序運行狀況,我們應始終檢查JVM的垃圾收集性能,使用以下參數,我們可以記錄GC活動:

-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=< number of log files > -XX:GCLogFileSize=< file size >[ unit ] -Xloggc:/path/to/gc.log

UseGCLogFileRotation指定日志文件滾動的政策,就像log4j的,s4lj等?NumberOfGCLogFiles表示單個應用程序記錄生命周期日志文件的最大數量。GCLogFileSize指定文件的最大大小。?loggc表示其位置。這里要注意的是,還有兩個可用的JVM參數(-XX:+ PrintGCTimeStamps-XX:+ PrintGCDateStamps),可用于在GC日志中打印日期時間戳。

7.2.4內存溢出

大型應用程序面臨內存不足的錯誤是很常見的,這是一個非常關鍵的場景,很難復制以解決問題。

這就是JVM帶有一些參數的原因,這些參數將堆內存轉儲到一個物理文件中,以后可以用它來查找泄漏:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./java_pid<pid>.hprof -XX:OnOutOfMemoryError="< cmd args >;< cmd args >" -XX:+UseGCOverheadLimit

這里有幾點需要注意:

  • 在OutOfMemoryError的情況下,?HeapDumpOnOutOfMemoryError指示JVM將堆轉儲到物理文件中

  • HeapDumpPath表示要寫入文件的路徑; 任何文件名都可以給出; 但是如果JVM在名稱中找到?標記,則導致內存不足錯誤的進程ID將以?.hprof格式附加到文件名

  • OnOutOfMemoryError用于發出緊急命令,以便在出現內存不足錯誤時執行; 應該在cmd args的空間中使用正確的命令。例如,如果我們想在內存不足時重新啟動服務器,我們可以設置參數:

-XX:OnOutOfMemoryError="shutdown -r"
  • UseGCOverheadLimit是一種策略,用于限制在拋出?OutOfMemory錯誤之前在GC中花費的VM時間的比例

7.2.5其他配置

  • -server:啟用“Server Hotspot VM”; 默認情況下,此參數在64位JVM中使用

  • -XX:+ UseStringDeduplication: Java 8引入了這個JVM參數,通過創建相同?String的太多實例來減少不必要的內存使用?;?這通過將重復的?String值減少到單個全局char []數組來優化堆內存

  • -XX:+ UseLWPSynchronization:設置基于?LWP(輕量級進程)的同步策略而不是基于線程的同步

  • -XX:LargePageSizeInBytes:設置用于Java堆的大頁面大小; 它采用GB / MB / KB的參數; 通過更大的頁面大小,我們可以更好地利用虛擬內存硬件資源; 但是這可能會導致?PermGen的空間大小增加,從而可以強制減小Java堆空間的大小

  • -XX:MaxHeapFreeRatio:設置?GC后堆的最大自由百分比,以避免收縮

  • -XX:MinHeapFreeRatio:設置?GC后堆的最小自由百分比以避免擴展,監視堆使用情況

  • -XX:SurvivorRatio:Eden區 /幸存者空間大小的比例

  • -XX:+ UseLargePages:如果系統支持,則使用大頁面內存; 如果使用此JVM參數,OpenJDK 7往往會崩潰

  • -XX:+ UseStringCache:啟用字符串池中可用的常用分配字符串的緩存

  • -XX:+ UseCompressedStrings:對?String對象使用?byte []類型,可以用純ASCII格式表示

  • -XX:+ OptimizeStringConcat:它盡可能優化字符串連接操作

7.3 調優工具

7.3.1命令行工具

  • 虛擬機進程狀況工具:jps -lvm

  • 診斷命令工具:jcmd

    用來發送診斷命令請求到JVM,這些請求是控制Java的運行記錄,它必須在運行JVM的同一臺機器上使用,并且具有用于啟動JVM的相同有效用戶和分組,可以使用以下命令創建堆轉儲(hprof轉儲):

    jcmd?GC.heap_dump filename =

  • 虛擬機統計信息監視工具:jstat

    提供有關運行的應用程序的性能和資源消耗的信息。在診斷性能問題時,可以使用該工具,特別是與堆大小調整和垃圾回收相關的問題。jstat不需要虛擬機啟動任何特殊配置。

    jstat -gc pid interval count

  • java配置信息工具:jinfo

    jinfo -flag pid

  • java內存映像工具:jmap

    用于生成堆轉儲文件

    jmap -dump:format=b,file=java.bin pid

  • 虛擬機堆轉儲快照分析工具:jhat

    jhat file 分析堆轉儲文件,通過瀏覽器訪問分析文件

  • java堆棧跟蹤工具:jstack

    用于生成虛擬機當前時刻的線程快照threaddump或者Javacore

    jstack [ option ] vmid

  • 堆和CPU分析工具:HPROF

    HPROF是每個JDK版本附帶的堆和CPU分析工具。它是一個動態鏈接庫(DLL),它使用Java虛擬機工具接口(JVMTI)與JVM連接。該工具將分析信息以ASCII或二進制格式寫入文件或套接字。HPROF工具能夠顯示CPU使用情況,堆分配統計信息和監視爭用配置文件。此外,它還可以報告JVM中所有監視器和線程的完整堆轉儲和狀態。在診斷問題方面,HPROF在分析性能,鎖爭用,內存泄漏和其他問題時非常有用。

    java -agentlib:hprof = heap = sites target.class

7.3.2可視化工具

  • jconsole

  • jvisualvm

8.字節增強

我們從類加載的應用介紹了熱部署和類隔離兩大應用場景,但是基于類加載器的技術始終只是獨立于JVM內核功能而存在的,也就是所有實現都只是基于最基礎的類加載機制,并無應用其他JVM 高級特性,本章節我們開始從字節增強的層面介紹JVM的一些高級特性。

說到字節增強我們最先想到的是字節碼,也就是本文最開頭所要研究的class文件,任何合法的源碼編譯成class后被類加載器加載進JVM的方法區,也就是以字節碼的形態存活在JVM的內存空間。這也就是我們為什么現有講明白類的結構和加載過程,而字節碼增強技術不只是在內存里面對class的字節碼進行操縱,更為復雜的是class聯動的上下游對象生命周期的管理。

首先我們回憶一下我們開發過程中最為熟悉的一個場景就是本地debug調試代碼。可能很多同學都已經習慣在IDE上對某句代碼打上斷點,然后逐步往下追蹤代碼執行的步驟。我們進一步想想,這個是怎么實現的,是一股什么樣的力量能把已經跑起來的線程踩下剎車,一步一步往前挪?我們知道線程運行其實就是在JVM的棧空間上不斷的把代碼對應的JVM指令集不斷的送到CPU執行。那能阻止這個流程的力量也肯定是發生在JVM范圍內,所以我們可以很輕松的預測到這肯定是JVM提供的機制,而不是IDE真的有這樣的能力,只不過是JVM把這種能力封裝成接口暴露出去,然后提供給IDE調用,而IDE只不過是通過界面交互來調用這些接口而已。那么下面我們就來介紹JVM這種重要的能力。

8.1JPDA

上面所講的JVM提供的程序運行斷點能力,其實JVM提供的一個工具箱JVMTI(JVM TOOL Interface)提供的接口,而這個工具箱是一套叫做JPDA的架構定義的,本節我們就來聊聊JPDA。

JPDA(Java Platform Debugger Architecture)Java平臺調試架構,既不是一個應用程序,也不是調試工具,而是定義了一系列設計良好的接口和協議用于調試java代碼,我們將會從三個層面來講解JPDA。

8.1.1概念

  • JVMTI

    JVMTI(Java Virtual Machine Tool Interface)Java 虛擬機調試接口,處于最底層,是我們上文所提到的JVM開放的能力,JPDA規定了JDK必須提供一個叫做JVMTI(Java6之前是由JVMPI和JVMDI組成,Java6開始廢棄掉統一為JVMTI)的工具箱,也就是定義了一系列接口能力,比如獲取棧幀、設置斷點、斷點響應等接口,具體開放的能力參考JVMDI官方API文檔。

  • JDWP

    JDWP(Java Debug Wire Protocol)Java 調試連線協議,存在在中間層,定義信息格式,定義調試者和被調試程序之間請求的協議轉換,位于JDI下一層,JDI更為抽象,JDWP則關注實現。也就是說JVM定義好提供的能力,但是如何調用JVM提供的接口也是需要規范的,就比如我們Servlet容器也接收正確合法的HTTP請求就可以成功調用接口。JPDA同樣也規范了調用JVMTI接口需要傳入數據的規范,也就是請求包的格式,類別HTTP的數據包格式。但是JPDA并不關心請求來源,也就是說只要調用JVMTI的請求方式和數據格式對了就可以,不論是來做遠程調用還是本地調用。JDWP制定了調試者和被調試應用的字節流動機制,但沒有限定具體實現,可以是遠程的socket連接,或者本機的共享內存,當然還有自定義實現的通信協議。既然只是規范了調用協議,并不局限請求來源,而且也沒限制語言限制,所以非java語言只要發起調用符合規范就可以,這個大大豐富了異構應用場景,具體的協議細節可以參考JDWP官方規范文檔。

  • JDI

    JDI(Java Debug Interface)Java調試接口處在最上層,基于Java開發的調試接口,也就是我們調試客戶端,客戶端代碼封裝在jdk下面tools.jar的com.sun.jdi包里面,java程序可以直接調用的接口集合,具體提供的功能可以參考JDI官方API文檔。

8.1.2原理

介紹完JPDA的架構體系后,我們了解到JAVA調試平臺各個層級的作用,這一節我們更近一步講解JPDA各個層面的工作原理,以及三個層級結合起來時如何交互的。

JVMTI

我們JVMTI是JVM提供的一套本地接口,包含了非常豐富的功能,我們調試和優化代碼需要操作JVM,多數情況下就是調用到JVMTI,從官網我們可以看到,JVMTI包含了對JVM線程/內存/堆/棧/類/方法/變量/事件/定時器處理等的20多項功能。但其實我們通常不是直接調用JVMTI,而是創建一個代理客戶端,我們可以自由的定義對JVMTI的操作然后打包到代理客戶端里面如libagent.so。當目標程序執行時會啟動JVM,這個時候在目標程序運行前會加載代理客戶端,所以代理客戶端是跟目標程序運行在同一個進程上。這樣一來外部請求就通過代理客戶端間接調用到JVMTI,這樣的好處是我們可以在客戶端Agent里面定制高級功能,而且代理客戶端編譯打包成一個動態鏈接庫之后可以復用,提高效率。我們簡單描述一下代理客戶端Agent的工作流程。

建立代理客戶端首先需要定義Agent的入口函數,猶如Java類的main方法一樣:

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved);

然后JVM在啟動的時候就會把JVMTI的指針JavaVM傳給代理的入口函數,options則是傳參,有了這個指針后代理就可以充分調用JVMTI的函數了。

//設置斷點,參數是調試目標方法和行數位置 jvmtiError SetBreakpoint(jvmtiEnv* env,jmethodID method,jlocation location); //當目標程序執行到指定斷點,目標線程則被掛起 jvmtiError SuspendThread(jvmtiEnv* env,jthread thread);

當然除了JVM啟動時可以加載代理,運行過程中也是可以的,這個下文我們講字節碼增強還會再說到。

JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char *options, void *reserved);

有興趣的同學可以自己動手寫一個Agent試試,通過調用JVMTI接口可以實現自己定制化的調試工具。

JDWP

上文我們知道調用JVMTI需要建立一個代理客戶端,但是假如我建立了包含通用功能的Agent想開發出去給所有調試器使用,有一種方式是資深開發者通過閱讀我的文檔后進行開發調用,還有另外一種方式就是我在我的Agent里面加入了JDWP協議模塊,這樣調試器就可以不用關心我的接口細節,只需按照閱讀的協議發起請求即可。JDWP是調試器和JVM中間的協議規范,類似HTTP協議一樣,JDWP也定義規范了握手協議和報文格式。

調試器發起請求的握手流程:

1)調試器發送一段包含“JDWP-Handshake”的14個bytes的字符串

2)JVM回復同樣的內容“JDWP-Handshake”

完成握手流程后就可以像HTTP一樣向JVM的代理客戶端發送請求數據,同時回復所需參數。請求和回復的數據幀也有嚴格的結構,請求的數據格式為Command Packet,回復的格式為Reply Packet,包含包頭和數據兩部分,具體格式參考官網。實際上JDWP卻是也是通過建立代理客戶端來實現報文格式的規范,也就是JDWP Agent 里面的JDWPTI實現了JDWP對協議的定義。JDWP的功能是由JDWP傳輸接口(Java Debug Wire Protocol Transport Interface)實現的,具體流程其實跟JVMTI差不多,也是講JDWPTI編譯打包成代理庫后,在JVM啟動的時候加載到目標進程。那么調試器調用的過程就是JDWP Agent接收到請求后,調用JVMTI Agent,JDWP負責定義好報文數據,而JDWPTI則是具體的執行命令和響應事件。

JDI

前面已經解釋了JVMTI和JDWP的工作原理和交互機制,剩下的就是搞清楚面向用戶的JDI是如何運行的。首先JDI位于JPDA的最頂層入口,它的實現是通過JAVA語言編寫的,所以可以理解為Java調試客戶端對JDI接口的封裝調用,比如我們熟悉的IDE界面啟動調試,或者JAVA的命令行調試客戶端JDB。

通常我們設置好目標程序的斷點之后啟動程序,然后通過調試器啟動程序之前,調試器會先獲取JVM管理器,然后通過JVM管理器對象virtualMachineManager獲取連接器Connector,調試器與虛擬機獲得鏈接后就可以啟動目標程序了。如下代碼:

VirtualMachineManager virtualMachineManager = Bootstrap.virtualMachineManager();

JDI完成調試需要實現的功能有三個模塊:數據、鏈接、事件

  • 數據

    調試器要調試的程序在目標JVM上,那么調試之前肯定需要將目標程序的執行環境同步過來,不然我們壓根就不知道要調試什么,所以需要一種鏡像機制,把目標程序的堆棧方法區包含的數據以及接收到的事件請求都映射到調試器上面。那么JDI的底層接口Mirror就是干這樣的事,具體數據結構可以查詢文檔。

  • 鏈接

    我們知道調試器跟目標JVM直接的通訊是雙向的,所以鏈接雙方都可以發起。一個調試器可以鏈接多個目標JVM,但是一個目標虛擬機只能提供給一個調試器,不然就亂套了不知道聽誰指令了。JDI定義三種鏈接器:啟動鏈接器(LaunchingConnector)、依附鏈接器(AttachingConnector)、監聽鏈接器(ListeningConnector)和。分別對應的場景是目標程序JVM啟動時發起鏈接、調試器中途請求接入目標程序JVM和調試器監聽到被調試程序返回請求時發起的鏈接。

  • 事件

    也就是調試過程中對目標JVM返回請求的響應。

講解完JPDA體系的實現原理,我們再次梳理一下調試的整個流程:

調試器 —> JDI客戶端 —> JDWP Agent—> JVMTI Agent —>> JVMTI —> Application

8.1.3 實現

現在我們已經對整個JPDA結構有了深入理解,接下來我們就通過對這些樸素的原理來實現程序的斷點調試。當然我們不會在這里介紹從IDE的UI斷點調試的過程,因為對這套是使用已經非常熟悉了,我們知道IDE的UI斷點調試本質上是調試器客戶端對JDI的調用,那我們就通過一個調試的案例來解釋一下這背后的原理。

搭建服務

首先我們需要先搭建一個可供調試的web服務,這里我首選springboot+來搭建,通過官網生成樣例project或者maven插件都可以,具體的太基礎的就不在這里演示,該服務只提供一個Controller包含的一個簡單方法。如果使用Tomcat部署,則可以通過自有的開關catalina jpda start來啟動debug模式。

package com.zooncool.debug.rest; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController("/debug") public class DebugController {@GetMappingpublic String ask(@RequestParam("name") String name) {String message = "are you ok?" + name;return message;} }

啟動服務

搭建好服務之后我們先啟動服務,我們通過maven來啟動服務,其中涉及到的一些參數下面解釋。

mvn spring-boot:run -Drun.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8001" 或者 mvn spring-boot:run -Drun.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8001"
  • mvn:maven的腳本命令這個不用解釋

  • Spring-boot:run:啟動springboot工程

  • -Drun.jvmArguments:執行jvm環境的參數,里面的參數值才是關鍵

  • -Xdebug

    Xdebug開啟調試模式,為非標準參數,也就是可能在其他JVM上面是不可用的,Java5之后提供了標準的執行參數agentlib,下面兩種參數同樣可以開啟debug模式,但是在JIT方面有所差異,這里先不展開。

    java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8001

    java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8001

  • Xrunjdwp/jdwp=transport:表示連接模式是本地內存共享還是遠程socket連接

  • server:y表示打開socket監聽調試器的請求;n表示被調試程序像客戶端一樣主動連接調試器

  • suspend:y表示被調試程序需要等到調試器的連接請求之后才能啟動運行,在此之前都是掛起的,n表示被調試程序無需等待直接運行。

  • address:被調試程序啟動debug模式后監聽請求的地址和端口,地址缺省為本地。

執行完上述命令后,就等著我們調試器的請求接入到目標程序了。

調試接入

我們知道java的調試器客戶端為jdb,下面我們就使用jdb來接入我們的目標程序。

#jdb 通過attach參數選擇本地目標程序,同時附上目標程序的源碼,回想之前我們講到的JDI的鏡像接口,就是把目標程序的堆棧結構同步過來,如果能我們提供的源碼對應上,那就可以在源碼上面顯示斷點標志 $ jdb -attach localhost:8001 -sourcepath /Users/linzhenhua/Documents/repositories/practice/stackify-master/remote-debugging/src/main/java/ 設置未捕獲的java.lang.Throwable 設置延遲的未捕獲的java.lang.Throwable 正在初始化jdb...#stop,選擇對應方法設置斷點 > stop in com.zooncool.debug.rest.DebugController.ask(java.lang.String) 設置斷點com.zooncool.debug.rest.DebugController.ask(java.lang.String)#如果我們設置不存在的方法為斷點,則會有錯誤提示 > stop in com.zooncool.debug.rest.DebugController.ask2(java.lang.String) 無法設置斷點com.zooncool.debug.rest.DebugController.ask2(java.lang.String): com.zooncool.debug.rest.DebugController中沒有方法ask2#這時候我們已經設置完斷點,就可以發起個HTTP請求 #http://localhost:7001/remote-debugging/debug/ask?name=Jack #發起請求后我們回到jdb控制臺,觀察是否命中斷點 > 斷點命中: "線程=http-nio-7001-exec-5", com.zooncool.debug.rest.DebugController.ask(), 行=14 bci=0 14 String message = "are you ok?" + name;#list,對照源碼,確實是進入ask方法第一行命中斷點,也就是14行,這時候我們可以查看源碼 http-nio-7001-exec-5[1] list 10 @RestController("/debug") 11 public class DebugController { 12 @GetMapping 13 public String ask(@RequestParam("name") String name) { 14 => String message = "are you ok?" + name; 15 return message; 16 } 17 }#locals,觀察完源碼,我們想獲取name的傳參,跟URL傳入的一致 http-nio-7001-exec-5[1] locals 方法參數: name = "Jack" 本地變量:#print name,打印入參 http-nio-7001-exec-5[1] print namename = "Jack"#where,查詢方法調用的棧幀,從web容器入口調用方法到目標方法的調用鏈路 http-nio-7001-exec-5[1] where[1] com.zooncool.debug.rest.DebugController.ask (DebugController.java:14)...[55] java.lang.Thread.run (Thread.java:748) #step,下一步到下一行代碼 http-nio-7001-exec-5[1] step > 已完成的步驟: "線程=http-nio-7001-exec-5", com.zooncool.debug.rest.DebugController.ask(), 行=15 bci=20 15 return message;#step up,完成當前方法的調用 http-nio-7001-exec-5[1] step up > 已完成的步驟: "線程=http-nio-7001-exec-5", sun.reflect.NativeMethodAccessorImpl.invoke(), 行=62 bci=103#cont,結束調試,執行完畢 http-nio-7001-exec-5[1] cont > #clear,完成調試任務,清除斷點 > clear 斷點集:斷點com.zooncool.debug.rest.DebugController.ask(java.lang.String)斷點com.zooncool.debug.rest.DebugController.ask2(java.lang.String) #選擇一個斷點刪除 > clear com.zooncool.debug.rest.DebugController.ask(java.lang.String) 已刪除: 斷點com.zooncool.debug.rest.DebugController.ask(java.lang.String)

我們已經完成了命令行調試的全部流程,stop/list/locals/print name/where/step/step up/cont/clear這些命令其實就是IDE的UI后臺調用的腳本。而這些腳本就是基于JDI層面的接口所提供的能力,下面我們還有重點觀察一個核心功能,先從頭再設置一下斷點。

#stop,選擇對應方法設置斷點 > stop in com.zooncool.debug.rest.DebugController.ask(java.lang.String) 設置斷點com.zooncool.debug.rest.DebugController.ask(java.lang.String) #這時候我們已經設置完斷點,就可以發起個HTTP請求 #http://localhost:7001/remote-debugging/debug/ask?name=Jack #發起請求后我們回到jdb控制臺,觀察是否命中斷點 > 斷點命中: "線程=http-nio-7001-exec-5", com.zooncool.debug.rest.DebugController.ask(), 行=14 bci=0 14 String message = "are you ok?" + name; #print name,打印入參 http-nio-7001-exec-5[1] print namename = "Jack" #如果這個時候我們想替換掉Jack,換成Lucy http-nio-7001-exec-6[1] set name = "Lucy" name = "Lucy" = "Lucy" #進入下一步 http-nio-7001-exec-6[1] step > 已完成的步驟: "線程=http-nio-7001-exec-6", com.zooncool.debug.rest.DebugController.ask(), 行=15 bci=20 15 return message; #查看變量,我們發現name的值已經被修改了 http-nio-7001-exec-6[1] locals 方法參數: name = "Lucy" 本地變量: message = "are you ok?Lucy"

至此我們已經完成了JPDA的原理解析到調試實踐,也理解了JAVA調試的工作機制,其中留下一個重要的彩蛋就是通過JPDA進入調試模式,我們可以動態的修改JVM內存對象和類的內容,這也講引出下文我們要介紹的字節碼增強技術。

8.2 熱替換

8.2.1概念

終于來到熱替換這節了,前文我們做了好多鋪墊,介紹熱替換之前我們稍稍回顧一下熱部署。我們知道熱部署是“獨立”于JVM之外的一門對類加載器應用的技術,通常是應用容器借助自定義類加載器的迭代,無需重啟JVM缺能更新代碼從而達到熱部署,也就是說熱部署是JVM之外容器提供的一種能力。而本節我們介紹的熱替換技術是實打實JVM提供的能力,是JVM提供的一種能夠實時更新內存類結構的一種能力,這種實時更新JVM方法區類結構的能力當然也是無需重啟JVM實例。

熱替換HotSwap是Sun公司在Java 1.4版本引入的一種新實驗性技術,也就是上一節我們介紹JPDA提到的調試模式下可以動態替換類結構的彩蛋,這個功能被集成到JPDA框架的接口集合里面,首先我們定義好熱替換的概念。

熱替換(HotSwap):使用字節碼增強技術替換JVM內存里面類的結構,包括對應類的對象,而不需要重啟虛擬機。

8.2.2原理

前文從宏觀上介紹了JVM實例的內存布局和垃圾回收機制,微觀上也解釋了類的結構和類加載機制,上一節又學習了JAVA的調試框架,基本上我們對JVM的核心模塊都已經摸透了,剩下的就是攻克字節碼增強的技術了。而之前講的字節碼增強技術也僅僅是放在JPDA里面作為實驗性技術,而且僅僅局限在方法體和變量的修改,無法動態修改方法簽名或者增刪方法,因為字節碼增強涉及到垃圾回收機制,類結構變更,對象引用,即時編譯等復雜問題。在HotSwap被引進后至今,JCP也未能通過正式的字節碼增強實現。

JAVA是一門靜態語言,而字節碼增強所要達的效果就是讓Java像動態語言一樣跑起來,無需重啟服務器。下面我們介紹字節碼增強的基本原理。

  • 反射代理

    反射代理不能直接修改內存方法區的字節碼,但是可以抽象出一層代理,通過內存新增實例來實現類的更新

  • 原生接口

    jdk上層提供面向java語言的字節碼增強接口java.lang.instrument,通過實現ClassFileTransformer接口來操作JVM方法區的類文件字節碼。

  • JVMTI代理

    JVM的JVMTI接口包含了操作方法區類文件字節碼的函數,通過創建代理,將JVMTI的指針JavaVM傳給代理,從而擁有JVM 本地操作字節碼的方法引用。

  • 類加載器織入

    字節碼增強接口加上類加載器的織入,結合起來也是一種熱替換技術。

  • JVM增強

    直接新增JVM分支,增加字節碼增強功能。

8.2.3實現

但是盡管字節碼增強是一門復雜的技術,這并不妨礙我們進一步的探索,下面我們介紹幾種常見的實現方案。

  • Instrumentation

  • AspectJ

  • ASM

  • DCEVM

  • JREBEL

  • CGLIB

  • javassist

  • BCEL

具體的我會挑兩個具有代表性的工具深入講解,篇幅所限,這里就補展開了。

9.總結

JVM是程序發展至今的一顆隗寶,是程序設計和工程實現的完美結合。JVM作為作為三大工業級程序語言為首JAVA的根基,本文試圖在瀚如煙海的JVM海洋中找出其中最耀眼的冰山,并力求用簡潔的邏輯線索把各個冰山串起來,在腦海中對JVM的觀感有更加立體的認識。更近一步的認識JVM對程序設計的功力提示大有裨益,而本文也只是將海平面上的冰山鏈接起來,但這只是冰山一角,JVM更多的底層設計和實現細節還遠遠沒有涉及到,而且也不乏知識盲區而沒有提及到的,路漫漫其修遠兮,JVM本身也在不斷的推陳出新,借此機會總結出JVM的核心體系,以此回顧對JVM知識的查漏補缺,也是一次JVM的認知升級。最后還是例牌來兩張圖結束JVM的介紹,希望對更的同學有幫助。

?

from:https://www.javazhiyin.com/34166.html?

總結

以上是生活随笔為你收集整理的JVM核心知识体系的全部內容,希望文章能夠幫你解決所遇到的問題。

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

永久免费观看美女裸体的网站 | 精品国产aⅴ无码一区二区 | 午夜丰满少妇性开放视频 | 亚洲人成无码网www | 午夜精品久久久久久久 | 亚洲一区二区三区偷拍女厕 | 亚洲精品欧美二区三区中文字幕 | 高清国产亚洲精品自在久久 | 性做久久久久久久久 | 国产精品18久久久久久麻辣 | 国色天香社区在线视频 | 少女韩国电视剧在线观看完整 | 波多野结衣av一区二区全免费观看 | 国产亚洲日韩欧美另类第八页 | 国产偷国产偷精品高清尤物 | 无遮挡国产高潮视频免费观看 | 精品水蜜桃久久久久久久 | 亚洲国产欧美国产综合一区 | 秋霞特色aa大片 | 午夜理论片yy44880影院 | 无遮挡啪啪摇乳动态图 | 在线а√天堂中文官网 | 天天摸天天透天天添 | 大乳丰满人妻中文字幕日本 | 欧美人与禽zoz0性伦交 | 狠狠cao日日穞夜夜穞av | 亚洲理论电影在线观看 | 久久精品国产99久久6动漫 | 国产av剧情md精品麻豆 | 亚洲综合精品香蕉久久网 | 欧美丰满熟妇xxxx性ppx人交 | 无码吃奶揉捏奶头高潮视频 | 水蜜桃亚洲一二三四在线 | 牲欲强的熟妇农村老妇女视频 | 亚洲中文字幕乱码av波多ji | 午夜成人1000部免费视频 | 无码午夜成人1000部免费视频 | аⅴ资源天堂资源库在线 | 日韩亚洲欧美中文高清在线 | а√资源新版在线天堂 | 熟妇激情内射com | 六月丁香婷婷色狠狠久久 | 成人影院yy111111在线观看 | 精品 日韩 国产 欧美 视频 | 久青草影院在线观看国产 | 中文无码成人免费视频在线观看 | 99久久精品午夜一区二区 | 日本精品人妻无码免费大全 | 国产一区二区三区四区五区加勒比 | 天天拍夜夜添久久精品 | 鲁大师影院在线观看 | 国产成人无码专区 | 少妇性l交大片 | 日本精品人妻无码免费大全 | 无码国产乱人伦偷精品视频 | 特级做a爰片毛片免费69 | 成 人影片 免费观看 | 少妇人妻大乳在线视频 | 亚洲精品一区国产 | 国模大胆一区二区三区 | 男人的天堂av网站 | 亚洲毛片av日韩av无码 | 国产精品无码成人午夜电影 | 无码国内精品人妻少妇 | 人人妻人人澡人人爽人人精品浪潮 | 亚洲精品www久久久 | 中文字幕无码av波多野吉衣 | 亚洲s色大片在线观看 | 天堂久久天堂av色综合 | 日本大乳高潮视频在线观看 | 领导边摸边吃奶边做爽在线观看 | 亚洲理论电影在线观看 | 国产免费久久精品国产传媒 | 亚洲成av人在线观看网址 | 西西人体www44rt大胆高清 | 97无码免费人妻超级碰碰夜夜 | 性色av无码免费一区二区三区 | 日日鲁鲁鲁夜夜爽爽狠狠 | 高清无码午夜福利视频 | 无码人妻丰满熟妇区毛片18 | 久久人人爽人人爽人人片av高清 | 少妇性l交大片 | 午夜时刻免费入口 | 久久天天躁狠狠躁夜夜免费观看 | 色综合久久88色综合天天 | 国产精品久久久 | 草草网站影院白丝内射 | 午夜嘿嘿嘿影院 | 四虎影视成人永久免费观看视频 | 沈阳熟女露脸对白视频 | 亚洲成av人影院在线观看 | 欧美三级不卡在线观看 | 无码精品人妻一区二区三区av | 99国产精品白浆在线观看免费 | 无码任你躁久久久久久久 | 性欧美疯狂xxxxbbbb | 午夜成人1000部免费视频 | 99久久精品午夜一区二区 | 日本爽爽爽爽爽爽在线观看免 | 国产97色在线 | 免 | 久久国产自偷自偷免费一区调 | 少妇人妻大乳在线视频 | 少妇邻居内射在线 | 牲欲强的熟妇农村老妇女 | 丰满少妇高潮惨叫视频 | 青青青爽视频在线观看 | 国产肉丝袜在线观看 | 无码任你躁久久久久久久 | 国产xxx69麻豆国语对白 | 免费看少妇作爱视频 | 国产成人精品三级麻豆 | 狠狠色噜噜狠狠狠7777奇米 | 性色欲网站人妻丰满中文久久不卡 | 亚洲日韩av一区二区三区中文 | 波多野结衣 黑人 | 亚洲熟妇色xxxxx亚洲 | 精品无码国产自产拍在线观看蜜 | 精品久久8x国产免费观看 | 人人澡人摸人人添 | 久久人人爽人人爽人人片av高清 | 久久精品丝袜高跟鞋 | 国产亚洲精品久久久闺蜜 | 国产无套粉嫩白浆在线 | 一区二区三区乱码在线 | 欧洲 | 伦伦影院午夜理论片 | 国产精华av午夜在线观看 | 国产精品鲁鲁鲁 | 亚洲人成无码网www | 一二三四社区在线中文视频 | 高潮毛片无遮挡高清免费 | 欧美熟妇另类久久久久久多毛 | 国产无遮挡又黄又爽又色 | www一区二区www免费 | 成人一区二区免费视频 | 国产亚洲精品久久久久久 | 成人性做爰aaa片免费看 | 免费乱码人妻系列无码专区 | 在线视频网站www色 | 色妞www精品免费视频 | 无码人妻丰满熟妇区五十路百度 | 亚洲国产精华液网站w | 欧洲vodafone精品性 | 亚拍精品一区二区三区探花 | 国产后入清纯学生妹 | 国产乱人偷精品人妻a片 | 99久久精品午夜一区二区 | 性生交大片免费看女人按摩摩 | 亚洲の无码国产の无码影院 | 国产精品久久久一区二区三区 | 色一情一乱一伦一区二区三欧美 | 亚洲aⅴ无码成人网站国产app | 国产片av国语在线观看 | 一二三四在线观看免费视频 | 无码精品国产va在线观看dvd | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 蜜臀aⅴ国产精品久久久国产老师 | 日产精品高潮呻吟av久久 | 正在播放东北夫妻内射 | 性开放的女人aaa片 | 精品日本一区二区三区在线观看 | 娇妻被黑人粗大高潮白浆 | 波多野结衣aⅴ在线 | 亚洲午夜无码久久 | 初尝人妻少妇中文字幕 | 成人三级无码视频在线观看 | 亚洲精品无码国产 | 天天爽夜夜爽夜夜爽 | 久久婷婷五月综合色国产香蕉 | 亚洲欧美国产精品久久 | 精品国偷自产在线 | 人妻体内射精一区二区三四 | 久久久精品人妻久久影视 | 日本一卡二卡不卡视频查询 | 久久久久免费精品国产 | 国产精品亚洲综合色区韩国 | 亚洲自偷自拍另类第1页 | 亚洲の无码国产の无码步美 | 久久伊人色av天堂九九小黄鸭 | 麻豆国产97在线 | 欧洲 | 麻豆蜜桃av蜜臀av色欲av | 免费无码的av片在线观看 | 国产一区二区三区日韩精品 | 亚洲成av人片天堂网无码】 | 国产精品无码mv在线观看 | 国产免费无码一区二区视频 | 国产精品人妻一区二区三区四 | 2020久久超碰国产精品最新 | 国产亚洲人成a在线v网站 | 在线а√天堂中文官网 | 久久国产精品精品国产色婷婷 | 日韩精品无码一区二区中文字幕 | 国产av人人夜夜澡人人爽麻豆 | 亚洲乱码中文字幕在线 | 中文字幕无码视频专区 | 国产97在线 | 亚洲 | 成人综合网亚洲伊人 | 牛和人交xxxx欧美 | 我要看www免费看插插视频 | 无人区乱码一区二区三区 | 亚洲中文字幕在线观看 | 十八禁真人啪啪免费网站 | 桃花色综合影院 | 亚洲国产午夜精品理论片 | 欧美亚洲国产一区二区三区 | 在线欧美精品一区二区三区 | 性生交大片免费看l | 国产精品毛片一区二区 | 99久久人妻精品免费一区 | 亚洲综合无码久久精品综合 | 久久久婷婷五月亚洲97号色 | 国产无遮挡吃胸膜奶免费看 | 性史性农村dvd毛片 | 国产乱人伦av在线无码 | 久久亚洲精品中文字幕无男同 | 熟妇激情内射com | 国产av剧情md精品麻豆 | 四虎国产精品一区二区 | 伊人久久婷婷五月综合97色 | 欧美人与牲动交xxxx | 波多野结衣 黑人 | 亚洲国产高清在线观看视频 | 日本大香伊一区二区三区 | 无码免费一区二区三区 | 国产亚洲精品久久久久久国模美 | 国产又粗又硬又大爽黄老大爷视 | 在线成人www免费观看视频 | 欧洲极品少妇 | 无码人妻精品一区二区三区不卡 | 亚洲一区av无码专区在线观看 | 国产在线精品一区二区高清不卡 | 国产乱人伦av在线无码 | 中文字幕乱码中文乱码51精品 | 久久综合九色综合97网 | 中文字幕久久久久人妻 | 露脸叫床粗话东北少妇 | 少妇被黑人到高潮喷出白浆 | 麻豆国产丝袜白领秘书在线观看 | 日韩人妻系列无码专区 | 18无码粉嫩小泬无套在线观看 | 亚洲日韩av片在线观看 | 久久精品丝袜高跟鞋 | 妺妺窝人体色www在线小说 | 国产av人人夜夜澡人人爽麻豆 | 国内综合精品午夜久久资源 | 伊人久久大香线蕉亚洲 | 67194成是人免费无码 | 久久国产精品_国产精品 | 99视频精品全部免费免费观看 | 一二三四社区在线中文视频 | 国产精品理论片在线观看 | 国产一区二区三区影院 | av在线亚洲欧洲日产一区二区 | 日本一区二区三区免费高清 | 免费无码午夜福利片69 | 精品国产一区二区三区四区在线看 | 99久久久国产精品无码免费 | 国产精品无码久久av | 999久久久国产精品消防器材 | 色综合久久中文娱乐网 | 日韩少妇内射免费播放 | 亚洲精品欧美二区三区中文字幕 | 成人一在线视频日韩国产 | 国内丰满熟女出轨videos | 国产精品手机免费 | 欧美真人作爱免费视频 | 激情内射亚州一区二区三区爱妻 | 亚洲国产日韩a在线播放 | 久久久精品人妻久久影视 | 亚洲毛片av日韩av无码 | 亚洲精品国产精品乱码不卡 | 国产精品二区一区二区aⅴ污介绍 | 色欲av亚洲一区无码少妇 | 亚洲男人av天堂午夜在 | 人人澡人摸人人添 | 久久亚洲日韩精品一区二区三区 | 亚洲综合无码一区二区三区 | 综合激情五月综合激情五月激情1 | 久久久精品成人免费观看 | 国产成人精品视频ⅴa片软件竹菊 | 日产精品99久久久久久 | 高清不卡一区二区三区 | 精品欧洲av无码一区二区三区 | 精品人妻人人做人人爽夜夜爽 | 亚洲s色大片在线观看 | 色爱情人网站 | www成人国产高清内射 | 国产av无码专区亚洲a∨毛片 | 国产一区二区三区四区五区加勒比 | 夜先锋av资源网站 | 高潮毛片无遮挡高清免费 | 成人性做爰aaa片免费看不忠 | 精品人妻人人做人人爽 | 蜜桃臀无码内射一区二区三区 | 激情内射日本一区二区三区 | 国产真实乱对白精彩久久 | 亚洲国产精品久久久久久 | 一区二区三区高清视频一 | 亚洲精品无码国产 | 激情五月综合色婷婷一区二区 | 天天做天天爱天天爽综合网 | 性色欲情网站iwww九文堂 | 国产精品二区一区二区aⅴ污介绍 | 亚洲无人区午夜福利码高清完整版 | 免费看少妇作爱视频 | 中文字幕人妻无码一区二区三区 | 国产免费无码一区二区视频 | 丰满岳乱妇在线观看中字无码 | 国产农村妇女高潮大叫 | 久久精品国产99精品亚洲 | 欧美肥老太牲交大战 | 高清无码午夜福利视频 | 欧美性生交活xxxxxdddd | 人妻少妇被猛烈进入中文字幕 | 欧美激情一区二区三区成人 | 精品乱码久久久久久久 | 精品一区二区三区波多野结衣 | 偷窥日本少妇撒尿chinese | 国产亚洲精品久久久久久大师 | 日韩少妇白浆无码系列 | 东京无码熟妇人妻av在线网址 | 亚洲の无码国产の无码影院 | 亚洲中文字幕va福利 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 两性色午夜视频免费播放 | 久久成人a毛片免费观看网站 | 亚洲经典千人经典日产 | 日日天干夜夜狠狠爱 | 无遮无挡爽爽免费视频 | 国内揄拍国内精品少妇国语 | 性色欲情网站iwww九文堂 | 亚洲精品国产品国语在线观看 | 人人妻在人人 | 亚洲色大成网站www国产 | 男人扒开女人内裤强吻桶进去 | 久久无码中文字幕免费影院蜜桃 | 亚洲大尺度无码无码专区 | 天天爽夜夜爽夜夜爽 | 野外少妇愉情中文字幕 | 无码帝国www无码专区色综合 | 久激情内射婷内射蜜桃人妖 | 精品乱子伦一区二区三区 | 人妻体内射精一区二区三四 | 无码人妻久久一区二区三区不卡 | 亚洲欧洲日本综合aⅴ在线 | 国产午夜福利亚洲第一 | 少妇无码吹潮 | 日本丰满护士爆乳xxxx | 精品水蜜桃久久久久久久 | 免费无码肉片在线观看 | 色一情一乱一伦一区二区三欧美 | 久久精品国产亚洲精品 | 亚洲国产精品久久久天堂 | 精品偷拍一区二区三区在线看 | 久久精品国产大片免费观看 | 人妻熟女一区 | 秋霞成人午夜鲁丝一区二区三区 | 亚洲国产精品久久人人爱 | 国产亚洲人成a在线v网站 | www国产亚洲精品久久久日本 | 兔费看少妇性l交大片免费 | 成人性做爰aaa片免费看 | 一二三四在线观看免费视频 | 无码人妻精品一区二区三区下载 | 久久精品人妻少妇一区二区三区 | 国产成人久久精品流白浆 | 一本久久伊人热热精品中文字幕 | 久久综合色之久久综合 | 女人和拘做爰正片视频 | 亚洲综合精品香蕉久久网 | 高潮毛片无遮挡高清免费 | 久久综合给久久狠狠97色 | 国产午夜亚洲精品不卡下载 | 成 人 网 站国产免费观看 | 免费看男女做好爽好硬视频 | 亚洲一区二区三区偷拍女厕 | 欧美性猛交内射兽交老熟妇 | 强辱丰满人妻hd中文字幕 | 麻豆国产97在线 | 欧洲 | 久久精品中文闷骚内射 | 国产av无码专区亚洲awww | 欧美性生交活xxxxxdddd | 欧美乱妇无乱码大黄a片 | 天堂无码人妻精品一区二区三区 | 美女极度色诱视频国产 | 成人无码视频在线观看网站 | 无码午夜成人1000部免费视频 | 亚洲啪av永久无码精品放毛片 | 免费人成在线观看网站 | а√资源新版在线天堂 | 高潮喷水的毛片 | 精品无人国产偷自产在线 | 伊人久久婷婷五月综合97色 | 黑人巨大精品欧美黑寡妇 | 国产精品亚洲lv粉色 | 亚洲综合在线一区二区三区 | 国产精品va在线播放 | 欧美丰满熟妇xxxx性ppx人交 | 成人欧美一区二区三区 | 97无码免费人妻超级碰碰夜夜 | 久久 国产 尿 小便 嘘嘘 | 青草视频在线播放 | 亚洲欧美综合区丁香五月小说 | 丝袜美腿亚洲一区二区 | 国产suv精品一区二区五 | 对白脏话肉麻粗话av | 成人欧美一区二区三区黑人 | 国产午夜手机精彩视频 | 300部国产真实乱 | 乌克兰少妇xxxx做受 | 日韩欧美群交p片內射中文 | 国产欧美精品一区二区三区 | 牛和人交xxxx欧美 | 日本一卡2卡3卡四卡精品网站 | 久久综合网欧美色妞网 | 久久无码专区国产精品s | 免费乱码人妻系列无码专区 | 亚洲性无码av中文字幕 | 小sao货水好多真紧h无码视频 | 亚洲精品中文字幕乱码 | 日韩视频 中文字幕 视频一区 | 国产在线精品一区二区高清不卡 | 国产亚洲美女精品久久久2020 | 精品成在人线av无码免费看 | 精品久久综合1区2区3区激情 | 国产人妻精品一区二区三区 | 国产免费久久久久久无码 | 国产熟妇高潮叫床视频播放 | 亚洲成色www久久网站 | 国产另类ts人妖一区二区 | 99国产精品白浆在线观看免费 | 国产在线一区二区三区四区五区 | 人妻熟女一区 | 亚洲码国产精品高潮在线 | 亚洲色偷偷偷综合网 | 国产精品久久久久影院嫩草 | 欧美自拍另类欧美综合图片区 | 久久综合色之久久综合 | 国产av人人夜夜澡人人爽麻豆 | 日韩成人一区二区三区在线观看 | 亚洲成av人综合在线观看 | 久久人人爽人人人人片 | 夜夜夜高潮夜夜爽夜夜爰爰 | 国产精品久免费的黄网站 | 无码人妻少妇伦在线电影 | 国产乱子伦视频在线播放 | 2020久久香蕉国产线看观看 | 日韩精品一区二区av在线 | 色婷婷综合中文久久一本 | 国産精品久久久久久久 | 四十如虎的丰满熟妇啪啪 | 啦啦啦www在线观看免费视频 | 亚洲第一无码av无码专区 | 97精品国产97久久久久久免费 | 澳门永久av免费网站 | 久在线观看福利视频 | 亚洲日韩av一区二区三区四区 | 男女作爱免费网站 | 日本va欧美va欧美va精品 | 精品成人av一区二区三区 | 永久免费观看美女裸体的网站 | 美女扒开屁股让男人桶 | 狠狠cao日日穞夜夜穞av | 偷窥日本少妇撒尿chinese | √天堂中文官网8在线 | 少妇厨房愉情理9仑片视频 | 中文精品无码中文字幕无码专区 | 免费观看激色视频网站 | 亚洲欧美精品aaaaaa片 | 99久久99久久免费精品蜜桃 | 亚洲人成无码网www | 久9re热视频这里只有精品 | 曰韩少妇内射免费播放 | 中文精品无码中文字幕无码专区 | 又大又黄又粗又爽的免费视频 | 国产人妻精品一区二区三区 | 夜夜影院未满十八勿进 | √天堂资源地址中文在线 | 波多野结衣一区二区三区av免费 | 国产色xx群视频射精 | 老头边吃奶边弄进去呻吟 | 精品国产一区av天美传媒 | 成人片黄网站色大片免费观看 | 国产av无码专区亚洲a∨毛片 | 国产精品久久久久久无码 | 国产人成高清在线视频99最全资源 | 国产av一区二区精品久久凹凸 | 99视频精品全部免费免费观看 | 欧美亚洲日韩国产人成在线播放 | 亚洲熟妇色xxxxx欧美老妇 | 亚洲の无码国产の无码影院 | 国产精品久久久午夜夜伦鲁鲁 | 在线精品国产一区二区三区 | 日本丰满护士爆乳xxxx | 青青青爽视频在线观看 | 中文字幕乱码人妻二区三区 | 亚洲精品一区二区三区大桥未久 | 亚洲精品一区二区三区在线观看 | 一本久久a久久精品vr综合 | 野狼第一精品社区 | 少妇激情av一区二区 | 亚洲国产av精品一区二区蜜芽 | 国产色精品久久人妻 | 日日躁夜夜躁狠狠躁 | 国产97人人超碰caoprom | 我要看www免费看插插视频 | 我要看www免费看插插视频 | 大色综合色综合网站 | 人妻aⅴ无码一区二区三区 | 国产午夜精品一区二区三区嫩草 | 亚洲欧美中文字幕5发布 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 小鲜肉自慰网站xnxx | 久久国产36精品色熟妇 | 国产精品久久久av久久久 | 欧美性生交活xxxxxdddd | 国产精品福利视频导航 | 久久成人a毛片免费观看网站 | 成人影院yy111111在线观看 | 久久久久av无码免费网 | 中文无码成人免费视频在线观看 | 国产婷婷色一区二区三区在线 | 牲欲强的熟妇农村老妇女 | 久久精品国产亚洲精品 | 日日天干夜夜狠狠爱 | 午夜精品久久久内射近拍高清 | 中文字幕乱码中文乱码51精品 | 奇米影视7777久久精品 | 人人妻人人澡人人爽欧美一区九九 | 无套内谢老熟女 | 中文毛片无遮挡高清免费 | 扒开双腿吃奶呻吟做受视频 | 久久久久亚洲精品中文字幕 | 精品久久久中文字幕人妻 | 亚洲va中文字幕无码久久不卡 | 国产熟妇另类久久久久 | 日韩人妻无码一区二区三区久久99 | 久久精品国产亚洲精品 | 99国产精品白浆在线观看免费 | 中文字幕乱码人妻无码久久 | 牲交欧美兽交欧美 | 亚洲成熟女人毛毛耸耸多 | 国产精品igao视频网 | 人人妻人人澡人人爽欧美一区九九 | 在线精品国产一区二区三区 | 2019nv天堂香蕉在线观看 | 日日摸夜夜摸狠狠摸婷婷 | 粉嫩少妇内射浓精videos | 99视频精品全部免费免费观看 | 18禁止看的免费污网站 | 成年美女黄网站色大免费视频 | 国产香蕉97碰碰久久人人 | 亚洲第一无码av无码专区 | 中文字幕乱码中文乱码51精品 | 国内精品一区二区三区不卡 | 国产精品久久久久影院嫩草 | 亚洲色偷偷偷综合网 | 男人的天堂av网站 | 无码免费一区二区三区 | 无码人妻精品一区二区三区下载 | 亚洲精品国产a久久久久久 | 成人精品视频一区二区三区尤物 | 国产激情综合五月久久 | 亚洲熟妇自偷自拍另类 | 亚洲日韩乱码中文无码蜜桃臀网站 | 人人澡人人透人人爽 | 高中生自慰www网站 | 国产欧美亚洲精品a | 欧美激情综合亚洲一二区 | 成人无码视频在线观看网站 | 西西人体www44rt大胆高清 | 99久久久无码国产精品免费 | 亚洲精品久久久久avwww潮水 | 色爱情人网站 | 久在线观看福利视频 | 欧美日韩一区二区三区自拍 | 亚洲自偷精品视频自拍 | 99视频精品全部免费免费观看 | 又大又紧又粉嫩18p少妇 | 国产无遮挡又黄又爽免费视频 | 国产成人无码午夜视频在线观看 | 成人免费视频视频在线观看 免费 | 日日鲁鲁鲁夜夜爽爽狠狠 | 东京无码熟妇人妻av在线网址 | 中文字幕色婷婷在线视频 | 狠狠色欧美亚洲狠狠色www | 无遮挡国产高潮视频免费观看 | 国产午夜手机精彩视频 | 国产精品毛多多水多 | 奇米影视7777久久精品人人爽 | 国产香蕉尹人综合在线观看 | 欧美日韩色另类综合 | 伊人久久大香线蕉亚洲 | 少妇激情av一区二区 | 丝袜 中出 制服 人妻 美腿 | 学生妹亚洲一区二区 | 久久国产精品偷任你爽任你 | 人人妻在人人 | 少妇被黑人到高潮喷出白浆 | 中文字幕无码免费久久99 | 国产成人无码午夜视频在线观看 | 欧美性生交活xxxxxdddd | 丝袜美腿亚洲一区二区 | 国产精品亚洲lv粉色 | 日本一卡2卡3卡四卡精品网站 | 好爽又高潮了毛片免费下载 | 免费国产成人高清在线观看网站 | 风流少妇按摩来高潮 | 欧美日本日韩 | 国产美女极度色诱视频www | 内射欧美老妇wbb | 久久国产精品_国产精品 | 99精品无人区乱码1区2区3区 | 国产极品视觉盛宴 | 丰满少妇弄高潮了www | 麻豆蜜桃av蜜臀av色欲av | 亚洲 a v无 码免 费 成 人 a v | 久激情内射婷内射蜜桃人妖 | 天天做天天爱天天爽综合网 | 久久久中文久久久无码 | 亚洲人交乣女bbw | 亚洲区欧美区综合区自拍区 | 无码人妻丰满熟妇区五十路百度 | 国产精品99久久精品爆乳 | 又大又硬又黄的免费视频 | 精品无码av一区二区三区 | 国产尤物精品视频 | 国产色xx群视频射精 | 亚洲精品鲁一鲁一区二区三区 | 无码国产色欲xxxxx视频 | av无码不卡在线观看免费 | 亚洲国产一区二区三区在线观看 | 无码福利日韩神码福利片 | 最新国产乱人伦偷精品免费网站 | 极品嫩模高潮叫床 | 成人精品视频一区二区三区尤物 | 又湿又紧又大又爽a视频国产 | 久久亚洲精品成人无码 | 亚洲熟妇色xxxxx欧美老妇y | av无码久久久久不卡免费网站 | 国产成人精品一区二区在线小狼 | 人妻少妇被猛烈进入中文字幕 | 国产精品福利视频导航 | 亚洲第一无码av无码专区 | 内射爽无广熟女亚洲 | 亚洲日本在线电影 | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 精品人人妻人人澡人人爽人人 | 人妻无码αv中文字幕久久琪琪布 | 夫妻免费无码v看片 | 色妞www精品免费视频 | 欧美性猛交内射兽交老熟妇 | 动漫av网站免费观看 | 亚洲日韩精品欧美一区二区 | 亚洲s色大片在线观看 | 国产人妻人伦精品1国产丝袜 | 国产精品人人妻人人爽 | 亚洲无人区午夜福利码高清完整版 | 国产真人无遮挡作爱免费视频 | 黑人玩弄人妻中文在线 | 国产成人无码av片在线观看不卡 | 国产精品人人妻人人爽 | 在线 国产 欧美 亚洲 天堂 | 久久午夜无码鲁丝片秋霞 | 午夜不卡av免费 一本久久a久久精品vr综合 | 国产凸凹视频一区二区 | 亚洲中文字幕无码中字 | 麻豆国产人妻欲求不满谁演的 | 亚洲日韩精品欧美一区二区 | 欧美一区二区三区视频在线观看 | 少妇性荡欲午夜性开放视频剧场 | 国产一区二区三区精品视频 | 无码国模国产在线观看 | 国产亚洲精品久久久久久国模美 | 人人超人人超碰超国产 | 国产电影无码午夜在线播放 | 亚洲精品久久久久久久久久久 | 国产明星裸体无码xxxx视频 | 日韩人妻无码一区二区三区久久99 | 亚洲成a人片在线观看日本 | 国产成人无码专区 | 男女爱爱好爽视频免费看 | 国产电影无码午夜在线播放 | 又大又紧又粉嫩18p少妇 | 噜噜噜亚洲色成人网站 | 国产成人一区二区三区在线观看 | 丝袜人妻一区二区三区 | 伊在人天堂亚洲香蕉精品区 | 99精品无人区乱码1区2区3区 | 少妇无码av无码专区在线观看 | 在线天堂新版最新版在线8 | 精品国产福利一区二区 | 天天摸天天透天天添 | 初尝人妻少妇中文字幕 | 国产香蕉尹人视频在线 | 无码人妻黑人中文字幕 | 成人综合网亚洲伊人 | 小鲜肉自慰网站xnxx | 日韩亚洲欧美中文高清在线 | 无码免费一区二区三区 | 欧美高清在线精品一区 | 亚洲男人av香蕉爽爽爽爽 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 99er热精品视频 | 亚洲欧美日韩成人高清在线一区 | 久久99精品久久久久婷婷 | 中文精品无码中文字幕无码专区 | 日韩无套无码精品 | 欧美日韩综合一区二区三区 | 最近的中文字幕在线看视频 | 巨爆乳无码视频在线观看 | 扒开双腿吃奶呻吟做受视频 | av香港经典三级级 在线 | 国产av一区二区精品久久凹凸 | 国产办公室秘书无码精品99 | 亚洲理论电影在线观看 | 无码国内精品人妻少妇 | 久久这里只有精品视频9 | 成年美女黄网站色大免费全看 | 亚洲午夜福利在线观看 | 精品一二三区久久aaa片 | 亚洲精品中文字幕乱码 | 欧美激情一区二区三区成人 | 久久久久久国产精品无码下载 | 精品 日韩 国产 欧美 视频 | 国内精品一区二区三区不卡 | 国产精品内射视频免费 | 亚洲欧美色中文字幕在线 | 国产人妖乱国产精品人妖 | 人人妻人人藻人人爽欧美一区 | 亚洲一区二区三区偷拍女厕 | 综合激情五月综合激情五月激情1 | 少女韩国电视剧在线观看完整 | 精品无码av一区二区三区 | 国产成人综合在线女婷五月99播放 | 亚洲精品午夜无码电影网 | 一本久久a久久精品亚洲 | 日本在线高清不卡免费播放 | 欧美 日韩 亚洲 在线 | 无码纯肉视频在线观看 | 亚洲a无码综合a国产av中文 | 人妻无码久久精品人妻 | 2019nv天堂香蕉在线观看 | 日日天日日夜日日摸 | 久久精品视频在线看15 | 无码人妻丰满熟妇区五十路百度 | 精品无码国产自产拍在线观看蜜 | 国产精品理论片在线观看 | 窝窝午夜理论片影院 | 荫蒂添的好舒服视频囗交 | 国产精品久久久久7777 | 2019午夜福利不卡片在线 | 中文字幕日产无线码一区 | 青青久在线视频免费观看 | 国产激情综合五月久久 | 鲁鲁鲁爽爽爽在线视频观看 | 亚洲成a人片在线观看日本 | 中文字幕色婷婷在线视频 | 爽爽影院免费观看 | 人妻少妇精品无码专区动漫 | 国产明星裸体无码xxxx视频 | 天堂一区人妻无码 | 亚洲精品综合一区二区三区在线 | 国产免费久久精品国产传媒 | 任你躁在线精品免费 | 亚洲中文字幕av在天堂 | 色噜噜亚洲男人的天堂 | 午夜理论片yy44880影院 | 97精品人妻一区二区三区香蕉 | 久激情内射婷内射蜜桃人妖 | 无码免费一区二区三区 | 色欲av亚洲一区无码少妇 | 久久精品女人天堂av免费观看 | 欧美国产日韩久久mv | 内射欧美老妇wbb | 国产精品无套呻吟在线 | 国产莉萝无码av在线播放 | 十八禁视频网站在线观看 | 午夜精品久久久内射近拍高清 | 97夜夜澡人人爽人人喊中国片 | 久久无码专区国产精品s | 午夜嘿嘿嘿影院 | 激情国产av做激情国产爱 | 东北女人啪啪对白 | 东京热一精品无码av | 国产精品a成v人在线播放 | 欧美精品国产综合久久 | 99riav国产精品视频 | 日本熟妇人妻xxxxx人hd | 日日橹狠狠爱欧美视频 | 欧美精品一区二区精品久久 | 蜜桃臀无码内射一区二区三区 | 国产又爽又猛又粗的视频a片 | 天堂在线观看www | 国产97人人超碰caoprom | 精品人妻人人做人人爽 | 久久久亚洲欧洲日产国码αv | av无码久久久久不卡免费网站 | 18禁止看的免费污网站 | 国产午夜无码视频在线观看 | 国产另类ts人妖一区二区 | 国产精品美女久久久久av爽李琼 | 亚洲国产高清在线观看视频 | 中文无码精品a∨在线观看不卡 | 无遮无挡爽爽免费视频 | 亚洲 激情 小说 另类 欧美 | 日本成熟视频免费视频 | 国产精品va在线观看无码 | av香港经典三级级 在线 | 欧美野外疯狂做受xxxx高潮 | 国产精品人人爽人人做我的可爱 | 国精产品一品二品国精品69xx | 国产高潮视频在线观看 | 精品一区二区不卡无码av | 国产午夜亚洲精品不卡下载 | 熟妇人妻无乱码中文字幕 | 日本熟妇大屁股人妻 | av无码不卡在线观看免费 | 中文字幕人成乱码熟女app | 帮老师解开蕾丝奶罩吸乳网站 | 国产亚洲日韩欧美另类第八页 | 日日夜夜撸啊撸 | 波多野结衣一区二区三区av免费 | 无码人妻出轨黑人中文字幕 | 亚洲爆乳大丰满无码专区 | 欧洲美熟女乱又伦 | 国产三级久久久精品麻豆三级 | 国产免费观看黄av片 | 美女黄网站人色视频免费国产 | 又大又黄又粗又爽的免费视频 | 国产精品美女久久久网av | 欧美35页视频在线观看 | 久久国产36精品色熟妇 | 久久综合九色综合欧美狠狠 | 久久精品女人天堂av免费观看 | 国产口爆吞精在线视频 | 成人性做爰aaa片免费看 | 国产suv精品一区二区五 | 天天爽夜夜爽夜夜爽 | 国产精品人人爽人人做我的可爱 | 波多野结衣高清一区二区三区 | 成人免费视频一区二区 | 女人和拘做爰正片视频 | 激情国产av做激情国产爱 | 一二三四社区在线中文视频 | 露脸叫床粗话东北少妇 | 欧美日韩久久久精品a片 | 最新国产乱人伦偷精品免费网站 | 四虎影视成人永久免费观看视频 | 性欧美熟妇videofreesex | 久久无码专区国产精品s | 国内丰满熟女出轨videos | 亚洲天堂2017无码 | 中文无码精品a∨在线观看不卡 | 国产农村乱对白刺激视频 | 性做久久久久久久久 | 九九久久精品国产免费看小说 | 国产猛烈高潮尖叫视频免费 | 久久久www成人免费毛片 | 国产精品99久久精品爆乳 | 欧美精品无码一区二区三区 | 久久亚洲a片com人成 | 岛国片人妻三上悠亚 | a片免费视频在线观看 | 国产精品人人爽人人做我的可爱 | 午夜丰满少妇性开放视频 | 亚洲精品鲁一鲁一区二区三区 | 无遮挡啪啪摇乳动态图 | 东京一本一道一二三区 | 丰满诱人的人妻3 | 亚洲欧洲无卡二区视頻 | 久久国产精品萌白酱免费 | 亚洲综合久久一区二区 | 国产女主播喷水视频在线观看 | 窝窝午夜理论片影院 | 激情国产av做激情国产爱 | 免费网站看v片在线18禁无码 | 人妻与老人中文字幕 | 精品人妻中文字幕有码在线 | 日本一卡2卡3卡四卡精品网站 | 免费看少妇作爱视频 | 日本一卡二卡不卡视频查询 | 中文字幕人妻无码一夲道 | 18黄暴禁片在线观看 | 无套内射视频囯产 | 狠狠亚洲超碰狼人久久 | 亲嘴扒胸摸屁股激烈网站 | 在线观看国产一区二区三区 | 国产麻豆精品精东影业av网站 | 亚洲の无码国产の无码步美 | 男人的天堂av网站 | 亚洲熟悉妇女xxx妇女av | 性色欲情网站iwww九文堂 | 国产精品高潮呻吟av久久4虎 | 亚洲精品综合五月久久小说 | 国产偷自视频区视频 | 国产特级毛片aaaaaa高潮流水 | 欧美人与禽zoz0性伦交 | 国产精品理论片在线观看 | 18禁止看的免费污网站 | 久久综合狠狠综合久久综合88 | 国产亚洲精品久久久久久久久动漫 | 成年美女黄网站色大免费视频 | 中文字幕无码av激情不卡 | 十八禁真人啪啪免费网站 | 亚洲国产精品毛片av不卡在线 | 夜夜夜高潮夜夜爽夜夜爰爰 | 性开放的女人aaa片 | 国产精品高潮呻吟av久久4虎 | 中文精品无码中文字幕无码专区 | 夜夜夜高潮夜夜爽夜夜爰爰 | 久久99精品久久久久久 | 六月丁香婷婷色狠狠久久 | a在线亚洲男人的天堂 | 国产亚洲日韩欧美另类第八页 | 久久97精品久久久久久久不卡 | 一本久道久久综合婷婷五月 | 波多野42部无码喷潮在线 | 欧美35页视频在线观看 | 熟女少妇在线视频播放 | 欧美 亚洲 国产 另类 | 国产乡下妇女做爰 | 亚洲色成人中文字幕网站 | 国产精品va在线观看无码 | 18无码粉嫩小泬无套在线观看 | 日韩精品无码一本二本三本色 | 国产人妻精品一区二区三区不卡 | 久久99国产综合精品 | 扒开双腿疯狂进出爽爽爽视频 | 男女爱爱好爽视频免费看 | 亚洲国产欧美日韩精品一区二区三区 | 久久亚洲日韩精品一区二区三区 | 亚洲小说春色综合另类 | 性欧美牲交在线视频 | 人妻少妇精品无码专区动漫 | 国产后入清纯学生妹 | 精品午夜福利在线观看 | 久久精品人人做人人综合试看 | 又大又紧又粉嫩18p少妇 | 亚洲精品久久久久avwww潮水 | 成人无码精品1区2区3区免费看 | 欧美日韩精品 | 偷窥日本少妇撒尿chinese | 领导边摸边吃奶边做爽在线观看 | 国产三级精品三级男人的天堂 | 国产美女极度色诱视频www | 国产精品人人妻人人爽 | 黑人巨大精品欧美黑寡妇 | 国产精品久久久午夜夜伦鲁鲁 | 未满成年国产在线观看 | 亚洲色欲色欲天天天www | 无码任你躁久久久久久久 | 国产午夜福利亚洲第一 | 人人超人人超碰超国产 | 国产无套粉嫩白浆在线 | 性生交大片免费看l | 国产麻豆精品一区二区三区v视界 | 无码免费一区二区三区 | 国产午夜亚洲精品不卡 | 国产成人无码a区在线观看视频app | 亚洲精品午夜国产va久久成人 | 风流少妇按摩来高潮 | 人妻少妇精品久久 | 日韩在线不卡免费视频一区 | 夜精品a片一区二区三区无码白浆 | 国精品人妻无码一区二区三区蜜柚 | 成年美女黄网站色大免费视频 | 久久熟妇人妻午夜寂寞影院 | 夜精品a片一区二区三区无码白浆 | 人妻插b视频一区二区三区 | 国产亚洲精品久久久闺蜜 | 国产明星裸体无码xxxx视频 | 麻豆md0077饥渴少妇 | 女人被男人躁得好爽免费视频 | 夜夜夜高潮夜夜爽夜夜爰爰 | 国产成人无码av在线影院 | 99久久人妻精品免费二区 | 中国大陆精品视频xxxx | 国产九九九九九九九a片 | 欧美 亚洲 国产 另类 | 色老头在线一区二区三区 | 国产av剧情md精品麻豆 | 天堂在线观看www | 久久国产精品偷任你爽任你 | 国产精品无码一区二区三区不卡 | 免费观看黄网站 | 蜜桃av抽搐高潮一区二区 | 亚洲日本va午夜在线电影 | 免费无码一区二区三区蜜桃大 | 精品夜夜澡人妻无码av蜜桃 | 国产性猛交╳xxx乱大交 国产精品久久久久久无码 欧洲欧美人成视频在线 | 影音先锋中文字幕无码 | 日日摸天天摸爽爽狠狠97 | 伊人久久大香线蕉午夜 | 性欧美牲交在线视频 | 性色欲网站人妻丰满中文久久不卡 | 久久 国产 尿 小便 嘘嘘 | 2019nv天堂香蕉在线观看 | 亚洲国产精华液网站w | 乱码av麻豆丝袜熟女系列 | 欧美大屁股xxxxhd黑色 | 国产真实乱对白精彩久久 | 无码人妻少妇伦在线电影 | 国内综合精品午夜久久资源 | 国产亚洲美女精品久久久2020 | 中文字幕精品av一区二区五区 | 天天燥日日燥 | 亚洲精品一区二区三区婷婷月 | 欧洲精品码一区二区三区免费看 | 国产女主播喷水视频在线观看 | 国产精品手机免费 | 丰满人妻一区二区三区免费视频 | 成人av无码一区二区三区 | 无码av岛国片在线播放 | 成人欧美一区二区三区黑人免费 | 国产精品igao视频网 | 在线成人www免费观看视频 | 中文字幕无码热在线视频 | 又色又爽又黄的美女裸体网站 | 欧美肥老太牲交大战 | 欧洲熟妇精品视频 | 精品国产精品久久一区免费式 | 欧美人与动性行为视频 | 激情五月综合色婷婷一区二区 | 少女韩国电视剧在线观看完整 | 性欧美videos高清精品 | 无码任你躁久久久久久久 | 狠狠综合久久久久综合网 | 国产亚洲精品久久久闺蜜 | 夜夜高潮次次欢爽av女 | 亚洲第一无码av无码专区 | 亚洲s码欧洲m码国产av | 国产精品理论片在线观看 | 国产色视频一区二区三区 | 国产无遮挡吃胸膜奶免费看 | 亚洲性无码av中文字幕 | 亚洲gv猛男gv无码男同 | 97se亚洲精品一区 | 国产亚洲人成a在线v网站 | 国产又粗又硬又大爽黄老大爷视 | 亚洲国产高清在线观看视频 | 日本成熟视频免费视频 | 麻豆蜜桃av蜜臀av色欲av | 成年美女黄网站色大免费视频 | 无码av岛国片在线播放 | 成人影院yy111111在线观看 | 丰满妇女强制高潮18xxxx | 精品久久久无码中文字幕 | 国产精品久久久久久久9999 | 亚洲国产欧美国产综合一区 | 亚洲a无码综合a国产av中文 | 久久无码中文字幕免费影院蜜桃 | 美女黄网站人色视频免费国产 | 久热国产vs视频在线观看 | 人人妻人人澡人人爽欧美一区 | 久久综合久久自在自线精品自 | 亚洲区欧美区综合区自拍区 | 国产亚洲日韩欧美另类第八页 | 无码福利日韩神码福利片 | 国产美女极度色诱视频www | 国产精华av午夜在线观看 | 内射老妇bbwx0c0ck | 永久免费精品精品永久-夜色 | 少妇性l交大片欧洲热妇乱xxx | 婷婷六月久久综合丁香 | 欧美 日韩 人妻 高清 中文 | 亚洲天堂2017无码 | 狠狠噜狠狠狠狠丁香五月 | 在线观看国产一区二区三区 | 未满成年国产在线观看 | 中文字幕 亚洲精品 第1页 | 一本久久a久久精品亚洲 | 国产香蕉97碰碰久久人人 | 久久综合九色综合欧美狠狠 | 精品亚洲韩国一区二区三区 | 日本一区二区三区免费播放 | 网友自拍区视频精品 | 精品国产乱码久久久久乱码 | 国产精品久久久 | 精品偷自拍另类在线观看 | 麻豆成人精品国产免费 | 久久久无码中文字幕久... | 97精品国产97久久久久久免费 | 最近中文2019字幕第二页 | 久久99精品国产麻豆蜜芽 | 日日夜夜撸啊撸 | 377p欧洲日本亚洲大胆 | 久久这里只有精品视频9 | 欧美性生交xxxxx久久久 | 亚洲成av人影院在线观看 | 国产成人无码av片在线观看不卡 | 在线播放亚洲第一字幕 | 午夜福利试看120秒体验区 | 97人妻精品一区二区三区 | 久久国产自偷自偷免费一区调 | 99久久精品日本一区二区免费 | 亚洲s色大片在线观看 | 亚洲色大成网站www国产 | 东京热一精品无码av | 大地资源网第二页免费观看 | 欧美大屁股xxxxhd黑色 | 久久亚洲中文字幕无码 | 欧美成人高清在线播放 | 欧美人与禽zoz0性伦交 | 亚洲高清偷拍一区二区三区 | 青青青手机频在线观看 | 久久久久国色av免费观看性色 | 伊人久久大香线焦av综合影院 | 欧美日本免费一区二区三区 | 日本熟妇人妻xxxxx人hd | 久久久久久av无码免费看大片 | 欧美怡红院免费全部视频 | 国产suv精品一区二区五 | 国产手机在线αⅴ片无码观看 | 99精品无人区乱码1区2区3区 | 给我免费的视频在线观看 | 亚洲成av人片在线观看无码不卡 | 国産精品久久久久久久 | 动漫av网站免费观看 | 蜜桃av抽搐高潮一区二区 | 欧美三级不卡在线观看 | 中文字幕av无码一区二区三区电影 | 中文字幕av无码一区二区三区电影 | 无码人妻精品一区二区三区下载 | 少妇厨房愉情理9仑片视频 | 高中生自慰www网站 | 精品国产aⅴ无码一区二区 | aa片在线观看视频在线播放 | 国产精品久久久久无码av色戒 | 狂野欧美性猛xxxx乱大交 | 精品国产精品久久一区免费式 | 久久亚洲国产成人精品性色 | 内射欧美老妇wbb | 成人女人看片免费视频放人 | 亚洲中文字幕乱码av波多ji | 99久久人妻精品免费一区 | 婷婷综合久久中文字幕蜜桃三电影 | 久久精品视频在线看15 | 国产电影无码午夜在线播放 | 激情爆乳一区二区三区 | 欧美午夜特黄aaaaaa片 | 欧美兽交xxxx×视频 | 国产激情一区二区三区 | 日本www一道久久久免费榴莲 | 中文字幕人妻无码一夲道 | 国产sm调教视频在线观看 | 成熟人妻av无码专区 | 香港三级日本三级妇三级 | 亚洲精品无码人妻无码 | 国产特级毛片aaaaaaa高清 | 国产成人无码一二三区视频 | 亚洲欧美中文字幕5发布 | 丰满诱人的人妻3 | 色欲久久久天天天综合网精品 | 中文字幕人妻丝袜二区 | 亚洲日韩av片在线观看 | 性生交大片免费看l | 伊人久久大香线蕉av一区二区 | 丰满人妻一区二区三区免费视频 | 欧美日韩一区二区综合 | 色综合久久久无码中文字幕 | 亚洲日韩一区二区 | 在线播放免费人成毛片乱码 | 白嫩日本少妇做爰 | 成人无码精品一区二区三区 | 中文久久乱码一区二区 | 奇米影视888欧美在线观看 | 精品国产国产综合精品 | 黑人大群体交免费视频 | 亚洲成a人片在线观看无码 | 无码人妻久久一区二区三区不卡 | 乱人伦中文视频在线观看 | √8天堂资源地址中文在线 | 日韩精品一区二区av在线 | 欧美变态另类xxxx | 国产一区二区三区日韩精品 | 日本爽爽爽爽爽爽在线观看免 | 亚洲精品无码人妻无码 | 国产高清av在线播放 | 国产成人久久精品流白浆 | 国产偷抇久久精品a片69 | 综合网日日天干夜夜久久 | 色诱久久久久综合网ywww | 国产亚洲视频中文字幕97精品 | 久久综合给久久狠狠97色 | 少妇无套内谢久久久久 | 97资源共享在线视频 | 久久综合久久自在自线精品自 | 小sao货水好多真紧h无码视频 | 国产人妻精品一区二区三区 | 久久久久国色av免费观看性色 | 美女极度色诱视频国产 | 人妻与老人中文字幕 | 中文字幕人妻无码一夲道 | 宝宝好涨水快流出来免费视频 | 欧美人与动性行为视频 | 男女下面进入的视频免费午夜 | 中文无码伦av中文字幕 | 久久精品人人做人人综合 | 麻豆av传媒蜜桃天美传媒 | 亚洲精品中文字幕乱码 | 爱做久久久久久 | 日韩视频 中文字幕 视频一区 | 伊人久久大香线焦av综合影院 | 国内丰满熟女出轨videos | 香港三级日本三级妇三级 | 俄罗斯老熟妇色xxxx | 国产9 9在线 | 中文 | 国内精品人妻无码久久久影院蜜桃 | 亚洲熟悉妇女xxx妇女av | 丰满少妇女裸体bbw | 丝袜美腿亚洲一区二区 | 99精品国产综合久久久久五月天 | 激情五月综合色婷婷一区二区 | 女人被爽到呻吟gif动态图视看 | 97se亚洲精品一区 | 国产电影无码午夜在线播放 | 欧美 丝袜 自拍 制服 另类 | aⅴ在线视频男人的天堂 | 亚洲成av人片在线观看无码不卡 | 欧美 丝袜 自拍 制服 另类 | 精品乱码久久久久久久 | 伊人久久大香线蕉av一区二区 | 骚片av蜜桃精品一区 | 东京热男人av天堂 | 久久aⅴ免费观看 | 骚片av蜜桃精品一区 | 国产亚洲日韩欧美另类第八页 | 好爽又高潮了毛片免费下载 | 精品无码成人片一区二区98 | 日日碰狠狠躁久久躁蜜桃 | 国产国产精品人在线视 | 亚洲人成无码网www | av小次郎收藏 | 婷婷五月综合激情中文字幕 | 成人试看120秒体验区 | 国产精品igao视频网 | 国产电影无码午夜在线播放 | 亚洲国产av精品一区二区蜜芽 | 999久久久国产精品消防器材 | 亚洲中文字幕在线观看 | 国产精品久久久久久亚洲毛片 | 亚洲区欧美区综合区自拍区 | 欧美熟妇另类久久久久久不卡 | 久久这里只有精品视频9 | 亚洲狠狠色丁香婷婷综合 | 国产精品内射视频免费 | 日本一区二区更新不卡 | 图片区 小说区 区 亚洲五月 | 麻花豆传媒剧国产免费mv在线 | 亚洲成在人网站无码天堂 | 日本精品人妻无码77777 天堂一区人妻无码 | 久久99精品国产.久久久久 | 亚洲а∨天堂久久精品2021 | 亚洲日本一区二区三区在线 | 精品亚洲成av人在线观看 | 激情综合激情五月俺也去 | 88国产精品欧美一区二区三区 | 亚洲精品久久久久中文第一幕 | 男女下面进入的视频免费午夜 | 亚洲精品国产a久久久久久 | 97久久精品无码一区二区 | 久9re热视频这里只有精品 | 中文字幕+乱码+中文字幕一区 | 亚洲欧美日韩国产精品一区二区 | 亚洲成a人一区二区三区 | 国产综合在线观看 | 亚洲天堂2017无码 | 福利一区二区三区视频在线观看 | 老司机亚洲精品影院 | 色老头在线一区二区三区 | 一本久道久久综合狠狠爱 | 久久综合给久久狠狠97色 | 久久精品99久久香蕉国产色戒 | 免费乱码人妻系列无码专区 | 亚洲中文字幕乱码av波多ji | 亚洲欧洲中文日韩av乱码 | 久久国产精品萌白酱免费 | 日日干夜夜干 | 正在播放东北夫妻内射 | 内射爽无广熟女亚洲 | 欧美xxxx黑人又粗又长 | 蜜桃臀无码内射一区二区三区 | 成在人线av无码免观看麻豆 | 99久久99久久免费精品蜜桃 | 思思久久99热只有频精品66 | 久久久久免费看成人影片 | 亚洲国产欧美日韩精品一区二区三区 | 国产精品无码一区二区三区不卡 | 亚洲精品国偷拍自产在线麻豆 | 亚洲日本一区二区三区在线 | 亚洲综合无码久久精品综合 | 一本大道久久东京热无码av | 国产精品理论片在线观看 | 精品国产av色一区二区深夜久久 | 免费网站看v片在线18禁无码 | ass日本丰满熟妇pics | 国产精品久久久久久久9999 | 一个人免费观看的www视频 | 国产免费观看黄av片 | 精品久久8x国产免费观看 | 丰满护士巨好爽好大乳 | 国产精品多人p群无码 | 亚洲精品一区二区三区大桥未久 | 中国女人内谢69xxxxxa片 | 高清无码午夜福利视频 | 午夜免费福利小电影 | 国产精品香蕉在线观看 | 日韩欧美中文字幕在线三区 | 在线看片无码永久免费视频 | 亚洲国产精品久久久久久 | 久久 国产 尿 小便 嘘嘘 | 中文字幕久久久久人妻 | 国产极品视觉盛宴 | 性色av无码免费一区二区三区 | 极品尤物被啪到呻吟喷水 | 蜜臀av无码人妻精品 | 国产精品爱久久久久久久 | 成人无码精品1区2区3区免费看 | 日本大香伊一区二区三区 | 午夜福利一区二区三区在线观看 | 亚洲国产日韩a在线播放 | 精品熟女少妇av免费观看 | 欧美人妻一区二区三区 | 最新国产麻豆aⅴ精品无码 | 欧美亚洲国产一区二区三区 | 欧美色就是色 | 国产特级毛片aaaaaa高潮流水 | 在线播放无码字幕亚洲 | 国产办公室秘书无码精品99 | av无码不卡在线观看免费 | 亚洲最大成人网站 | 婷婷五月综合激情中文字幕 | 国产精品久久久久久久9999 | 国产成人无码a区在线观看视频app | av人摸人人人澡人人超碰下载 | 麻豆蜜桃av蜜臀av色欲av | 啦啦啦www在线观看免费视频 | 人妻人人添人妻人人爱 | 国产精品无码一区二区桃花视频 | 牲欲强的熟妇农村老妇女 | 男女超爽视频免费播放 | 亚洲综合无码久久精品综合 | 亚洲日韩av片在线观看 | 久久久成人毛片无码 | 又粗又大又硬又长又爽 | 天天av天天av天天透 | 久久久av男人的天堂 | 乱码午夜-极国产极内射 | 久久午夜无码鲁丝片午夜精品 | 久久午夜夜伦鲁鲁片无码免费 | 人妻少妇被猛烈进入中文字幕 | 激情内射日本一区二区三区 | 日本www一道久久久免费榴莲 | 少妇人妻大乳在线视频 | 久久久久成人片免费观看蜜芽 | 欧美精品在线观看 | 影音先锋中文字幕无码 | 亚洲成a人片在线观看日本 | 日本精品高清一区二区 | 久久久久99精品成人片 | 亚洲va欧美va天堂v国产综合 | 5858s亚洲色大成网站www | 日本乱人伦片中文三区 | 亚洲欧美精品aaaaaa片 | 波多野结衣av在线观看 | 国产乱人伦av在线无码 | 欧美熟妇另类久久久久久不卡 | 97色伦图片97综合影院 | 中国女人内谢69xxxxxa片 | 欧美亚洲国产一区二区三区 | 最近中文2019字幕第二页 | 亚洲人成网站色7799 | 日日躁夜夜躁狠狠躁 | 国产又爽又猛又粗的视频a片 | 少妇邻居内射在线 | 动漫av网站免费观看 | 久久亚洲精品中文字幕无男同 | 国产极品视觉盛宴 | 国产麻豆精品一区二区三区v视界 | 色综合视频一区二区三区 | 波多野结衣av一区二区全免费观看 | 骚片av蜜桃精品一区 | 国产精品内射视频免费 | 麻花豆传媒剧国产免费mv在线 | 强开小婷嫩苞又嫩又紧视频 | 国产精品亚洲lv粉色 | 午夜性刺激在线视频免费 | 无码一区二区三区在线观看 | 亚洲色欲久久久综合网东京热 | 色窝窝无码一区二区三区色欲 | 国产精品久免费的黄网站 | 久久久中文久久久无码 | 俄罗斯老熟妇色xxxx | 四虎影视成人永久免费观看视频 | 中国女人内谢69xxxxxa片 | 日韩精品a片一区二区三区妖精 | 国产精品99爱免费视频 | 无遮挡国产高潮视频免费观看 | 成人无码视频在线观看网站 | www成人国产高清内射 | 欧美人与牲动交xxxx | 美女张开腿让人桶 | 日本免费一区二区三区最新 | www国产亚洲精品久久久日本 | 色综合久久88色综合天天 | 国产疯狂伦交大片 | 高潮喷水的毛片 | 亚洲精品成人福利网站 | 4hu四虎永久在线观看 | 一本大道伊人av久久综合 | 国产超级va在线观看视频 | 久久综合色之久久综合 | 亚洲成av人片天堂网无码】 | 久久久久亚洲精品男人的天堂 | 大地资源中文第3页 | 成在人线av无码免费 | 色综合久久久久综合一本到桃花网 | 久精品国产欧美亚洲色aⅴ大片 | 久久人人爽人人爽人人片av高清 | 日本熟妇乱子伦xxxx | 天堂亚洲2017在线观看 | 女人和拘做爰正片视频 | 成人亚洲精品久久久久 | 一本色道久久综合亚洲精品不卡 | 成熟女人特级毛片www免费 | 国产精品久免费的黄网站 | 性做久久久久久久久 | 久久久久免费精品国产 | 中文久久乱码一区二区 | 国产小呦泬泬99精品 | 久久久久久亚洲精品a片成人 | 亚洲国产欧美日韩精品一区二区三区 | 国产在线精品一区二区高清不卡 | 亚拍精品一区二区三区探花 | 荫蒂被男人添的好舒服爽免费视频 | 亚洲综合无码一区二区三区 | 精品人妻人人做人人爽夜夜爽 | 国产精品a成v人在线播放 | 人妻与老人中文字幕 | 色一情一乱一伦一区二区三欧美 | 台湾无码一区二区 | 图片小说视频一区二区 | 久久精品成人欧美大片 | 亚洲 激情 小说 另类 欧美 | 蜜桃无码一区二区三区 | 中文字幕av日韩精品一区二区 | 国产无av码在线观看 | 性欧美大战久久久久久久 | 蜜桃视频插满18在线观看 | 国产精品国产自线拍免费软件 | 综合人妻久久一区二区精品 | 色婷婷久久一区二区三区麻豆 | 牲交欧美兽交欧美 | 一本久道久久综合婷婷五月 | 国产亚洲精品久久久久久国模美 | 中文字幕乱妇无码av在线 | av香港经典三级级 在线 | 超碰97人人做人人爱少妇 | √8天堂资源地址中文在线 | 日本一卡2卡3卡四卡精品网站 | 国产激情综合五月久久 | 中文字幕无码av波多野吉衣 | 免费网站看v片在线18禁无码 | 人妻人人添人妻人人爱 | 久久天天躁狠狠躁夜夜免费观看 | 成年美女黄网站色大免费全看 | 国产内射爽爽大片视频社区在线 | 国产亚洲精品久久久闺蜜 | 色 综合 欧美 亚洲 国产 | 在线精品亚洲一区二区 | 欧美性生交xxxxx久久久 | 中文精品无码中文字幕无码专区 | 玩弄人妻少妇500系列视频 | 欧美精品一区二区精品久久 | 亚洲 日韩 欧美 成人 在线观看 | 波多野结衣aⅴ在线 | 国产午夜视频在线观看 | 两性色午夜视频免费播放 | 亚洲中文字幕无码一久久区 | 午夜福利电影 | 亚洲人成网站在线播放942 | 欧美野外疯狂做受xxxx高潮 | 亚洲精品鲁一鲁一区二区三区 | 亚洲日韩一区二区 | 两性色午夜视频免费播放 | 日韩人妻系列无码专区 | 精品国产乱码久久久久乱码 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 国产精品99久久精品爆乳 | 无码乱肉视频免费大全合集 | 午夜福利不卡在线视频 | 国产在线精品一区二区高清不卡 | 小鲜肉自慰网站xnxx | 国产精品久久精品三级 | 300部国产真实乱 | 少妇人妻偷人精品无码视频 | 人人妻人人澡人人爽欧美精品 | 欧美老人巨大xxxx做受 | 人妻与老人中文字幕 | 国产 浪潮av性色四虎 | 精品国产一区二区三区av 性色 | 日韩精品无码一区二区中文字幕 | 国产成人人人97超碰超爽8 | 欧美成人午夜精品久久久 | 2020久久香蕉国产线看观看 | 日日摸天天摸爽爽狠狠97 | 国产 浪潮av性色四虎 | 午夜无码区在线观看 | 欧洲极品少妇 | 狂野欧美激情性xxxx | 亚洲精品鲁一鲁一区二区三区 | 在线精品国产一区二区三区 | 丰满少妇高潮惨叫视频 | 久久精品女人天堂av免费观看 | 日本精品人妻无码免费大全 | 国内少妇偷人精品视频免费 | 人人爽人人澡人人高潮 | 免费观看又污又黄的网站 | 丰满护士巨好爽好大乳 | 领导边摸边吃奶边做爽在线观看 | 久久人人97超碰a片精品 | 波多野结衣高清一区二区三区 | 国产舌乚八伦偷品w中 | 欧美丰满熟妇xxxx | 国产麻豆精品精东影业av网站 | 国产无套粉嫩白浆在线 | aa片在线观看视频在线播放 | 日本又色又爽又黄的a片18禁 | 麻豆av传媒蜜桃天美传媒 | 久久久久成人精品免费播放动漫 | 全黄性性激高免费视频 | 曰本女人与公拘交酡免费视频 | 黑人巨大精品欧美黑寡妇 | 亚洲综合无码一区二区三区 | 国产成人综合在线女婷五月99播放 | 黄网在线观看免费网站 | 无码精品人妻一区二区三区av | 日日碰狠狠丁香久燥 | 小泽玛莉亚一区二区视频在线 | 国产精品二区一区二区aⅴ污介绍 | 久久99精品久久久久久 | 成人无码精品1区2区3区免费看 | 香蕉久久久久久av成人 | 一区二区三区乱码在线 | 欧洲 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 国产无av码在线观看 | 亚洲精品久久久久久一区二区 | 性色欲网站人妻丰满中文久久不卡 | 又紧又大又爽精品一区二区 | 亲嘴扒胸摸屁股激烈网站 | 亚洲精品中文字幕乱码 | 亚洲成在人网站无码天堂 | 国产精品无码一区二区桃花视频 | 日日橹狠狠爱欧美视频 | 熟女俱乐部五十路六十路av | 亚洲经典千人经典日产 | 永久黄网站色视频免费直播 | 99久久精品午夜一区二区 | 国产精品毛片一区二区 | 久久五月精品中文字幕 | 99精品国产综合久久久久五月天 | 300部国产真实乱 | 中文字幕无线码 | 精品偷拍一区二区三区在线看 | 四虎国产精品免费久久 | 欧美zoozzooz性欧美 | www国产亚洲精品久久网站 | 亚洲日韩av片在线观看 | 亚洲综合另类小说色区 | 国产美女精品一区二区三区 | 成人亚洲精品久久久久软件 | 麻豆蜜桃av蜜臀av色欲av | 99久久人妻精品免费二区 | 久久综合狠狠综合久久综合88 | 青春草在线视频免费观看 | 亚洲无人区午夜福利码高清完整版 | 国产 精品 自在自线 | 九月婷婷人人澡人人添人人爽 | 麻豆国产丝袜白领秘书在线观看 | 久久这里只有精品视频9 | 131美女爱做视频 | 高清不卡一区二区三区 | 少妇人妻偷人精品无码视频 | 久久久无码中文字幕久... | 精品成人av一区二区三区 | 亚洲 高清 成人 动漫 | 日日摸天天摸爽爽狠狠97 | 国产成人无码a区在线观看视频app | 久久综合狠狠综合久久综合88 | 日韩精品无码一区二区中文字幕 | 熟女体下毛毛黑森林 | 国产亚洲欧美在线专区 | 欧美成人家庭影院 | 国产精品理论片在线观看 | 国产莉萝无码av在线播放 | 乱码av麻豆丝袜熟女系列 | 76少妇精品导航 | 亚洲成a人片在线观看日本 | 欧美国产日韩久久mv | 色爱情人网站 | 精品国产乱码久久久久乱码 | 国产人妻大战黑人第1集 | 亚洲国产欧美日韩精品一区二区三区 | 欧美成人免费全部网站 | 国产精品久久久久9999小说 | 精品一二三区久久aaa片 | 人妻熟女一区 | 熟妇人妻无乱码中文字幕 | 成人免费视频视频在线观看 免费 | 亚洲综合伊人久久大杳蕉 | 日本va欧美va欧美va精品 | 国产成人精品三级麻豆 | 国产凸凹视频一区二区 | 亚洲中文无码av永久不收费 | a片在线免费观看 | 久久久精品人妻久久影视 | 强奷人妻日本中文字幕 | 女人和拘做爰正片视频 | 欧美一区二区三区视频在线观看 | 久久久久久av无码免费看大片 | 蜜臀aⅴ国产精品久久久国产老师 | 精品水蜜桃久久久久久久 | 国产av一区二区三区最新精品 | 一本久久伊人热热精品中文字幕 | 中文字幕人妻丝袜二区 | 人妻无码αv中文字幕久久琪琪布 | 少妇人妻av毛片在线看 | 国产色视频一区二区三区 | 99久久无码一区人妻 | 亚洲人成网站色7799 | 亚洲人成人无码网www国产 | 三级4级全黄60分钟 | 性欧美疯狂xxxxbbbb | 久久久久免费看成人影片 | 亚洲成a人一区二区三区 | 午夜精品一区二区三区的区别 | 免费国产黄网站在线观看 | 国产绳艺sm调教室论坛 | 桃花色综合影院 | 男女爱爱好爽视频免费看 | 熟女俱乐部五十路六十路av | 荫蒂被男人添的好舒服爽免费视频 | 国产又粗又硬又大爽黄老大爷视 | 97se亚洲精品一区 | 一本加勒比波多野结衣 | 亚洲日韩乱码中文无码蜜桃臀网站 | 九九久久精品国产免费看小说 | 成人精品一区二区三区中文字幕 | 真人与拘做受免费视频一 | 国产乱码精品一品二品 | 亚洲啪av永久无码精品放毛片 | av无码电影一区二区三区 | 国内精品一区二区三区不卡 | 久久国产36精品色熟妇 | 国产午夜无码精品免费看 | 人人妻人人澡人人爽精品欧美 | 欧美丰满少妇xxxx性 | 天堂无码人妻精品一区二区三区 | 国产日产欧产精品精品app | 无码av岛国片在线播放 | 国产欧美熟妇另类久久久 | 亚洲中文无码av永久不收费 | 成在人线av无码免费 | 熟女少妇在线视频播放 | 人妻尝试又大又粗久久 | 亚洲日本一区二区三区在线 | 奇米影视888欧美在线观看 | 国产精品美女久久久久av爽李琼 | 强辱丰满人妻hd中文字幕 | 国产超碰人人爽人人做人人添 | 成年美女黄网站色大免费视频 | 大肉大捧一进一出好爽视频 | 18禁止看的免费污网站 | 日韩av激情在线观看 | 精品久久久中文字幕人妻 | 国产日产欧产精品精品app |