<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      方法區(qū)

        棧,堆,方法區(qū)的交互關(guān)系

        從線程共享與否的角度來看

        ThreadLocal:如何保證多個(gè)線程在并發(fā)環(huán)境下的安全性?典型場景就是數(shù)據(jù)庫連接管理,以及會(huì)話管理。

         堆,棧,方法區(qū)的交互關(guān)系

        下面涉及了對象的訪問定位

        (1)Person類的.class信息存放在方法區(qū)中;

        (2)person變量存放在Java棧的局部變量表中

        (3)真正的person對象存放在java堆中

        (4)在person對象中,有個(gè)指針指向方法區(qū)中person類型數(shù)據(jù),表明這個(gè)person對象使用方法區(qū)中的Person類new出來的。

         方法區(qū)的理解

        官方文檔:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4

         方法區(qū)在哪里?

        (1)《Java虛擬機(jī)規(guī)范》中明確說明:盡管所有的方法區(qū)在邏輯上是屬于堆的一部分,但一些簡單的實(shí)現(xiàn)可能不會(huì)選擇去進(jìn)行垃圾收集或者進(jìn)行壓縮。但對于HotSpotJVM而言,方法區(qū)還有一個(gè)別名叫做Non-Heap(非堆),目的就是要和堆分開
        (2)所以,方法區(qū)可以看作是一塊獨(dú)立于Java堆的內(nèi)存空間

         方法區(qū)的理解

        方法區(qū)主要存放的是Class,而堆中主要存放的是實(shí)例化的對象

        (1)方法區(qū)(Method Area)與Java堆一樣,線程共享的內(nèi)存區(qū)域。多個(gè)線程同時(shí)加載統(tǒng)一類時(shí),只能有一個(gè)線程能加載該類,其他線程只能等待該線程加載完畢,然后直接使用該類,即類只能加載一次。

        (2)方法區(qū)在JVM啟動(dòng)的時(shí)候被創(chuàng)建,并且它的實(shí)際物理內(nèi)存空間中和Java堆區(qū)一樣都是可以不連續(xù)的

        (3)方法區(qū)的大小,跟堆空間一樣,可以選擇固定大小或者可擴(kuò)展。

        (4)方法區(qū)的大小決定了系統(tǒng)可以保存多少個(gè)類,如果系統(tǒng)定義了太多了類,導(dǎo)致方法區(qū)溢出,虛擬機(jī)同樣會(huì)拋出內(nèi)存溢出錯(cuò)誤:java.lang.OutofMemoryError:PermGen space或者java.lang.OutOfMemoryError:Metaspace

          加載大量的第三方j(luò)ar包

          tomcat部署的工程過多(30-50個(gè))

          大量動(dòng)態(tài)的生成反射類

        (5)關(guān)閉JVM就會(huì)釋放這個(gè)區(qū)域的內(nèi)存

        代碼舉例

      public class MethodAreaDemo {
          public static void main(String[] args) {
              System.out.println("start...");
              try {
                  Thread.sleep(1000000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
      
              System.out.println("end...");
          }
      }

        簡單的程序,加載了1600多個(gè)類

       

         HotSpot方法區(qū)演進(jìn)

        (1)在JDK7及以前,習(xí)慣上把方法區(qū),稱為永久代。在JDK8開始,使用元空間取代了永久代,我們可以將方法區(qū)類比為Java中的接口,將永久代或元空間類比為Java中具體的實(shí)現(xiàn)類

        (2)本質(zhì)上,方法區(qū)和永久代并不等價(jià)。僅是對Hotspot而言的可以看作等價(jià)。《Java虛擬機(jī)規(guī)范》對如何實(shí)現(xiàn)方法區(qū),不做統(tǒng)一要求。例如:BEAJRockit / IBM J9 中不存在永久代的概念。

          現(xiàn)在來看,當(dāng)年使用永久代,不是好的idea。導(dǎo)致Java程序更容易OOm(超過-XX:MaxPermsize上限)

        (3)而到了JDK8,終于完全廢棄了永久代的概念,改用與JRockit、J9一樣在本地內(nèi)存中實(shí)現(xiàn)的元空間(Metaspace)來代替

        (4)元空間的本質(zhì)和永久代類似,都是JVM規(guī)范中方法區(qū)的實(shí)現(xiàn),不過元空間與永久代最大的區(qū)別在于:元空間不在虛擬機(jī)設(shè)置的內(nèi)存中,而是使用本地內(nèi)存;

        (5)永久代,元空間二者并不是名字改變了,內(nèi)存結(jié)構(gòu)也調(diào)整了

        (6)根據(jù)《Java虛擬機(jī)規(guī)范》的規(guī)定,如果方法區(qū)無法滿足新的內(nèi)存分配需求時(shí),將拋出OOM異常

         設(shè)置方法區(qū)的大小與OOM

        方法區(qū)的大小不必是固定的,JVM可以根據(jù)應(yīng)用的需要?jiǎng)討B(tài)調(diào)整

        JDK7及以前(永久代)

        (1)通過-XX:Permsize來設(shè)置永久代初始分配空間,默認(rèn)值是20.75M

        (2)-XX:MaxPermsize來設(shè)置永久代最大可分配空間。32位機(jī)器默認(rèn)是64M,64位機(jī)器模式是82M.

        (3)當(dāng)JVM加載的類信息超過了這個(gè)值,會(huì)報(bào)異常OutofMemoryError:PermGen space。

         JDK8及以后(元空間)

        JDK 8版本設(shè)置元空間大小

        (1)元數(shù)據(jù)區(qū)大小可以使用參數(shù) -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize 指定

        (2)默認(rèn)值依賴于平臺(tái),Windows下,-XX:MetaspaceSize 約為21M,-XX:MaxMetaspaceSize的值是-1,即沒有限制。

        (3)與永久代不同,如果不指定大小,默認(rèn)情況下,虛擬機(jī)會(huì)耗盡所有的可用系統(tǒng)內(nèi)存,如果元數(shù)據(jù)區(qū)發(fā)生溢出,虛擬機(jī)一樣會(huì)拋出異常OutOfMemoryError:Metaspace

        (4)-XX:MetaspaceSize:設(shè)置初始的元空間大小。對于一個(gè) 64位 的服務(wù)器端 JVM 來說,其默認(rèn)的 -XX:MetaspaceSize值為21MB。這就是初始的高水位線,一旦觸及這個(gè)水位線,F(xiàn)ull GC將會(huì)被觸發(fā)并卸載沒用的類(即這些類對應(yīng)的類加載器不再存活),然后這個(gè)高水位線將會(huì)重置。新的高水位線的值取決于GC后釋放了多少元空間。如果釋放的空間不足,那么在不超過MaxMetaspaceSize時(shí),適當(dāng)提高該值。如果釋放空間過多,則適當(dāng)降低該值。

        (5)如果初始化的高水位線設(shè)置過低,上述高水位線調(diào)整情況會(huì)發(fā)生很多次。通過垃圾回收器的日志可以觀察到Full GC多次調(diào)用。為了避免頻繁地GC,建議將-XX:MetaspaceSize設(shè)置為一個(gè)相對較高的值

        方法區(qū)OOM

        舉例:

        代碼:OOMTest類繼承ClassLoader類,獲得defineClass()方法,可自己進(jìn)行類的加載

      /**
       * jdk6/7中:
       * -XX:PermSize=10m -XX:MaxPermSize=10m
       *
       * jdk8中:
       * -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
       *
       */
      public class OOMTest extends ClassLoader {
          public static void main(String[] args) {
              int j = 0;
              try {
                  OOMTest test = new OOMTest();
                  for (int i = 0; i < 10000; i++) {
                      //創(chuàng)建ClassWriter對象,用于生成類的二進(jìn)制字節(jié)碼
                      ClassWriter classWriter = new ClassWriter(0);
                      //指明版本號(hào),修飾符,類名,包名,父類,接口
                      classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
                      //返回byte[]
                      byte[] code = classWriter.toByteArray();
                      //類的加載
                      test.defineClass("Class" + i, code, 0, code.length);//Class對象
                      j++;
                  }
              } finally {
                  System.out.println(j);
              }
          }
      }

        不設(shè)置元空間的上限

        使用默認(rèn)的JVM參數(shù),元空間不設(shè)置上限

        輸出結(jié)果

      10000

        設(shè)置元空間的上限

        JVM參數(shù)

        -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

        輸出結(jié)果

      3331
      Exception in thread "main" java.lang.OutOfMemoryError: Compressed class space
          at java.lang.ClassLoader.defineClass1(Native Method)
          at java.lang.ClassLoader.defineClass(ClassLoader.java:756)
          at java.lang.ClassLoader.defineClass(ClassLoader.java:635)
          at com.example.demo.methodarea.OOMTest.main(OOMTest.java:27)

        如何解決OOM

        這個(gè)屬于調(diào)優(yōu)的問題,這里先簡單說下

        (1)要解決OOM異常或heap space的異常,一般的手段是首先通過內(nèi)存映像分析工具(如Ec1ipse Memory Analyzer)對dump出來的堆轉(zhuǎn)儲(chǔ)快照進(jìn)行分析,重點(diǎn)是確認(rèn)內(nèi)存中的對象是否是必要的,也就是要先分清楚到底是出現(xiàn)了內(nèi)存泄漏(Memory Leak)還是內(nèi)存溢出(Memory Overflow)

        (2)內(nèi)存泄漏就是有大量的引用指向某些對象,但是這些對象以后不會(huì)使用了,但是因?yàn)樗鼈冞€和GC ROOT有關(guān)聯(lián),所以導(dǎo)致以后這些對象也不會(huì)被回收,這就是內(nèi)存泄漏的問題

        (3)如果是內(nèi)存泄漏,可進(jìn)一步通過工具查看泄漏對象到GC Roots的引用鏈。于是就能找到泄漏對象是通過怎樣的路徑與GC Roots相關(guān)聯(lián)并導(dǎo)致垃圾收集器無法自動(dòng)回收它們的。掌握了泄漏對象的類型信息,以及GC Roots引用鏈的信息,就可以比較準(zhǔn)確地定位出泄漏代碼的位置。

        (4)如果不存在內(nèi)存泄漏,換句話說就是內(nèi)存中的對象確實(shí)都還必須存活著,那就應(yīng)當(dāng)檢查虛擬機(jī)的堆參數(shù)(-Xmx與-Xms),與機(jī)器物理內(nèi)存對比看是否還可以調(diào)大,從代碼上檢查是否存在某些對象生命周期過長、持有狀態(tài)時(shí)間過長的情況,嘗試減少程序運(yùn)行期的內(nèi)存消耗

        方法區(qū)的內(nèi)部結(jié)構(gòu)

        方法區(qū)存儲(chǔ)什么?

        概念

         《深入理解Java虛擬機(jī)》書中對方法區(qū)(Method Area)存儲(chǔ)內(nèi)容描述如下:它用于存儲(chǔ)已被虛擬機(jī)加載的類型信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼緩存等。

         類型信息

        對每個(gè)加載的類型(類class,接口interface,枚舉enum,注解annotation),JVM必須在方法區(qū)中存儲(chǔ)以下類型信息:

        (1)這個(gè)類型的完整的有效名稱(全名=包名.類名)

        (2)這個(gè)類型直接父類的完整有效名(對于interface或者是java.lang.Object,都沒有父類)

        (3)這個(gè)類型的修飾符(public,abstract,final的某個(gè)子集)

        (4)這個(gè)類型直接接口的一個(gè)有序列表

        域(Field)信息

        也就是我們常說的成員變量,域信息是比較官方的說法

        (1)JVM必須在方法區(qū)中保存類型的所有域的相關(guān)信息以及域的聲明順序

        (2)域的相關(guān)信息包括:域名稱,域類型,域修飾符(public,private,protected,static,final,volatile,transient的某個(gè)子集)

        方法(Method)信息

        JVM必須保存所有方法的以下信息,同域信息一樣包括聲明順序:

         (1)方法名稱
        (2)方法的返回類型(博阿凱void返回類型),void在java中對應(yīng)的void.class
        (3)方法的參數(shù)的數(shù)量和類型(按順序)
        (4)方法的修飾符(public,private,protected,static,final,synchronized,native,abstract的一個(gè)子集)
        (5)方法的字節(jié)碼(bytecodes),操作數(shù)棧,局部變量表及大小(abstract和native方法除外)
        (6)異常表(abstract和native方法除外),異常表記錄每個(gè)異常處理的開始位置、結(jié)束位置、代碼處理在程序計(jì)數(shù)器中的偏移地址、被捕獲的異常類的常量池索引
        舉例
      **
       * 測試方法區(qū)的內(nèi)部構(gòu)成
       */
      public class MethodInnerStrucTest extends Object implements Comparable<String>,Serializable {
          //屬性
          public int num = 10;
          private static String str = "測試方法的內(nèi)部結(jié)構(gòu)";
          //構(gòu)造器
          //方法
          public void test1(){
              int count = 20;
              System.out.println("count = " + count);
          }
          public static int test2(int cal){
              int result = 0;
              try {
                  int value = 30;
                  result = value / cal;
              } catch (Exception e) {
                  e.printStackTrace();
              }
              return result;
          }
      
          @Override
          public int compareTo(String o) {
              return 0;
          }
      }

        javap -v -p MethodInnerStrucTest.class > test.txt

        反編譯字節(jié)碼文件,并輸出值文本文件中,便于查看。參數(shù)-p確保能查看private權(quán)限類型的字段或方法

        字節(jié)碼:

      Classfile /D:/practice/test01/demo/target/classes/com/example/demo/methodarea/MethodInnerStrucTest.class
        Last modified 2024-7-1; size 1700 bytes
        MD5 checksum 2089e2caed2dedbaa294a7446722b38b
        Compiled from "MethodInnerStrucTest.java"
      public class com.example.demo.methodarea.MethodInnerStrucTest extends java.lang.Object implements java.lang.Comparable<java.lang.String>, java.io.Serializable
        minor version: 0
        major version: 52
        flags: ACC_PUBLIC, ACC_SUPER
      Constant pool:
         #1 = Methodref          #18.#53        // java/lang/Object."<init>":()V
         #2 = Fieldref           #17.#54        // com/example/demo/methodarea/MethodInnerStrucTest.num:I
         #3 = Fieldref           #55.#56        // java/lang/System.out:Ljava/io/PrintStream;
         #4 = Class              #57            // java/lang/StringBuilder
         #5 = Methodref          #4.#53         // java/lang/StringBuilder."<init>":()V
         #6 = String             #58            // count =
         #7 = Methodref          #4.#59         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
         #8 = Methodref          #4.#60         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
         #9 = Methodref          #4.#61         // java/lang/StringBuilder.toString:()Ljava/lang/String;
        #10 = Methodref          #62.#63        // java/io/PrintStream.println:(Ljava/lang/String;)V
        #11 = Class              #64            // java/lang/Exception
        #12 = Methodref          #11.#65        // java/lang/Exception.printStackTrace:()V
        #13 = Class              #66            // java/lang/String
        #14 = Methodref          #17.#67        // com/example/demo/methodarea/MethodInnerStrucTest.compareTo:(Ljava/lang/String;)I
        #15 = String             #68            // 測試方法的內(nèi)部結(jié)構(gòu)
        #16 = Fieldref           #17.#69        // com/example/demo/methodarea/MethodInnerStrucTest.str:Ljava/lang/String;
        #17 = Class              #70            // com/example/demo/methodarea/MethodInnerStrucTest
        #18 = Class              #71            // java/lang/Object
        #19 = Class              #72            // java/lang/Comparable
        #20 = Class              #73            // java/io/Serializable
        #21 = Utf8               num
        #22 = Utf8               I
        #23 = Utf8               str
        #24 = Utf8               Ljava/lang/String;
        #25 = Utf8               <init>
        #26 = Utf8               ()V
        #27 = Utf8               Code
        #28 = Utf8               LineNumberTable
        #29 = Utf8               LocalVariableTable
        #30 = Utf8               this
        #31 = Utf8               Lcom/example/demo/methodarea/MethodInnerStrucTest;
        #32 = Utf8               test1
        #33 = Utf8               count
        #34 = Utf8               test2
        #35 = Utf8               (I)I
        #36 = Utf8               value
        #37 = Utf8               e
        #38 = Utf8               Ljava/lang/Exception;
        #39 = Utf8               cal
        #40 = Utf8               result
        #41 = Utf8               StackMapTable
        #42 = Class              #64            // java/lang/Exception
        #43 = Utf8               MethodParameters
        #44 = Utf8               compareTo
        #45 = Utf8               (Ljava/lang/String;)I
        #46 = Utf8               o
        #47 = Utf8               (Ljava/lang/Object;)I
        #48 = Utf8               <clinit>
        #49 = Utf8               Signature
        #50 = Utf8               Ljava/lang/Object;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/io/Serializable;
        #51 = Utf8               SourceFile
        #52 = Utf8               MethodInnerStrucTest.java
        #53 = NameAndType        #25:#26        // "<init>":()V
        #54 = NameAndType        #21:#22        // num:I
        #55 = Class              #74            // java/lang/System
        #56 = NameAndType        #75:#76        // out:Ljava/io/PrintStream;
        #57 = Utf8               java/lang/StringBuilder
        #58 = Utf8               count =
        #59 = NameAndType        #77:#78        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        #60 = NameAndType        #77:#79        // append:(I)Ljava/lang/StringBuilder;
        #61 = NameAndType        #80:#81        // toString:()Ljava/lang/String;
        #62 = Class              #82            // java/io/PrintStream
        #63 = NameAndType        #83:#84        // println:(Ljava/lang/String;)V
        #64 = Utf8               java/lang/Exception
        #65 = NameAndType        #85:#26        // printStackTrace:()V
        #66 = Utf8               java/lang/String
        #67 = NameAndType        #44:#45        // compareTo:(Ljava/lang/String;)I
        #68 = Utf8               測試方法的內(nèi)部結(jié)構(gòu)
        #69 = NameAndType        #23:#24        // str:Ljava/lang/String;
        #70 = Utf8               com/example/demo/methodarea/MethodInnerStrucTest
        #71 = Utf8               java/lang/Object
        #72 = Utf8               java/lang/Comparable
        #73 = Utf8               java/io/Serializable
        #74 = Utf8               java/lang/System
        #75 = Utf8               out
        #76 = Utf8               Ljava/io/PrintStream;
        #77 = Utf8               append
        #78 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
        #79 = Utf8               (I)Ljava/lang/StringBuilder;
        #80 = Utf8               toString
        #81 = Utf8               ()Ljava/lang/String;
        #82 = Utf8               java/io/PrintStream
        #83 = Utf8               println
        #84 = Utf8               (Ljava/lang/String;)V
        #85 = Utf8               printStackTrace
      {
        public int num;
          descriptor: I
          flags: ACC_PUBLIC
      
        private static java.lang.String str;
          descriptor: Ljava/lang/String;
          flags: ACC_PRIVATE, ACC_STATIC
      
        public com.example.demo.methodarea.MethodInnerStrucTest();
          descriptor: ()V
          flags: ACC_PUBLIC
          Code:
            stack=2, locals=1, args_size=1
               0: aload_0
               1: invokespecial #1                  // Method java/lang/Object."<init>":()V
               4: aload_0
               5: bipush        10
               7: putfield      #2                  // Field num:I
              10: return
            LineNumberTable:
              line 10: 0
              line 12: 4
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0      11     0  this   Lcom/example/demo/methodarea/MethodInnerStrucTest;
      
        public void test1();
          descriptor: ()V
          flags: ACC_PUBLIC
          Code:
            stack=3, locals=2, args_size=1
               0: bipush        20
               2: istore_1
               3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
               6: new           #4                  // class java/lang/StringBuilder
               9: dup
              10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
              13: ldc           #6                  // String count =
              15: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
              18: iload_1
              19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
              22: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
              25: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
              28: return
            LineNumberTable:
              line 17: 0
              line 18: 3
              line 19: 28
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0      29     0  this   Lcom/example/demo/methodarea/MethodInnerStrucTest;
                  3      26     1 count   I
      
        public static int test2(int);
          descriptor: (I)I
          flags: ACC_PUBLIC, ACC_STATIC
          Code:
            stack=2, locals=3, args_size=1
               0: iconst_0
               1: istore_1
               2: bipush        30
               4: istore_2
               5: iload_2
               6: iload_0
               7: idiv
               8: istore_1
               9: goto          17
              12: astore_2
              13: aload_2
              14: invokevirtual #12                 // Method java/lang/Exception.printStackTrace:()V
              17: iload_1
              18: ireturn
            Exception table:
               from    to  target type
                   2     9    12   Class java/lang/Exception
            LineNumberTable:
              line 21: 0
              line 23: 2
              line 24: 5
              line 27: 9
              line 25: 12
              line 26: 13
              line 28: 17
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  5       4     2 value   I
                 13       4     2     e   Ljava/lang/Exception;
                  0      19     0   cal   I
                  2      17     1 result   I
            StackMapTable: number_of_entries = 2
              frame_type = 255 /* full_frame */
                offset_delta = 12
                locals = [ int, int ]
                stack = [ class java/lang/Exception ]
              frame_type = 4 /* same */
          MethodParameters:
            Name                           Flags
            cal
      
        public int compareTo(java.lang.String);
          descriptor: (Ljava/lang/String;)I
          flags: ACC_PUBLIC
          Code:
            stack=1, locals=2, args_size=2
               0: iconst_0
               1: ireturn
            LineNumberTable:
              line 33: 0
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0       2     0  this   Lcom/example/demo/methodarea/MethodInnerStrucTest;
                  0       2     1     o   Ljava/lang/String;
          MethodParameters:
            Name                           Flags
            o
      
        public int compareTo(java.lang.Object);
          descriptor: (Ljava/lang/Object;)I
          flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
          Code:
            stack=2, locals=2, args_size=2
               0: aload_0
               1: aload_1
               2: checkcast     #13                 // class java/lang/String
               5: invokevirtual #14                 // Method compareTo:(Ljava/lang/String;)I
               8: ireturn
            LineNumberTable:
              line 10: 0
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0       9     0  this   Lcom/example/demo/methodarea/MethodInnerStrucTest;
          MethodParameters:
            Name                           Flags
            o                              synthetic
      
        static {};
          descriptor: ()V
          flags: ACC_STATIC
          Code:
            stack=1, locals=0, args_size=0
               0: ldc           #15                 // String 測試方法的內(nèi)部結(jié)構(gòu)
               2: putstatic     #16                 // Field str:Ljava/lang/String;
               5: return
            LineNumberTable:
              line 13: 0
      }
      Signature: #50                          // Ljava/lang/Object;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/io/Serializable;
      SourceFile: "MethodInnerStrucTest.java"

        類型信息

        在運(yùn)行時(shí)方法區(qū)中,類信息中記錄了哪個(gè)加載器加載了該類,同時(shí)類加載器也記錄了它加載了哪些類

      //類型信息
      public class com.example.demo.methodarea.MethodInnerStrucTest extends java.lang.Object implements java.lang.Comparable<java.lang.String>, java.io.Serializable

        域信息

        (1)descriptor: I 表示字段類型為Integer

        (2)flags: ACC_PUBLIC 表示字段權(quán)限修飾符為public

        public int num;
          descriptor: I
          flags: ACC_PUBLIC
      
        private static java.lang.String str;
          descriptor: Ljava/lang/String;
          flags: ACC_PRIVATE, ACC_STATIC

        方法信息:

        (1)descriptor: ()V 表示方法返回值類型為 void

        (2)flags: ACC_PUBLIC 表示方法權(quán)限修飾符為 public

        (3)stack=3 表示操作數(shù)棧深度為 3

        (4)locals=2 表示局部變量個(gè)數(shù)為 2 個(gè)(實(shí)力方法包含 this)

        (5)test1() 方法雖然沒有參數(shù),但是其 args_size=1 ,這時(shí)因?yàn)閷?this 作為了參數(shù)

      public void test1();
          descriptor: ()V
          flags: ACC_PUBLIC
          Code:
            stack=3, locals=2, args_size=1
               0: bipush        20
               2: istore_1
               3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
               6: new           #4                  // class java/lang/StringBuilder
               9: dup
              10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
              13: ldc           #6                  // String count =
              15: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
              18: iload_1
              19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
              22: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
              25: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
              28: return
            LineNumberTable:
              line 17: 0
              line 18: 3
              line 19: 28
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0      29     0  this   Lcom/atguigu/java/MethodInnerStrucTest;
                  3      26     1 count   I

        non-final類型的類變量

        (1)靜態(tài)變量和類關(guān)聯(lián)在一起,隨著類的加載而加載,他們成為類數(shù)據(jù)在邏輯的一部分

        (2)類變量被類的所有實(shí)例共享,即使沒有類實(shí)例時(shí),也可以訪問

        舉例

        (1)如下,即使我們把order設(shè)置為null,也不會(huì)出現(xiàn)空指針異常

        (2)這更加表明了static類型的字段和方法隨著類的加載而加載,并不屬于特定的類實(shí)例

      public class MethodAreaTest {
          public static void main(String[] args) {
              Order order = null;
              order.hello();
              System.out.println(order.count);
          }
      }
      
      class Order {
          public static int count = 1;
          public static final int number = 2;
      
      
          public static void hello() {
              System.out.println("hello!");
          }
      }

        輸出結(jié)果:

      hello!
      1

        全局常量:static final

        (1)全局常量就是使用 static final 進(jìn)行修飾

        (2)被聲明為final的類變量的處理方法則不同,每個(gè)全局常量在編譯的時(shí)候就會(huì)被分配了。

        查看上面的代碼,這部分的字節(jié)碼指令:

      class Order {
          public static int count = 1;
          public static final int number = 2;
          ...
      }    
      public static int count;
          descriptor: I
          flags: ACC_PUBLIC, ACC_STATIC
      
        public static final int number;
          descriptor: I
          flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
          ConstantValue: int 2

        可以發(fā)現(xiàn)static和final同時(shí)修飾的numer的值在編譯上時(shí)候已經(jīng)寫死在字節(jié)碼文件中了

        運(yùn)行時(shí)常量池

        運(yùn)行時(shí)常量池 VS 常量池

        官方文檔:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html

         (1)方法區(qū),內(nèi)部包含了運(yùn)行時(shí)常量池

        (2)字節(jié)碼文件,內(nèi)部包含了常量池。(之前的字節(jié)碼文件中已經(jīng)看到了很多Constant pool的東西,這個(gè)就是常量池)

        (3)要弄清楚方法區(qū),需要理解清楚ClassFile,因?yàn)榧虞d類的信息都在方法區(qū)。

        (4)要弄清楚方法區(qū)的運(yùn)行時(shí)常量池,需要理解清楚ClassFile中的常量池。

        常量池

        (1)一個(gè)有效的字節(jié)碼文件中除了包含類的版本信息、字段、方法以及接口等描述符信息外。還包含一項(xiàng)信息就是常量池表(Constant Pool Table),包括各種字面量和對類型、域和方法的符號(hào)引用。

        (2)字面量: 10 , “我是某某”這種數(shù)字和字符串都是字面量

         為什么需要常量池

        (1)一個(gè)java源文件中的類、接口,編譯后產(chǎn)生一個(gè)字節(jié)碼文件。而Java中的字節(jié)碼需要數(shù)據(jù)支持,通常這種數(shù)據(jù)會(huì)很大以至于不能直接存到字節(jié)碼里,換另一種方式,可以存到常量池。這個(gè)字節(jié)碼包含了指向常量池的引用。在動(dòng)態(tài)鏈接的時(shí)候會(huì)用到運(yùn)行時(shí)常量池,之前有介紹

        比如:如下的代碼:

      public class SimpleClass {
          public void sayHello() {
              System.out.println("hello");
          }
      }

        (1)雖然上述代碼只有194字節(jié),但是里面卻使用了String、System、PrintStream及Object等結(jié)構(gòu)。

        (2)比如說我們這個(gè)文件中有6個(gè)地方用到了”hello”這個(gè)字符串,如果不用常量池,就需要在6個(gè)地方全寫一遍,造成臃腫。我們可以將”hello”等所需用到的結(jié)構(gòu)信息記錄在常量池中,并通過引用的方式,來加載、調(diào)用所需的結(jié)構(gòu)

        (3)這里的代碼量其實(shí)很少了,如果代碼多的話,引用的結(jié)構(gòu)將會(huì)更多,這里就需要用到常量池了。

        常量池中有啥

        數(shù)量值,字符串值,類引用,字段引用,方法引用

        MethodInnerStrucTest 的 test1方法的字節(jié)碼

       0 bipush 20
       2 istore_1
       3 getstatic #3 <java/lang/System.out>
       6 new #4 <java/lang/StringBuilder>
       9 dup
      10 invokespecial #5 <java/lang/StringBuilder.<init>>
      13 ldc #6 <count = >
      15 invokevirtual #7 <java/lang/StringBuilder.append>
      18 iload_1
      19 invokevirtual #8 <java/lang/StringBuilder.append>
      22 invokevirtual #9 <java/lang/StringBuilder.toString>
      25 invokevirtual #10 <java/io/PrintStream.println>
      28 return

        #3,#5等等這些帶# 的,都是引用了常量池。

        運(yùn)行時(shí)常量池

        (1)運(yùn)行時(shí)常量池(Runtime Constant Pool)是方法區(qū)的一部分

        (2)常量池表(Constant Pool Table)是Class字節(jié)碼文件的一部分,用于存放編譯期生成的各種字面量與符號(hào)引用,這部分內(nèi)容在類加載后存放到方法區(qū)的運(yùn)行時(shí)常量池中。(運(yùn)行時(shí)常量池就是常量池在程序運(yùn)行時(shí)的稱呼)

        (3)運(yùn)行時(shí)常量池,在加載類和接口到虛擬機(jī)后,就會(huì)創(chuàng)建對應(yīng)的運(yùn)行時(shí)常量池

        (4)JVM為每個(gè)已加載的類型(類或接口)都維護(hù)一個(gè)常量池,池中的數(shù)據(jù)項(xiàng)像數(shù)組項(xiàng)一樣,都是通過索引訪問的。

        (5)運(yùn)行時(shí)常量池中包含多種不同的常量,包括編譯期就已經(jīng)明確的數(shù)值字面量,也包括到運(yùn)行期解析后才能夠獲得的方法或者字段引用。此時(shí)不再是常量池中的符號(hào)地址了,這里換為真實(shí)地址

            運(yùn)行時(shí)常量池,相對于Class文件常量池的另一重要特征是:具備動(dòng)態(tài)性

        (6)運(yùn)行時(shí)常量池類似于傳統(tǒng)編程語言中的符號(hào)表(symbol table),但是它所包含的數(shù)據(jù)卻比符號(hào)表要更加豐富一些

        (7)當(dāng)創(chuàng)建類或接口的運(yùn)行時(shí)常量池時(shí),如果構(gòu)造運(yùn)行時(shí)常量池所需的內(nèi)存空間超過了方法區(qū)所能提供的最大值,則JVM會(huì)拋OutofMemoryError異常

        方法區(qū)的使用舉例

      public class MethodAreaDemo1 {
          public static void main(String[] args) {
              int x = 500;
              int y = 100;
              int a = x / y;
              int b = 50;
              System.out.println(a + b);
          }
      }

        字節(jié)碼

      Classfile /D:/practice/test01/demo/target/classes/com/example/demo/methodarea/MethodAreaDemo1.class
        Last modified 2024-7-2; size 693 bytes
        MD5 checksum ab63b3f6ca578dd4b6b79c3b51b75d31
        Compiled from "MethodAreaDemo1.java"
      public class com.example.demo.methodarea.MethodAreaDemo1
        minor version: 0
        major version: 52
        flags: ACC_PUBLIC, ACC_SUPER
      Constant pool:
         #1 = Methodref          #5.#25         // java/lang/Object."<init>":()V
         #2 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
         #3 = Methodref          #28.#29        // java/io/PrintStream.println:(I)V
         #4 = Class              #30            // com/example/demo/methodarea/MethodAreaDemo1
         #5 = Class              #31            // java/lang/Object
         #6 = Utf8               <init>
         #7 = Utf8               ()V
         #8 = Utf8               Code
         #9 = Utf8               LineNumberTable
        #10 = Utf8               LocalVariableTable
        #11 = Utf8               this
        #12 = Utf8               Lcom/example/demo/methodarea/MethodAreaDemo1;
        #13 = Utf8               main
        #14 = Utf8               ([Ljava/lang/String;)V
        #15 = Utf8               args
        #16 = Utf8               [Ljava/lang/String;
        #17 = Utf8               x
        #18 = Utf8               I
        #19 = Utf8               y
        #20 = Utf8               a
        #21 = Utf8               b
        #22 = Utf8               MethodParameters
        #23 = Utf8               SourceFile
        #24 = Utf8               MethodAreaDemo1.java
        #25 = NameAndType        #6:#7          // "<init>":()V
        #26 = Class              #32            // java/lang/System
        #27 = NameAndType        #33:#34        // out:Ljava/io/PrintStream;
        #28 = Class              #35            // java/io/PrintStream
        #29 = NameAndType        #36:#37        // println:(I)V
        #30 = Utf8               com/example/demo/methodarea/MethodAreaDemo1
        #31 = Utf8               java/lang/Object
        #32 = Utf8               java/lang/System
        #33 = Utf8               out
        #34 = Utf8               Ljava/io/PrintStream;
        #35 = Utf8               java/io/PrintStream
        #36 = Utf8               println
        #37 = Utf8               (I)V
      {
        public com.example.demo.methodarea.MethodAreaDemo1();
          descriptor: ()V
          flags: ACC_PUBLIC
          Code:
            stack=1, locals=1, args_size=1
               0: aload_0
               1: invokespecial #1                  // Method java/lang/Object."<init>":()V
               4: return
            LineNumberTable:
              line 3: 0
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0       5     0  this   Lcom/example/demo/methodarea/MethodAreaDemo1;
      
        public static void main(java.lang.String[]);
          descriptor: ([Ljava/lang/String;)V
          flags: ACC_PUBLIC, ACC_STATIC
          Code:
            stack=3, locals=5, args_size=1
               0: sipush        500
               3: istore_1
               4: bipush        100
               6: istore_2
               7: iload_1
               8: iload_2
               9: idiv
              10: istore_3
              11: bipush        50
              13: istore        4
              15: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
              18: iload_3
              19: iload         4
              21: iadd
              22: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
              25: return
            LineNumberTable:
              line 5: 0
              line 6: 4
              line 7: 7
              line 8: 11
              line 9: 15
              line 10: 25
            LocalVariableTable:
              Start  Length  Slot  Name   Signature
                  0      26     0  args   [Ljava/lang/String;
                  4      22     1     x   I
                  7      19     2     y   I
                 11      15     3     a   I
                 15      11     4     b   I
          MethodParameters:
            Name                           Flags
            args
      }
      SourceFile: "MethodAreaDemo1.java"

        圖解字節(jié)碼指令執(zhí)行過程

        (1)初始狀態(tài)

        (2)首先將操作數(shù)500壓入操作數(shù)棧中

         (3)然后操作數(shù)500從操作數(shù)棧中取出,存儲(chǔ)到局部變量表中索引為1的位置

         (4)

         (5)

         (6)

         (7)

         (8)

         (9)

         (10)

         (11)getstatic (#26.#27)獲取system類

         (12)

         (13)

         (14)執(zhí)行加法運(yùn)算后,將計(jì)算結(jié)果放在操作數(shù)棧頂

         (15)打印

         (16)

         符號(hào)引用->直接引用

        (1)上面代碼調(diào)用 System.out.println() 方法時(shí),首先需要看看 System 類有沒有加載,再看看 PrintStream 類有沒有加載

        (2)如果沒有加載,則執(zhí)行加載,執(zhí)行時(shí),將常量池中的符號(hào)引用(字面量)轉(zhuǎn)換為運(yùn)行時(shí)常量池的直接引用(真正的地址值)

        方法區(qū)的演進(jìn)

        永久代的演進(jìn)過程

        (1)首先明確:只有Hotspot才有永久代。BEA JRockit、IBMJ9等來說,是不存在永久代的概念的。原則上如何實(shí)現(xiàn)方法區(qū)屬于虛擬機(jī)實(shí)現(xiàn)細(xì)節(jié),不受《Java虛擬機(jī)規(guī)范》管束,并不要求統(tǒng)一

        (2)Hotspot中方法區(qū)的變化:

          JDK1.6及以前 有永久代(permanent generation),靜態(tài)變量存儲(chǔ)在永久代上

          JDK1.7 有永久代,但已經(jīng)逐步 “去永久代”,字符串常量池,靜態(tài)變量移除,保存在堆中

          JDK1.8 無永久代,類型信息,字段,方法,常量保存在本地內(nèi)存的元空間,但字符串常量池、靜態(tài)變量仍然在堆中

        JDK6

        方法區(qū)由永久代實(shí)現(xiàn),使用 JVM 虛擬機(jī)內(nèi)存(虛擬的內(nèi)存)

       

         JDK7

        方法區(qū)由永久代實(shí)現(xiàn),使用 JVM 虛擬機(jī)內(nèi)存

         JDK8

        方法區(qū)由元空間實(shí)現(xiàn),使用物理機(jī)本地內(nèi)存

       

         永久代為什么要被元空間替代?

        官方文檔:http://openjdk.java.net/jeps/122

        (1)隨著Java8的到來,HotSpot VM中再也見不到永久代了。但是這并不意味著類的元數(shù)據(jù)信息也消失了。這些數(shù)據(jù)被移到了一個(gè)與堆不相連的本地內(nèi)存區(qū)域,這個(gè)區(qū)域叫做元空間(Metaspace)。

        (2)由于類的元數(shù)據(jù)分配在本地內(nèi)存中,元空間的最大可分配空間就是系統(tǒng)可用內(nèi)存空間

        (3)這項(xiàng)改動(dòng)是很有必要的,原因有:

          為永久代設(shè)置空間大小是很難確定的。在某些場景下,如果動(dòng)態(tài)加載類過多,容易產(chǎn)生Perm區(qū)的OOM。比如某個(gè)實(shí)際Web工程中,因?yàn)楣δ茳c(diǎn)比較多,在運(yùn)行過程中,要不斷動(dòng)態(tài)加載很多類,經(jīng)常出現(xiàn)致命錯(cuò)誤。Exception in thread 'dubbo client x.x connector' java.lang.OutOfMemoryError:PermGen space而元空間和永久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中,而是使用本地內(nèi)存。 因此,默認(rèn)情況下,元空間的大小僅受本地內(nèi)存限制

          對永久代進(jìn)行調(diào)優(yōu)是很困難的。方法區(qū)的垃圾收集主要回收兩部分內(nèi)容:常量池中廢棄的常量和不再用的類型,方法區(qū)的調(diào)優(yōu)主要是為了降低Full GC

          有些人認(rèn)為方法區(qū)(如HotSpot虛擬機(jī)中的元空間或者永久代)是沒有垃圾收集行為的,其實(shí)不然。《Java虛擬機(jī)規(guī)范》對方法區(qū)的約束是非常寬松的,提到過可以不要求虛擬機(jī)在方法區(qū)中實(shí)現(xiàn)垃圾收集。事實(shí)上也確實(shí)有未實(shí)現(xiàn)或未能完整實(shí)現(xiàn)方法區(qū)類型卸載的收集器存在(如JDK11時(shí)期的ZGC收集器就不支持類卸載)

          一般來說這個(gè)區(qū)域的回收效果比較難令人滿意,尤其是類型的卸載,條件相當(dāng)苛刻**。但是這部分區(qū)域的回收有時(shí)又確實(shí)是必要的。以前Sun公司的Bug列表中,曾出現(xiàn)過的若干個(gè)嚴(yán)重的Bug就是由于低版本的HotSpot虛擬機(jī)對此區(qū)域未完全回收而導(dǎo)致內(nèi)存泄漏

        字符串常量池

        字符串常量池StringTable為什么要調(diào)整位置?

        (1)JDK7中將StringTable放到了堆空間中。因?yàn)橛谰么幕厥招屎艿停贔ull GC的時(shí)候才會(huì)執(zhí)行永久代的垃圾回收,而Full GC是老年代的空間不足、永久代不足時(shí)才會(huì)觸發(fā)。

        (2)這就導(dǎo)致StringTable回收效率不高,而我們開發(fā)中會(huì)有大量的字符串被創(chuàng)建,回收效率低,導(dǎo)致永久代內(nèi)存不足。放到堆里,能及時(shí)回收內(nèi)存。

        靜態(tài)變量放在哪里

        對象實(shí)體在哪里放著?

      /**
       * 結(jié)論:
       * 1、靜態(tài)引用對應(yīng)的對象實(shí)體(也就是這個(gè)new byte[1024 * 1024 * 100])始終都存在堆空間,
       * 2、只是那個(gè)變量(相當(dāng)于下面的arr變量名)在JDK6,JDK7,JDK8存放位置中有所變化
       *
       * jdk7:
       * -Xms200m -Xmx200m -XX:PermSize=300m -XX:MaxPermSize=300m -XX:+PrintGCDetails
       * jdk 8:
       * -Xms200m -Xmx200m -XX:MetaspaceSize=300m -XX:MaxMetaspaceSize=300m -XX:+PrintGCDetails
       */
      public class StaticFieldTest {
          private static byte[] arr = new byte[1024 * 1024 * 100];//100MB
      
          public static void main(String[] args) {
              System.out.println(StaticFieldTest.arr);
          }
      }

        JDK6環(huán)境下:

         JDK7環(huán)境下

         JDK8環(huán)境下

         運(yùn)行時(shí)數(shù)據(jù)區(qū)總結(jié)

         方法區(qū)的垃圾回收

        (1)有些人認(rèn)為方法區(qū)(如Hotspot虛擬機(jī)中的元空間或者永久代)是沒有垃圾收集行為的,其實(shí)不然。《Java虛擬機(jī)規(guī)范》對方法區(qū)的約束是非常寬松的,提到過可以不要求虛擬機(jī)在方法區(qū)中實(shí)現(xiàn)垃圾收集。事實(shí)上也確實(shí)有未實(shí)現(xiàn)或未能完整實(shí)現(xiàn)方法區(qū)類型卸載的收集器存在(如JDK11時(shí)期的ZGC收集器就不支持類卸載)。  

        (2)一般來說這個(gè)區(qū)域的回收效果比較難令人滿意,尤其是類型的卸載,條件相當(dāng)苛刻。但是這部分區(qū)域的回收有時(shí)又確實(shí)是必要的。以前sun公司的Bug列表中,曾出現(xiàn)過的若干個(gè)嚴(yán)重的Bug就是由于低版本的HotSpot虛擬機(jī)對此區(qū)域未完全回收而導(dǎo)致內(nèi)存泄漏

        (3)方法區(qū)的垃圾收集主要回收兩部分內(nèi)容:常量池中廢棄的常量和不再使用的類型。

        (4)先來說說方法區(qū)內(nèi)常量池之中主要存放的兩大類常量:字面量和符號(hào)引用。字面量比較接近Java語言層次的常量概念,如文本字符串、被聲明為final的常量值等。而符號(hào)引用則屬于編譯原理方面的概念,包括下面三類常量

          類和接口的全限定名

          字段的名稱和描述符

          方法的名稱和描述符

        (5)HotSpot虛擬機(jī)對常量池的回收策略是很明確的,只要常量池中的常量沒有被任何地方引用,就可以被回收

        (6)回收廢棄常量與回收J(rèn)ava堆中的對象非常類似。(關(guān)于常量的回收比較簡單,重點(diǎn)是類的回收)

        下面也稱作類卸載

        (1)判定一個(gè)常量是否“廢棄”還是相對簡單,而要判定一個(gè)類型是否屬于“不再被使用的類”的條件就比較苛刻了。需要同時(shí)滿足下面三個(gè)條件:

          該類所有的實(shí)例都已經(jīng)被回收,也就是Java堆中不存在該類及其任何派生子類的實(shí)例

          加載該類的類加載器已經(jīng)被回收,這個(gè)條件除非是經(jīng)過精心設(shè)計(jì)的可替換類加載器的場景,如OSGi、JSP的重加載等,否則通常是很難達(dá)成的。

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

        (2)Java虛擬機(jī)被允許對滿足上述三個(gè)條件的無用類進(jìn)行回收,這里說的僅僅是“被允許”,而并不是和對象一樣,沒有引用了就必然會(huì)回收。關(guān)于是否要對類型進(jìn)行回收,HotSpot虛擬機(jī)提供了-Xnoclassgc參數(shù)進(jìn)行控制,還可以使用-verbose:class 以及 -XX:+TraceClass-Loading-XX:+TraceClassUnLoading查看類加載和卸載信息

        (3)在大量使用反射、動(dòng)態(tài)代理、CGLib等字節(jié)碼框架,動(dòng)態(tài)生成JSP以及OSGi這類頻繁自定義類加載器的場景中,通常都需要Java虛擬機(jī)具備類型卸載的能力,以保證不會(huì)對方法區(qū)造成過大的內(nèi)存壓力。

      posted on 2024-07-03 16:16  homle  閱讀(42)  評論(0)    收藏  舉報(bào)

      主站蜘蛛池模板: 欧美成人精品手机在线| 国产精品人人爽人人做我的可爱| 内射干少妇亚洲69XXX| 精品乱码一区二区三四五区 | 午夜国产理论大片高清| a级黑人大硬长爽猛出猛进| 噜噜噜噜私人影院| 少妇被多人c夜夜爽爽av| 国产成人MV视频在线观看 | 亚洲一区二区三区自拍公司| 99久久精品国产一区二区| 亚洲欧美综合中文| 日日爽日日操| 亚洲国产另类久久久精品网站| 国产91精品一区二区亚洲| 在线免费成人亚洲av| 内射一区二区三区四区| 污网站在线观看视频| 欧美区一区二区三区| 日韩av不卡一区二区在线| L日韩欧美看国产日韩欧美| 久久96热在精品国产高清| 日韩欧美人妻一区二区三区| 精品久久精品久久精品九九| 午夜在线观看成人av| 五月丁香啪啪| 久久精品国产亚洲αv忘忧草| 久久不见久久见免费视频观看| 国产女人喷潮视频在线观看| 欧美成人免费一区二区三区视频 | av天堂午夜精品一区| 国产亚洲精品日韩香蕉网| 色成人亚洲| 亚洲午夜精品久久久久久浪潮| 亚洲国产成人精品激情姿源| 精品久久久无码人妻中文字幕| 亚洲色大成网站WWW永久麻豆| 高潮精品熟妇一区二区三区| 亚洲综合另类小说色区色噜噜| 中文字幕日韩有码第一页| 最新精品国偷自产在线美女足|