日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

3.内存分配、逃逸分析与栈上分配、直接内存和运行时常量池、基本类型的包装类和常量池、TLAB、可达性分析算法(学习笔记)

發(fā)布時(shí)間:2024/9/27 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 3.内存分配、逃逸分析与栈上分配、直接内存和运行时常量池、基本类型的包装类和常量池、TLAB、可达性分析算法(学习笔记) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

3.JVM內(nèi)存分配
3.1.內(nèi)存分配概述
3.2.內(nèi)存分配–Eden區(qū)域
3.3.內(nèi)存分配–大對(duì)象直接進(jìn)老年代
3.3.1.背景
3.3.2.解析
3.4.內(nèi)存分配–長(zhǎng)期存活的對(duì)象進(jìn)去老年代
3.5.內(nèi)存分配–空間分配擔(dān)保
3.5.1.堆空間參數(shù)
3.5.2.-XX:HandlePromotionFailure
3.6.內(nèi)存分配–逃逸分析與棧上分配
3.6.1.逃逸分析
3.6.1.1.方法逃逸
3.6.1.2.線程分配
3.6.2.棧上分配
3.6.3.逃逸分析/棧上分配的優(yōu)勢(shì)分析
3.6.3.1.同步消除
3.6.4.標(biāo)量替換
3.6.5.什么情況下會(huì)發(fā)生逃逸?
3.7.直接內(nèi)存
3.8.Java內(nèi)存區(qū)域-直接內(nèi)存和運(yùn)行時(shí)常量池
3.8.1.運(yùn)行時(shí)常量池簡(jiǎn)介
3.8.2.Class文件中的信息常量池
3.8.3.常量池的好處
3.8.4.基本類(lèi)型的包裝類(lèi)和常量池
3.9.對(duì)象在內(nèi)存中的布局-對(duì)象的創(chuàng)建
3.10.探究對(duì)象的結(jié)構(gòu)
3.11.深度理解對(duì)象的訪問(wèn)定位
3.12.Java對(duì)象訪問(wèn)方式
3.12.1.通過(guò)句柄訪問(wèn)
3.12.2.通過(guò)直接指針訪問(wèn)
3.13.對(duì)象分配內(nèi)存的策略
3.13.1.線程安全問(wèn)題
3.13.1.1.本地線程分配緩沖----TLAB
3.13.1.2.TLAB生命周期
3.13.1.3.TLAB的大小
3.13.1.4.總結(jié)
3.13.1.5.參數(shù)總結(jié)
3.14.垃圾回收-判斷對(duì)象是否存活算法-引用計(jì)數(shù)法詳解
3.15.垃圾回收-判斷對(duì)象是否存活算法-可達(dá)性分析法詳解
3.15.1.可達(dá)性分析算法
3.15.2.finalize()方法最終判定對(duì)象是否存活
3.15.3.Java引用
3.15.3.1.強(qiáng)引用
3.15.3.2.軟引用
3.15.3.3.弱引用
3.15.3.4.虛引用
3.15.3.5.軟引用和弱引用進(jìn)一步說(shuō)明
3.15.3.6.虛引用進(jìn)一步說(shuō)明:

3.JVM內(nèi)存分配

3.1.內(nèi)存分配概述

3.1.1.優(yōu)先分配到eden

3.1.2.大對(duì)象直接分配到老年代

3.1.3.長(zhǎng)期存活的對(duì)象分配到老年代

3.1.4.空間分配擔(dān)保

3.1.5.動(dòng)態(tài)對(duì)象年齡判斷

Java對(duì)象所占用的內(nèi)存主要在堆上實(shí)現(xiàn),因?yàn)槎咽蔷€程共享的,因此在堆上分配內(nèi)存時(shí)需要進(jìn)行加鎖,這就導(dǎo)致了創(chuàng)建對(duì)象的開(kāi)銷(xiāo)比較大。當(dāng)堆上空間不足時(shí),會(huì)觸發(fā)GC,如果GC后空間仍然不足,則會(huì)拋出OutOfMemory異常。

為了提升內(nèi)存分配效率,在年輕代的Eden區(qū)HotSpot虛擬機(jī)使用了兩種技術(shù)來(lái)加快內(nèi)存分配 ,分別是bump-the-pointerTLAB(Thread-Local Allocation Buffers)。由于Eden區(qū)是連續(xù)的,因此bump-the-pointer技術(shù)的核心就是跟蹤最后創(chuàng)建的一個(gè)對(duì)象,在對(duì)象創(chuàng)建時(shí),只需要檢查最后一個(gè)對(duì)象后面是否足夠的內(nèi)存即可,從而大大加快內(nèi)存分配速度;而對(duì)于TLAB技術(shù)是對(duì)于多線程而言的,它會(huì)為每個(gè)新創(chuàng)建的線程在新生代的Eden Space上分配一塊獨(dú)立的空間,這塊空間成為T(mén)LAB(Thread Local Allocation Buffer),其大小由JVM根據(jù)運(yùn)行情況計(jì)算而得。通過(guò)XX:TLABWasteTargetPercent來(lái)設(shè)置其可占用的Eden Space的百分比,默認(rèn)是1%。在TLAB上分配內(nèi)存不需要加鎖,一般JVM會(huì)優(yōu)先在TLAB上分配內(nèi)存,如果對(duì)象過(guò)大或者TLAB空間已經(jīng)用完,則仍然在堆上進(jìn)行分配。因此,在編寫(xiě)程序時(shí),多個(gè)小對(duì)象比大的對(duì)象分配起來(lái)效率更高??稍趩?dòng)參數(shù)上增加-XX:+PrintTLAB來(lái)查看TLAB空間的使用情況。

對(duì)象如果在年輕代存活了足夠長(zhǎng)的時(shí)間而沒(méi)有被清理掉(即在幾次Minor GC后存活了下來(lái)),則會(huì)被復(fù)制到年老代,年老代的空間一般比年輕代大,能存放更多的對(duì)象,在年老代上發(fā)生的GC次數(shù)也比年輕代少。當(dāng)年老代內(nèi)存不足時(shí),將執(zhí)行Major GC,也叫 Full GC。

可以使用**-XX:+UseAdaptiveSizePolicy**開(kāi)關(guān)來(lái)控制是否采用動(dòng)態(tài)控制策略,如果動(dòng)態(tài)控制,則動(dòng)態(tài)調(diào)整Java堆中各個(gè)區(qū)域的大小以及進(jìn)入老年代的年齡。

如果對(duì)象比較大(比如長(zhǎng)字符串或大數(shù)組),年輕代空間不足,則大對(duì)象會(huì)直接分配到老年代上(大對(duì)象可能觸發(fā)提前GC,應(yīng)少用,更應(yīng)避免使用短命的大對(duì)象)。用-XX:PretenureSizeThreshold來(lái)控制直接升入老年代的對(duì)象大小,大于這個(gè)值的對(duì)象會(huì)直接分配在老年代上。

3.2.內(nèi)存分配–Eden區(qū)域

Java執(zhí)行的時(shí)候,默認(rèn)使用parallel收集器。對(duì)象優(yōu)先到Eden中:
案例:
創(chuàng)建Main類(lèi):

package com.toto.jvm.demo;public class Main {public static void main(String[] args) {byte[] b1 = new byte[4 * 1024 * 1024];}}

在Eclipse中配置VM arguments參數(shù)(-verbose:gc -XX:+PrintGCDetails):

Eden是新生代上的一部分區(qū)域,當(dāng)運(yùn)行上面的代碼的時(shí)候,GC日志輸出中可以看到優(yōu)先到Eden,輸出結(jié)果如下:

上面輸出ParOldGen,說(shuō)明使用的parallel收集器。

使用serialGC的時(shí)候,打印的gc日志(-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC):

運(yùn)行后輸出結(jié)果:

由于上面的b1分配的內(nèi)存是4 * 1024 * 1024 即4M
而上圖可以看到只有eden space 138816K,占比8%??梢缘贸鼋Y(jié)論:創(chuàng)建的對(duì)象優(yōu)先進(jìn)入eden區(qū)域。

當(dāng)把b1變成200M時(shí)(即大對(duì)象):

說(shuō)明:大對(duì)象直接分配到老年代。

再如案例:

package com.toto.jvm.demo;public class Main {public static void main(String[] args) {byte[] b1 = new byte[5 * 1024 * 1024];byte[] b2 = new byte[4 * 1024 * 1024];byte[] b3 = new byte[4 * 1024 * 1024];byte[] b4 = new byte[4 * 1024 * 1024];}}

修改VM參數(shù):

-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8

輸出結(jié)果:

-XX:SurvivorRatio=8表示Survivor是Eden的1/8。

3.3.內(nèi)存分配–大對(duì)象直接進(jìn)老年代

3.3.1.背景

講到大對(duì)象主要指字符串和數(shù)組,虛擬機(jī)提供了一個(gè)-XX:PretenureSizeThreshold參數(shù),大于這個(gè)值的參數(shù)直接在老年代分配。

這樣做的目的是避免在Eden區(qū)和兩個(gè)Survivor區(qū)之間發(fā)生大量的內(nèi)存復(fù)制(新生代采用復(fù)制算法)。

3.3.2.解析

有兩種情況,對(duì)象會(huì)直接分配到老年代:
?如果在新生代分配失敗且對(duì)象是一個(gè)不含任何對(duì)象引用的大數(shù)組,可被直接分配到老年代。通過(guò)在老年代的分配避免新生代的一次垃圾回收。
?XX:PretenureSizeThreshold=<字節(jié)大小>可以設(shè)分配到新生代分配內(nèi)存。任何比這個(gè)大的對(duì)象都不會(huì)嘗試在新生代分配,將在老年代分配內(nèi)存。
?PretenureSizeThreshold默認(rèn)值是0,意味著任何對(duì)象都會(huì)現(xiàn)在新生代分配內(nèi)存。

案例:
設(shè)置虛擬機(jī)參數(shù):

-verbose:gc -XX:+PrintGCDetails -Xms2048M -Xmx2048M -Xmn1024M -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC

-Xms表示初始化堆內(nèi)存
-Xmx表示最大堆內(nèi)存
-Xmn表示新生代的內(nèi)存
-XX:SurvivorRatio=8表示新生代的Eden占8/10,S1和S2各占1/10
因此Eden的內(nèi)存大小為:0.8 * 1024 * 1024 * 1024字節(jié) 約為819 * 1024 * 1024
上代碼:

package com.toto.jvm.demo2;import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean;public class Main {public static void main(String[] args) {//734003216byte[] array = new byte[700 * 1024 * 1024];for (MemoryPoolMXBean memoryPoolMXBean : ManagementFactory.getMemoryPoolMXBeans()) {System.out.println(memoryPoolMXBean.getName() + " 總量:" + memoryPoolMXBean.getUsage().getCommitted() + " 使用的內(nèi)存:" + memoryPoolMXBean.getUsage().getUsed());}}}

輸出結(jié)果:

當(dāng)把代碼改成:

package com.toto.jvm.demo2;import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean;public class Main {public static void main(String[] args) {//734003216byte[] array = new byte[900 * 1024 * 1024];for (MemoryPoolMXBean memoryPoolMXBean : ManagementFactory.getMemoryPoolMXBeans()) {System.out.println(memoryPoolMXBean.getName() + " 總量:" + memoryPoolMXBean.getUsage().getCommitted() + " 使用的內(nèi)存:" + memoryPoolMXBean.getUsage().getUsed());}}}

3.4.內(nèi)存分配–長(zhǎng)期存活的對(duì)象進(jìn)去老年代

用法: -XX:MaxTenuringThreshold=15
該參數(shù)主要是控制新生代需要經(jīng)歷多少次GC晉升到老年代中的最大閾值。在JVM中用4個(gè)bit存儲(chǔ)(放在對(duì)象頭中),(1111)所以其最大值是15。

但并非意味著,對(duì)象必須要經(jīng)歷15次YGC才會(huì)晉升到老年代中。例如,當(dāng)Survivor區(qū)空間不夠時(shí),便會(huì)提前進(jìn)入到老年代中,但這個(gè)次數(shù)一定不大于設(shè)置的最大閾值。

那么JVM到底是如何來(lái)計(jì)算S區(qū)對(duì)象晉升到Old區(qū)的呢?
首先介紹另一個(gè)重要的JVM參數(shù):
-XX:TargetSurvivorRatio:一個(gè)計(jì)算期望S區(qū)存活大小(Desired survivor size)的參數(shù)。默認(rèn)值為50,即50%。
當(dāng)一個(gè)S區(qū)中所有的age對(duì)象的大小如果大于等于Desired survivor size,則重新計(jì)算threshold,以age和MaxTenuringThreshold兩者的最小值為準(zhǔn)。

以一個(gè)Demo為例。設(shè)置VM參數(shù)值:

-Xmx200M -Xmn50m -XX:TargetSurvivorRatio=60 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:MaxTenuringThreshold=3 -XX:+PrintTenuringDistribution

代碼:

package com.toto.jvm.demo3;/*** -Xmx200M -Xmn50m -XX:TargetSurvivorRatio=60 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:MaxTenuringThreshold=3* 最小堆為50M,默認(rèn)SurvivorRatio為8,那么可以知道Eden區(qū)為40M,S0和S1為5M* * 可以在JVM啟動(dòng)參數(shù)中加上-XX:+PrintTenuringDistribution,該參數(shù)可以輸出age的額外信息。*/ public class App {public static void main(String[] args) throws InterruptedException {// main方法作為主線程,變量不會(huì)被回收byte[] byte1 = new byte[1 * 1024 * 1024];byte[] byte2 = new byte[1 * 1024 * 1024];YGC(40);Thread.sleep(3000);YGC(40);Thread.sleep(3000);YGC(40);Thread.sleep(3000);// 這次再ygc時(shí), 由于byte1和byte2的年齡經(jīng)過(guò)3次ygc后已經(jīng)達(dá)到3(-XX:MaxTenuringThreshold=3),// 所以會(huì)晉升到oldYGC(40);// ygc后, s0(from)/s1(to)的空間為0Thread.sleep(3000);// 達(dá)到TargetSurvivorRatio這個(gè)比例指定的值,即5M(S區(qū))*60%(TargetSurvivorRatio)=3M(Desired survivor size)byte[] byte4 = new byte[1 * 1024 * 1024];byte[] byte5 = new byte[1 * 1024 * 1024];byte[] byte6 = new byte[1 * 1024 * 1024];// 這次ygc時(shí), 由于s區(qū)已經(jīng)占用達(dá)到了60%(-XX:TargetSurvivorRatio=60),// 所以會(huì)重新計(jì)算對(duì)象晉升的min(age, MaxTenuringThreshold) = 1YGC(40);Thread.sleep(3000);// 由于前一次ygc時(shí)算出age=1, 所以這一次再ygc時(shí), byte4, byte5, byte6就要晉升到Old,// 而不需要等MaxTenuringThreshold這么多次, 此次ygc后, s0(from)/s1(to)的空間再次為0,// 對(duì)象全部晉升到oldYGC(40);Thread.sleep(3000);System.out.println("GC end!");}// 塞滿Eden區(qū),局部變量會(huì)被回收,作為觸發(fā)GC的小工具private static void YGC(int edenSize) {for (int i = 0; i < edenSize; i++) {byte[] byte1m = new byte[1 * 1024 * 1024];}}}

輸出結(jié)果:

2021-05-03T10:53:15.791+0800: [GC (Allocation Failure) 2021-05-03T10:53:15.791+0800: [ParNew Desired survivor size 3145728 bytes, new threshold 3 (max 3) - age 1: 2649352 bytes, 2649352 total : 40551K->2623K(46080K), 0.0022131 secs] 40551K->2623K(199680K), 0.0023103 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2021-05-03T10:53:18.797+0800: [GC (Allocation Failure) 2021-05-03T10:53:18.797+0800: [ParNew Desired survivor size 3145728 bytes, new threshold 3 (max 3) - age 1: 168 bytes, 168 total - age 2: 2647416 bytes, 2647584 total : 43362K->2824K(46080K), 0.0025757 secs] 43362K->2824K(199680K), 0.0026316 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2021-05-03T10:53:21.805+0800: [GC (Allocation Failure) 2021-05-03T10:53:21.805+0800: [ParNew Desired survivor size 3145728 bytes, new threshold 3 (max 3) - age 2: 168 bytes, 168 total - age 3: 2647416 bytes, 2647584 total : 43562K->2694K(46080K), 0.0009461 secs] 43562K->2694K(199680K), 0.0009973 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2021-05-03T10:53:24.808+0800: [GC (Allocation Failure) 2021-05-03T10:53:24.808+0800: [ParNew Desired survivor size 3145728 bytes, new threshold 3 (max 3) - age 3: 168 bytes, 168 total : 43432K->104K(46080K), 0.0048805 secs] 43432K->2740K(199680K), 0.0049507 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2021-05-03T10:53:27.820+0800: [GC (Allocation Failure) 2021-05-03T10:53:27.821+0800: [ParNew Desired survivor size 3145728 bytes, new threshold 1 (max 3) - age 1: 3145776 bytes, 3145776 total : 40842K->3072K(46080K), 0.0028666 secs] 43478K->5708K(199680K), 0.0030672 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2021-05-03T10:53:30.827+0800: [GC (Allocation Failure) 2021-05-03T10:53:30.827+0800: [ParNew Desired survivor size 3145728 bytes, new threshold 3 (max 3) : 43811K->0K(46080K), 0.0033850 secs] 46447K->5708K(199680K), 0.0034430 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] GC end! Heappar new generation total 46080K, used 13910K [0x00000000f3800000, 0x00000000f6a00000, 0x00000000f6a00000)eden space 40960K, 33% used [0x00000000f3800000, 0x00000000f45959a0, 0x00000000f6000000)from space 5120K, 0% used [0x00000000f6000000, 0x00000000f6000000, 0x00000000f6500000)to space 5120K, 0% used [0x00000000f6500000, 0x00000000f6500000, 0x00000000f6a00000)concurrent mark-sweep generation total 153600K, used 5708K [0x00000000f6a00000, 0x0000000100000000, 0x0000000100000000)Metaspace used 2595K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 288K, capacity 386K, committed 512K, reserved 1048576K

============================================================================

另外的一篇文章的說(shuō)明:
-XX:MaxTenuringThreshold設(shè)置的是年齡閾值,默認(rèn)15(對(duì)象被復(fù)制的次數(shù))
JVM為每個(gè)對(duì)象定義了一個(gè)對(duì)象年齡(Age)計(jì)數(shù)器, 對(duì)象在Eden出生如果經(jīng)第一次Minor GC后仍然存活, 且能被Survivor容納的話, 將被移動(dòng)到Survivor空間中, 并將年齡設(shè)為1. 以后對(duì)象在Survivor區(qū)中每熬過(guò)一次Minor GC年齡就+1. 當(dāng)增加到設(shè)置的閥值時(shí)將會(huì)晉升到老年代。

但有一個(gè)疑惑,為什么我設(shè)置-XX:MaxTenuringThreshold足夠大了防止大量對(duì)象進(jìn)入老年區(qū),雖然進(jìn)入老年區(qū)的對(duì)象減少了,但還是有?
因?yàn)槿绻赟urvivor空間中相同年齡所有對(duì)象大小的總和大于Survivor空間的一半, 年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)入老年代。

3.5.內(nèi)存分配–空間分配擔(dān)保

主要使用的JVM參數(shù)配置是:-XX:HandlePromotionFailure,使用空間分配擔(dān)保的時(shí)候使用-XX:+HandlePromotionFailure,不使用分配擔(dān)保的時(shí)候使用-XX:-HandlePromotionFailure。

3.5.1.堆空間參數(shù)

官網(wǎng)地址:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
?-XX:+PrintFlagsInitial : 查看所有的參數(shù)默認(rèn)初始值
?-XX:+PrintFlagsFinal: 查看所有的參數(shù)的最終值(可能會(huì)存在修改,不再是初始值)
?-Xms: 初始堆空間內(nèi)存(默認(rèn)為物理內(nèi)存的1/64)
?-Xmx: 最大堆空間內(nèi)存(默認(rèn)為物理內(nèi)存的1/4)
?-Xmn: 設(shè)置新生代的大小(初始值及最大值)。
?-XX:NewRatio: 配置新生代與老年代在堆結(jié)構(gòu)的占比。
?-XX:SurvivorRatio: 設(shè)置新生代中Eden和S0/S1空間的比例。
?-XX:MaxTenuringThreshold: 設(shè)置新生代垃圾的最大年齡。
?-XX:+PrintGCDetails: 輸出詳細(xì)的GC處理日志
?打印gc簡(jiǎn)要信息:(1) -XX:+PrintGC (2) -verbose:gc
?-XX:HandlePromotionFailure: 是否設(shè)置空間分配擔(dān)保

3.5.2.-XX:HandlePromotionFailure

JDK7及以后這個(gè)參數(shù)就失效了。
只要老年代的連續(xù)空間大于新生代對(duì)象的總大小或者歷次晉升到老年代的對(duì)象的平均大小就進(jìn)行MinorGC,否則FullGC

JDK7及以前這個(gè)參數(shù)的作用見(jiàn)下圖:

3.6.內(nèi)存分配–逃逸分析與棧上分配

3.6.1.逃逸分析

內(nèi)存逃逸主要是對(duì)象的動(dòng)態(tài)作用域的改變而引起的,故而內(nèi)存逃逸的分析就是分析對(duì)象的動(dòng)態(tài)作用域。
發(fā)生逃逸行為的情況分為兩種:方法逃逸和線程逃逸


逃逸是指在某個(gè)方法之內(nèi)創(chuàng)建的對(duì)象,除了在方法體之內(nèi)被引用之外,還在方法體之外被其它變量引用到;這樣帶來(lái)的后果是在該方法執(zhí)行完畢之后,該方法中創(chuàng)建的對(duì)象將無(wú)法將GC回收,由于其被其它變量引用。正常的方法調(diào)用中,方法體中創(chuàng)建的對(duì)象將在執(zhí)行完畢之后,將回收其中創(chuàng)建的對(duì)象;故由于無(wú)法回收,即成為逃逸。

如果對(duì)象發(fā)生逃逸,那會(huì)分配到堆中。(因?yàn)閷?duì)象發(fā)生了逃逸,就代表這個(gè)對(duì)象可以被外部訪問(wèn),換句話說(shuō),就是可以共享,能共享數(shù)據(jù)的,無(wú)非就是堆或方法區(qū),這就是堆。)

如果對(duì)象沒(méi)發(fā)生逃逸,那會(huì)分配到棧中。(因?yàn)閷?duì)象沒(méi)發(fā)生逃逸,那就代表這個(gè)對(duì)象不能外部訪問(wèn),換句話說(shuō),就是不可共享,這里就是棧。)

package com.toto.jvm.demo4;import jvm.test;public class Main {public static Object obj;public void globalVariableEscape() {// 給全局變量賦值,發(fā)生逃逸obj = new Object(); }public Object methodEscape() {// 方法返回值,發(fā)生逃逸return new Object(); }public void instanceEscape() {// 實(shí)例引用,發(fā)生逃逸 test(this);}public void getInstance() {//對(duì)象的作用域只在當(dāng)前方法中有效,沒(méi)有發(fā)生逃逸Object obj1 = new Object();}}

運(yùn)行java時(shí)傳遞jvm參數(shù)-XX:+DoEscapeAnalysis

棧上分配與逃逸分析的關(guān)系
進(jìn)行逃逸分析之后,產(chǎn)生的后果是所有的對(duì)象都將由棧上分配,而非從JVM內(nèi)存模型中的堆來(lái)分配。

棧上分配可以提升代碼性能,降低在多線程情況下的鎖使用,但是會(huì)受限于其空間的大小。

分析找到未逃逸的變量,將變量類(lèi)的實(shí)例化內(nèi)存直接在棧里分配(無(wú)需進(jìn)入堆),分配完成后,繼續(xù)在調(diào)用棧內(nèi)執(zhí)行,最后線程結(jié)束,棧空間被回收,局部變量對(duì)象也被回收。

能在方法內(nèi)創(chuàng)建對(duì)象,就不要再方法外創(chuàng)建對(duì)象。


1.什么是棧上分配?
棧上分配主要是指在java程序的執(zhí)行過(guò)程中,在方法體中聲明的變量以及創(chuàng)建的對(duì)象,將直接從該線程所使用的棧中分配空間。一般而言,創(chuàng)建對(duì)象都是從堆中來(lái)分配的,這里是指在棧上來(lái)分配空間給新創(chuàng)建的對(duì)象。

2.什么是逃逸?
逃逸是指在某個(gè)方法之內(nèi)創(chuàng)建的對(duì)象,除了在方法體之內(nèi)被引用之外,還在方法體之外被其它變量引用到;這樣帶來(lái)的后果是在該方法執(zhí)行完畢之后,該方法中創(chuàng)建的對(duì)象將無(wú)法被GC回收,由于其被其它變量引用。正常的方法調(diào)用中,方法體中創(chuàng)建的對(duì)象將在執(zhí)行完畢之后,將回收其中創(chuàng)建的對(duì)象;故由于無(wú)法回收,即成為逃逸。

3.6.1.1.方法逃逸

當(dāng)方法創(chuàng)建了一個(gè)對(duì)象之后,這個(gè)對(duì)象被外部方法所調(diào)用,這個(gè)時(shí)候方法運(yùn)行結(jié)束要進(jìn)行GC時(shí),本該方法的對(duì)象被回收,卻發(fā)現(xiàn)該對(duì)象還存活著,沒(méi)法回收,則稱(chēng)為"方法逃逸"
簡(jiǎn)單來(lái)說(shuō):就是當(dāng)前方法創(chuàng)建的對(duì)象,本該是當(dāng)前方法的棧幀所管理,卻被調(diào)用方所使用,可以稱(chēng)之為內(nèi)存逃逸。

3.6.1.2.線程分配

直接將對(duì)象進(jìn)行返回出去,該對(duì)象很可能被外部線程所訪問(wèn),如:賦值給變量等,則稱(chēng)為”線程逃逸”。

當(dāng)我們創(chuàng)建一個(gè)對(duì)象的時(shí)候,會(huì)立馬想到該對(duì)象是會(huì)存儲(chǔ)到堆空間中的,而垃圾回收機(jī)制會(huì)在堆空間中回收不再使用的對(duì)象,但是篩選可回收對(duì)象,還有整理對(duì)象都需要消耗時(shí)間,如果能夠通過(guò)逃逸分析確定某些對(duì)象不會(huì)逃出到方法外的話,那么就可以直接讓這個(gè)對(duì)象在??臻g分配內(nèi)存,這樣該對(duì)象會(huì)隨著方法的執(zhí)行完畢自動(dòng)進(jìn)行銷(xiāo)毀。

3.6.2.棧上分配

棧上分配主要是指在Java程序的執(zhí)行過(guò)程中,在方法體中聲明的變量以及創(chuàng)建的對(duì)象,將直接從該線程所使用的棧中分配空間。一般而言,創(chuàng)建對(duì)象都是從堆中來(lái)分配的,這里是指在棧上分配空間給新建的對(duì)象。

如果能夠證明一個(gè)對(duì)象,不會(huì)進(jìn)行逃逸到方法或線程外的話,則可以對(duì)該變量進(jìn)行優(yōu)化。

3.6.3.逃逸分析/棧上分配的優(yōu)勢(shì)分析

優(yōu)勢(shì)表現(xiàn)在以下兩個(gè)方面:
?消除同步:線程同步的代價(jià)是相當(dāng)高的,同步的后果是降低并發(fā)性和性能。逃逸分析可以判斷出某個(gè)對(duì)象是否始終只被一個(gè)線程訪問(wèn),如果只被一個(gè)線程訪問(wèn),那么對(duì)該對(duì)象的同步操作就可以轉(zhuǎn)化成沒(méi)有同步保護(hù)的操作,這樣就能大大提高并發(fā)程度和性能。
?矢量替代:逃逸分析方法如果發(fā)現(xiàn)對(duì)象的內(nèi)存存儲(chǔ)結(jié)構(gòu)不需要連續(xù)進(jìn)行的話,就可以將對(duì)象的部分甚至全部保存在CPU寄存器內(nèi),這樣能大大提高訪問(wèn)速度。
劣勢(shì):
?棧上分配受限于棧的空間大小,一般自我迭代類(lèi)的需求以及大的對(duì)象空間需求操作,將導(dǎo)致棧的內(nèi)存溢出;故只適用于一定范圍之內(nèi)的內(nèi)存范圍請(qǐng)求。

3.6.3.1.同步消除

線程同步本身比較耗時(shí),若確定了一個(gè)變量不會(huì)逃逸出線程,無(wú)法被其他線程訪問(wèn)到,那這個(gè)變量的讀寫(xiě)就不會(huì)存在競(jìng)爭(zhēng),則可以消除對(duì)該對(duì)象的同步鎖。

3.6.4.標(biāo)量替換

1、標(biāo)量是指不可分割的量,如java中基本數(shù)據(jù)類(lèi)型和引用類(lèi)型,都不能夠再進(jìn)一步分解,他們就可以成為稱(chēng)為標(biāo)量。
2、若一個(gè)數(shù)據(jù)可以繼續(xù)分解,那就稱(chēng)之為聚合量,而對(duì)象就是典型的聚合量。
3、若逃逸分析證明一個(gè)對(duì)象不會(huì)逃逸出方法,不會(huì)被外部訪問(wèn),并且這個(gè)對(duì)象是可以被分解的,那程序在真正執(zhí)行的時(shí)候可能不創(chuàng)建這個(gè)對(duì)象,而是直接創(chuàng)建這個(gè)對(duì)象分解后的標(biāo)量來(lái)代替。這樣就無(wú)需在對(duì)對(duì)象分配空間了,只在棧上為分解出的變量分配內(nèi)存即可。

注意:
逃逸分析是比較耗時(shí)的,所以性能未必提升很多,因?yàn)槠浜臅r(shí)性,采用的算法都是不那么準(zhǔn)確但是時(shí)間壓力相對(duì)較小的算法來(lái)完成的,這就可能導(dǎo)致效果不穩(wěn)定,要慎重。
由于HotSpot虛擬機(jī)目前的實(shí)現(xiàn)方法導(dǎo)致棧上分配實(shí)現(xiàn)起來(lái)比較復(fù)雜,所以HotSpot虛擬機(jī)中暫時(shí)還沒(méi)有這項(xiàng)優(yōu)化。

相關(guān)JVM參數(shù):
-XX:+DoEscapeAnalysis 開(kāi)啟逃逸分析、
-XX:+PrintEscapeAnalysis 開(kāi)啟逃逸分析后,可通過(guò)此參數(shù)查看分析結(jié)果。
-XX:+EliminateAllocations 開(kāi)啟標(biāo)量替換。
-XX:+EliminateLocks 開(kāi)啟同步消除。
-XX:+PrintEliminateAllocations 開(kāi)啟標(biāo)量替換后,查看標(biāo)量替換情況。

3.6.5.什么情況下會(huì)發(fā)生逃逸?

案例:

package com.toto.jvm.demo4;public class StackAllocation {public StackAllocation obj;/*** 方法返回StackAllocation對(duì)象,發(fā)生逃逸* @return*/public StackAllocation getInstance() {return obj == null ? new StackAllocation() : obj;}/*** 為成員屬性賦值,發(fā)生逃逸*/public void setObj() {this.obj = new StackAllocation();}/*** 對(duì)象的作用域僅在當(dāng)前方法中有效,沒(méi)有發(fā)生逃逸*/public void useStackAllocation() {StackAllocation s = new StackAllocation();}/*** 引用成員變量的值,發(fā)生逃逸*/public void useStackAllocation2() {StackAllocation s = getInstance();}}

3.7.直接內(nèi)存

查看一下什么是直接內(nèi)存。
NIO中直接分配直接內(nèi)存。

3.8.Java內(nèi)存區(qū)域-直接內(nèi)存和運(yùn)行時(shí)常量池

3.8.1.運(yùn)行時(shí)常量池簡(jiǎn)介

運(yùn)行時(shí)常量池(Runtime Constant Pool),它是方法區(qū)的一部分。Class文件中除了有類(lèi)的版本、字段、方法、接口等描述等信息外,還有一項(xiàng)信息是常量池(Constant Pool Table),用于存放編譯期生成的各種字面量和符號(hào)引用,這部分內(nèi)容將在類(lèi)加載后存放到常量池中

運(yùn)行時(shí)常量是相對(duì)于常量來(lái)說(shuō)的,它具備一個(gè)重要特征是:動(dòng)態(tài)性。當(dāng)然,值相同的動(dòng)態(tài)常量與我們通常說(shuō)的常量只是來(lái)源不同,但是都是儲(chǔ)存在池內(nèi)同一塊內(nèi)存區(qū)域。Java語(yǔ)言并不要求常量一定只能在編譯期產(chǎn)生,運(yùn)行期間也可能產(chǎn)生新的常量,這些常量被放在運(yùn)行時(shí)常量池中。這里所說(shuō)的常量包括:基本類(lèi)型包裝類(lèi)(包裝類(lèi)不管理浮點(diǎn)型,整型只會(huì)管理-128到127)和String(也可以通過(guò)**String.intern()**方法可以強(qiáng)制將String放入常量池)

3.8.2.Class文件中的信息常量池

在Class文件結(jié)構(gòu)中,最頭的4個(gè)字節(jié)用于存儲(chǔ)Megic Number,用于確定一個(gè)文件是否能被JVM接受,再接著4個(gè)字節(jié)用于存儲(chǔ)版本號(hào),前2個(gè)字節(jié)存儲(chǔ)次版本號(hào),后2個(gè)存儲(chǔ)主版本號(hào),再接著是用于存放常量的常量池,由于常量的數(shù)量是不固定的,所以常量池的入口放置一個(gè)U2類(lèi)型的數(shù)據(jù)(constant_pool_count)存儲(chǔ)常量池容量計(jì)數(shù)值。

常量池主要用于存放兩大類(lèi)常量:字面量(Literal)和符號(hào)引用量(Symbolic References),字面量相當(dāng)于Java語(yǔ)言層面常量的概念,如文本字符串,聲明為final的常量值等,符號(hào)引用則屬于編譯原理方面的概念,包括了如下三種類(lèi)型的常量:

?類(lèi)和接口的全限定名
?字段名稱(chēng)和描述符
?方法名稱(chēng)和描述符

3.8.3.常量池的好處

常量池是為了避免頻繁的創(chuàng)建和銷(xiāo)毀對(duì)象而影響系統(tǒng)性能,其實(shí)現(xiàn)了對(duì)象的共享。例如字符串常量池,在編譯階段就把所有的字符串文字放到一個(gè)常量池中。
?節(jié)省內(nèi)存空間:常量池中所有相同的字符串常量被合并,只占用一個(gè)空間。
?節(jié)省運(yùn)行時(shí)間:比較字符串時(shí),比equals()快。對(duì)于兩個(gè)引用變量,只用判斷引用是否相等,也就判斷實(shí)際值是否相等。

雙等號(hào)==的含義
?基本數(shù)據(jù)類(lèi)型之間應(yīng)用雙等號(hào),比較的是他們的數(shù)值。
?復(fù)合數(shù)據(jù)類(lèi)型(類(lèi))之間應(yīng)用雙等號(hào),比較的是他們?cè)趦?nèi)存中的存放地址。

3.8.4.基本類(lèi)型的包裝類(lèi)和常量池

java中基本類(lèi)型的包裝類(lèi)的大部分都實(shí)現(xiàn)了常量池技術(shù),即Byte,Short,Integer,Long,Character,Boolean。這5種包裝類(lèi)默認(rèn)創(chuàng)建了數(shù)值[-128, 127]的相應(yīng)類(lèi)型的緩存數(shù)據(jù),但是超出此范圍仍然會(huì)去創(chuàng)建新的對(duì)象。兩種浮點(diǎn)數(shù)類(lèi)型的包裝類(lèi)Float,Double并沒(méi)有實(shí)現(xiàn)常量池技術(shù)。

1)Integer與常量池

Integer i1 = 40; Integer i2 = 40; Integer i3 = 0; Integer i4 = new Integer(40); Integer i5 = new Integer(40); Integer i6 = new Integer(0);System.out.println("i1=i2 " + (i1 == i2)); System.out.println("i1=i2+i3 " + (i1 == i2 + i3)); System.out.println("i1=i4 " + (i1 == i4)); System.out.println("i4=i5 " + (i4 == i5)); System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); System.out.println("40=i5+i6 " + (40 == i5 + i6));i1=i2 true i1=i2+i3 true i1=i4 false i4=i5 false i4=i5+i6 true 40=i5+i6 true

解釋:
?Integer i1 = 40; java在編譯的時(shí)候會(huì)直接將代碼封裝成Integer i1 = Integer.valueOf(40); 從而使用常量池中的對(duì)象。
?Integer i4 = new Integer(40); 這種情況下會(huì)創(chuàng)建新的對(duì)象。
?語(yǔ)句i4 == i5 + i6,因此+這個(gè)操作符不適用于Integer對(duì)象,首先i5和i6進(jìn)行自動(dòng)拆箱操作,進(jìn)行數(shù)值相加,即i4 == 40。然后Integer對(duì)象無(wú)法與數(shù)值進(jìn)行直接比較,所以i4自動(dòng)拆箱轉(zhuǎn)為int值40,最終這條語(yǔ)句轉(zhuǎn)為40 == 40進(jìn)行數(shù)值比較。

2)String與常量池-普通方法賦值

String str1 = "abcd"; String str2 = new String("abcd"); System.out.println(str1==str2);//falseString str1 = "str"; String str2 = "ing"; String str3 = "str" + "ing"; String str4 = str1 + str2; System.out.println("string" == "str" + "ing");// true System.out.println(str3 == str4);//falseString str5 = "string"; System.out.println(str3 == str5);//true

解釋:
?“abcd”是在常量池中拿對(duì)象,new String(“abcd”)是直接在堆內(nèi)存空間創(chuàng)建一個(gè)新的對(duì)象。只要使用new方法,便需要?jiǎng)?chuàng)建的對(duì)象
?連接表達(dá)式+,只有使用引號(hào)包含文本的方式創(chuàng)建的String對(duì)象之間使用”+”連接產(chǎn)生的新對(duì)象才會(huì)被加入常量池中。
?對(duì)于字符串變量的”+”連接表達(dá)式,它所產(chǎn)生的新對(duì)象都不會(huì)被加入字符串池中,其屬于在運(yùn)行時(shí)創(chuàng)建的字符串,具有獨(dú)立的內(nèi)存地址,所以不引用自同—String對(duì)象。

3)String與常量池-靜態(tài)方法賦值

package com.toto.jvm.demo5;public class Main {/** 常量A **/public static final String A;/** 常量B **/public static final String B;static {A = "ab";B = "cd";}public static void main(String[] args) {// 將兩個(gè)常量用 + 連接對(duì)s進(jìn)行初始化String s = A + B;String t = "abcd";if (s == t) {System.out.println("s等于t,它們是同一個(gè)對(duì)象");} else {System.out.println("s不等于t,它們不是同一個(gè)對(duì)象");}}} 輸出結(jié)果: s不等于t,它們不是同一個(gè)對(duì)象

解釋:
s不等于t,它們不是同一個(gè)對(duì)象。A和B雖然被定義為常量,但是它們都沒(méi)有馬上被賦值。在運(yùn)算出s的值之前,他們何時(shí)被賦值,以及被賦予什么樣的值,都是個(gè)變量。因此A和B在賦值之前,性質(zhì)類(lèi)似于一個(gè)變量。那么s就不能在編譯期被確定,而只能運(yùn)行時(shí)被創(chuàng)建了。

4)String與常量池 - intern方法

package com.toto.jvm.demo6;public class Main {public static void main(String[] args) {String s1 = new String("計(jì)算機(jī)");String s2 = s1.intern();String s3 = "計(jì)算機(jī)";System.out.println("s1 == s2 ? " + (s1 == s2));System.out.println("s3 == s2 ? " + (s3 == s2));/*** 結(jié)果是:* s1 == s2 ? false* s3 == s2 ? true**/}}

解釋:
String的intern()方法會(huì)查找在常量池中是否存在一份equal相等的字符串,如果有則返回該字符串的引用,如果沒(méi)有則添加自己的字符串進(jìn)入常量。

5)String與常量池 - 延伸

String s1 = new String(“xyz”); //創(chuàng)建了幾個(gè)對(duì)象?

解釋:
考慮類(lèi)加載階段和實(shí)際執(zhí)行時(shí)。
?類(lèi)加載對(duì)一個(gè)類(lèi)只會(huì)進(jìn)行一次。”xyz”在類(lèi)加載時(shí)就已經(jīng)創(chuàng)建并駐留了(如果該類(lèi)被加載之前已經(jīng)有”xyz”字符串被駐留過(guò)則不需要重新創(chuàng)建用于駐留的”xyz”實(shí)例)。駐留的字符串是放在全局共享的字符串常量池中的。
?在這段代碼后連續(xù)被運(yùn)行的時(shí)候,”xyz”字面量對(duì)應(yīng)的String實(shí)例已經(jīng)固定了,不會(huì)再被重新創(chuàng)建。所以這段代碼將常量池中的對(duì)象復(fù)制一份放在到heap中,并且把heap中的這個(gè)對(duì)象的引用交給s1持有。

這條語(yǔ)句創(chuàng)建了2個(gè)對(duì)象。

intern()會(huì)把值搬到運(yùn)行時(shí)常量池中。它是一個(gè)native方法。
如果無(wú)法申請(qǐng)內(nèi)存,報(bào):OutOfMemoryError

3.9.對(duì)象在內(nèi)存中的布局-對(duì)象的創(chuàng)建

對(duì)象創(chuàng)建 步驟
1、new類(lèi)名
2、根據(jù)new的參數(shù)在常量池中定位一個(gè)類(lèi)的符號(hào)引用。
3、如果沒(méi)有找到這個(gè)符號(hào)引用,說(shuō)明類(lèi)還沒(méi)加被加載,則進(jìn)行類(lèi)的加載、解析和初始化。
4、虛擬機(jī)為對(duì)象分配內(nèi)存(位于堆中)
5、將分配的內(nèi)存初始化為零值(不包括對(duì)象頭)
6、調(diào)用對(duì)象的方法。

3.10.探究對(duì)象的結(jié)構(gòu)


3.11.深度理解對(duì)象的訪問(wèn)定位

已經(jīng)創(chuàng)建對(duì)象,如何找到對(duì)象呢?就涉及到訪問(wèn)定位的問(wèn)題

有兩種方式(百度一下):
1、使用句柄
2、直接指針

使用句柄池的用途

Hotsport使用直接尋址(直接指針)的方式定位

  • 到對(duì)象實(shí)例數(shù)據(jù)的指針
  • 到對(duì)象類(lèi)型數(shù)據(jù)的指針

3.12.Java對(duì)象訪問(wèn)方式

一般來(lái)說(shuō),一個(gè)Java的引用訪問(wèn)涉及到3個(gè)內(nèi)存區(qū)域:JVM棧,堆,方法區(qū)。以最簡(jiǎn)單的本地變量引用:Object objRef = new Object()為例:
?Object objRef表示一個(gè)本地引用,存儲(chǔ)在JVM棧的本地變量表中,表示一個(gè)reference類(lèi)型數(shù)據(jù);
?new Object()作為實(shí)例對(duì)象數(shù)據(jù)存儲(chǔ)在堆中;
?堆中還記錄了能夠查詢(xún)到此Object對(duì)象的類(lèi)型數(shù)據(jù)(接口、方法、field、對(duì)象類(lèi)型等)的地址,實(shí)際的數(shù)據(jù)則存儲(chǔ)在方法區(qū)中;

在Java虛擬機(jī)規(guī)范中,只規(guī)定了指向?qū)ο蟮囊?#xff0c;對(duì)于通過(guò)reference類(lèi)型引用訪問(wèn)具體對(duì)象的方式并未做規(guī)定,不過(guò)目前主流的實(shí)現(xiàn)方式主要有兩種:

3.12.1.通過(guò)句柄訪問(wèn)

通過(guò)句柄訪問(wèn)的實(shí)現(xiàn)方式中,JVM堆中會(huì)劃分單獨(dú)一塊內(nèi)存區(qū)域作為句柄池,句柄池中存儲(chǔ)了對(duì)象實(shí)例數(shù)據(jù)(在堆中)和對(duì)象類(lèi)型數(shù)據(jù)(在方法區(qū)中)的指針。這種實(shí)現(xiàn)方法由于用句柄表示地址,因此十分穩(wěn)定。

3.12.2.通過(guò)直接指針訪問(wèn)

通過(guò)直接指針訪問(wèn)的方式中,reference中存儲(chǔ)的就是對(duì)象在堆中的實(shí)際地址,在堆中存儲(chǔ)的對(duì)象信息中包含了在方法區(qū)中的相應(yīng)類(lèi)型數(shù)據(jù)。這種方法最大的優(yōu)勢(shì)是速度快,在HotSpot虛擬機(jī)中用的就是這種方式。

3.13.對(duì)象分配內(nèi)存的策略

虛擬機(jī)遇到一條new指令時(shí),首先將去檢查這個(gè)指令的參數(shù)是否能在常量池中定位到一個(gè)類(lèi)的符號(hào)引用,并且檢查這個(gè)符號(hào)引用代表的類(lèi)是否已被加載、解析和初始化過(guò)。如果沒(méi)有,那必須先執(zhí)行相應(yīng)的類(lèi)加載過(guò)程。

在類(lèi)加載檢查通過(guò)后,接下來(lái)虛擬機(jī)將為新生對(duì)象分配內(nèi)存。對(duì)象所需內(nèi)存的大小在類(lèi)加載完成后便可完全確定,為對(duì)象分配空間的任務(wù)等同于把一塊確定大小的內(nèi)存從Java堆中劃分出來(lái)。假設(shè)Java堆中內(nèi)存是絕對(duì)規(guī)整的,所有用過(guò)的內(nèi)存都放在一邊,空閑的內(nèi)存放在另一邊,中間放著一個(gè)指針作為分界點(diǎn)的指示器,那所分配內(nèi)存就僅僅是把那個(gè)指針向空閑空間那邊挪動(dòng)一段與對(duì)象大小相等的距離,這種分配方式稱(chēng)為“指針碰撞”(Bump thePointer)。如果Java堆中的內(nèi)存并不是規(guī)整的,已使用的內(nèi)存和空閑的內(nèi)存相互交錯(cuò),那就沒(méi)有辦法簡(jiǎn)單地進(jìn)行指針碰撞了,虛擬機(jī)就必須維護(hù)一個(gè)列表,記錄上哪些內(nèi)存塊是可用的,在分配的時(shí)候從列表中找到一塊足夠大的空間劃分給對(duì)象實(shí)例,并更新列表上的記錄,這種分配方式稱(chēng)為“空閑列表”(FreeList)。選擇哪種分配方式由Java堆是否規(guī)整決定,而Java堆是否規(guī)整又由所采用的垃圾收集器是否帶有壓縮整理功能決定。因此,在使用Serial、ParNew等帶Compact過(guò)程的收集器時(shí),系統(tǒng)采用的分配算法是指針碰撞,而使用CMS這種基于Mark-Sweep算法的收集器時(shí),通常采用空閑列表。

給對(duì)象分配的方式:
方式一:指針碰撞
方式二:空間列表

下面兩張圖可以解釋指針碰撞和空閑列表:
指針碰撞:

空間列表:

3.13.1.線程安全問(wèn)題

1、實(shí)現(xiàn)線程同步,加鎖(但:執(zhí)行效率低)
2、本地線程分配緩沖TLAB: (每個(gè)線程分配一定一定的內(nèi)存)

3.13.1.1.本地線程分配緩沖----TLAB

TLAB是虛擬機(jī)在堆內(nèi)存的劃分出來(lái)的一塊專(zhuān)用空間,是線程專(zhuān)屬的。在TLAB啟動(dòng)的情況下,在線程初始化時(shí),虛擬機(jī)會(huì)為每個(gè)線程分配一塊TLAB空間,只給當(dāng)前線程使用,這樣每個(gè)線程都單獨(dú)擁有一個(gè)空間,如果需要分配內(nèi)存,就在自己的空間上分配,這樣就不存在競(jìng)爭(zhēng)的情況,可以大大提升分配效率。

ps:這里說(shuō)線程獨(dú)享的堆內(nèi)存,只是在“內(nèi)存分配”這個(gè)動(dòng)作上是線程獨(dú)享的,至于在讀取、垃圾回收等動(dòng)作上都是線程共享的。即是指其他線程可以在這個(gè)區(qū)域讀取、操作數(shù)據(jù),但是無(wú)法在這個(gè)區(qū)域中分配內(nèi)存。

3.13.1.2.TLAB生命周期

在分代收集的垃圾回收器中,TLAB是在eden區(qū)分配的。TLAB 是從堆上 Eden 區(qū)的分配的一塊線程本地私有內(nèi)存。線程初始化的時(shí)候,如果JVM 啟用了TLAB(默認(rèn)是啟用的, 可以通過(guò) -XX:-UseTLAB 關(guān)閉),則會(huì)創(chuàng)建并初始化TLAB。同時(shí),在GC 掃描對(duì)象發(fā)生之后,線程第一次嘗試分配對(duì)象的時(shí)候,也會(huì)創(chuàng)建并初始化TLAB

在TLAB已經(jīng)滿了或者接近于滿了的時(shí)候,TLAB可能會(huì)被釋放回Eden。GC掃描對(duì)象發(fā)生時(shí),TLAB會(huì)被釋放回Eden。TLAB 的生命周期期望只存在于一個(gè)GC 掃描周期內(nèi)。在JVM中,一個(gè) GC 掃描周期,就是一個(gè)epoch。那么,可以知道,TLAB 內(nèi)分配內(nèi)存一定是線性分配的。

3.13.1.3.TLAB的大小

TLAB的初始大小可由參數(shù)-XX:TLABSize指定,若指定了TLAB的值,TLAB初始大小就是TLABSize。否則,TLAB大小為分配線程的平均值。
源碼地址:https://github.com/openjdk/jdk/blob/master/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp

TLAB 的大小的最小值:通過(guò)MinTLABSize指定
TLAB 的大小的最大值:不同GC中有不同的最大值。例如G1 GC中,TLAB的最大值為大對(duì)象的大小,即是Region的一半;ZGC中的最大值為1/8的Region,在大部分情況下Shenandoah GC也是每個(gè)Region 大小的 8 分之一。對(duì)于其他的GC,則是int 數(shù)組的最大大小。

TLAB空間大小的動(dòng)態(tài)調(diào)整:
默認(rèn)情況下:

-XX:ResizeTLAB

resize開(kāi)關(guān)是默認(rèn)開(kāi)啟的,JVM可以對(duì)TLAB空間大小進(jìn)行調(diào)整。

對(duì)象的慢分配
當(dāng)TLAB內(nèi)存充足時(shí),分配新對(duì)象的方式稱(chēng)為快分配。當(dāng)TLAB內(nèi)存不足,分配新對(duì)象的方式稱(chēng)為“慢分配”。慢分配有兩種處理方式:
1、當(dāng)TLAB剩余內(nèi)存空間小于TLAB最大浪費(fèi)空間時(shí),丟棄當(dāng)前 TLAB 回歸 Eden,線程獲取新的 TLAB 分配對(duì)象。
2、當(dāng)TLAB剩余內(nèi)存空間大于TLAB最大浪費(fèi)空間時(shí),對(duì)象直接在Eden區(qū)分配內(nèi)存。

TLAB最大浪費(fèi)空間
最大浪費(fèi)空間是一個(gè)動(dòng)態(tài)值,TLAB最大浪費(fèi)空間初始值=TLAB大小/TLABRefillWasteFraction。TLABRefillWasteFraction默認(rèn)為64,所以TLAB最大浪費(fèi)空間初始值為T(mén)LAB大小的1/64。伴隨著每次慢分配,這個(gè)TLAB最大浪費(fèi)空間會(huì)每次遞增 TLABWasteIncrement 大小的空間。

3.13.1.4.總結(jié)

TLAB流程總結(jié):



3.13.1.5.參數(shù)總結(jié)

參數(shù)名稱(chēng)參數(shù)作用
UseTLAB是否啟用 TLAB,默認(rèn)是啟用的。
ResizeTLABTLAB 是否是自適應(yīng)可變的,默認(rèn)為是
TLABSize初始 TLAB 大小,單位是字節(jié) 。默認(rèn)為0,0 就是不主動(dòng)設(shè)置 TLAB 初始大小,而是通過(guò) JVM 自己計(jì)算每一個(gè)線程的初始大小。例如:-XX:TLABSize=65536
MinTLABSize最小 TLAB 大小。單位是字節(jié),默認(rèn)2048。例如-XX:MinTLABSize=4096
TLABRefillWasteFraction在一次 TLAB 再填充(refill)發(fā)生的時(shí)候,最大的 TLAB 浪費(fèi)。默認(rèn)為64,和TLAB最大浪費(fèi)空間有關(guān)。TLAB最大浪費(fèi)空間= TLAB大小/TLABRefillWasteFraction
TLABWasteIncrementTLAB 慢分配時(shí)允許的 TLAB 浪費(fèi)增量.

參考:
https://blog.csdn.net/a1076067274/article/details/112969208

3.14.垃圾回收-判斷對(duì)象是否存活算法-引用計(jì)數(shù)法詳解

在對(duì)象中添加一個(gè)引用計(jì)數(shù)器,當(dāng)有地方引用這個(gè)對(duì)象的時(shí)候,引用計(jì)數(shù)器的值就+1,當(dāng)引用失效的時(shí)候,計(jì)數(shù)就減一

Java中一般不用:引用計(jì)數(shù)方法。

如何判斷垃圾如何回收。

查看gc信息的方式

案例:

創(chuàng)建循環(huán)引用方式:

斷掉右側(cè)的先:




Jdk8采用的并不是引用計(jì)數(shù)法,而是默認(rèn)是:parallel垃圾回收即。

3.15.垃圾回收-判斷對(duì)象是否存活算法-可達(dá)性分析法詳解

3.15.1.可達(dá)性分析算法

在Java中,是通過(guò)可達(dá)性分析(Reachability Analysis)來(lái)判定對(duì)象是否存活的。該算法的基本思路就是通過(guò)一些被稱(chēng)為引用鏈(GC Roots)的對(duì)象作為起點(diǎn),從這些節(jié)點(diǎn)開(kāi)始向下搜索,搜索走過(guò)的路徑被稱(chēng)為(Reference Chain),當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連時(shí)(即從GC Roots節(jié)點(diǎn)到該節(jié)點(diǎn)不可達(dá)),則證明該對(duì)象是不可用的。

如上圖所示,object1~object4對(duì)GC Root都是可達(dá)的,說(shuō)明不可被回收,object5和object6對(duì)GC Root節(jié)點(diǎn)不可達(dá),說(shuō)明其可以被回收。
在Java中,可作為GC Root的對(duì)象包括以下幾種:
?虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象
?方法區(qū)中類(lèi)靜態(tài)屬性所引用的對(duì)象
?方法區(qū)中常量所引用的對(duì)象
?本地方法棧中JNI(即一般說(shuō)的Native方法)引用的對(duì)象

在堆里存放著幾乎多有的java對(duì)象實(shí)例,垃圾搜集器在對(duì)堆進(jìn)行回收之前,第一件事情就是確定這些對(duì)象之中哪些還“存活”著(即通過(guò)任何途徑都無(wú)法使用的對(duì)象)。

3.15.2.finalize()方法最終判定對(duì)象是否存活

即使在可達(dá)性分析算法中不可達(dá)的對(duì)象,也并非是“非死不可”的,這時(shí)候它們暫時(shí)處于“緩刑”階段,要真正宣告一個(gè)對(duì)象死亡,至少要經(jīng)歷再次標(biāo)記過(guò)程。
標(biāo)記的前提是對(duì)象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒(méi)有與GC Roots相連接的引用鏈。
1. 第一次標(biāo)記并進(jìn)行一次篩選。
篩選的條件是此對(duì)象是否有必要執(zhí)行finalize()方法。
當(dāng)對(duì)象沒(méi)有覆蓋finalize方法,或者finzlize方法已經(jīng)被虛擬機(jī)調(diào)用過(guò),虛擬機(jī)將這兩種情況都視為“沒(méi)有必要執(zhí)行”,對(duì)象被回收。
2. 第二次標(biāo)記
如果這個(gè)對(duì)象被判定為有必要執(zhí)行finalize()方法,那么這個(gè)對(duì)象將會(huì)被放置在一個(gè)名為:F-Queue的隊(duì)列之中,并在稍后由一條虛擬機(jī)自動(dòng)建立的、低優(yōu)先級(jí)的Finalizer線程去執(zhí)行。這里所謂的“執(zhí)行”是指虛擬機(jī)會(huì)觸發(fā)這個(gè)方法,但并不承諾會(huì)等待它運(yùn)行結(jié)束。這樣做的原因是,如果一個(gè)對(duì)象finalize()方法中執(zhí)行緩慢,或者發(fā)生死循環(huán)(更極端的情況),將很可能會(huì)導(dǎo)致F-Queue隊(duì)列中的其他對(duì)象永久處于等待狀態(tài),甚至導(dǎo)致整個(gè)內(nèi)存回收系統(tǒng)崩潰。
Finalize()方法是對(duì)象脫逃死亡命運(yùn)的最后一次機(jī)會(huì),稍后GC將對(duì)F-Queue中的對(duì)象進(jìn)行第二次小規(guī)模標(biāo)記,如果對(duì)象要在finalize()中成功拯救自己----只要重新與引用鏈上的任何的一個(gè)對(duì)象建立關(guān)聯(lián)即可,譬如把自己賦值給某個(gè)類(lèi)變量或?qū)ο蟮某蓡T變量,那在第二次標(biāo)記時(shí)它將移除出“即將回收”的集合。如果對(duì)象這時(shí)候還沒(méi)逃脫,那基本上它就真的被回收了。

3.15.3.Java引用

從可達(dá)性算法中可以看出,判斷對(duì)象是否可達(dá)時(shí),與“引用”有關(guān)。那么什么情況下可以說(shuō)一個(gè)對(duì)象被引用,引用到底代表什么?
在JDK1.2之后,Java對(duì)引用的概念進(jìn)行了擴(kuò)充,可以將引用分為以下四類(lèi):

強(qiáng)引用(Strong Reference)
軟引用(Soft Reference)
弱引用(Weak Reference)
虛引用(Phantom Reference)

這四種引用從上到下,依次減弱

3.15.3.1.強(qiáng)引用

強(qiáng)引用就是指在程序代碼中普遍存在的,類(lèi)似Object obj = new Object()這類(lèi)似的引用,只要強(qiáng)引用在,垃圾搜集器永遠(yuǎn)不會(huì)搜集被引用的對(duì)象。也就是說(shuō),寧愿出現(xiàn)內(nèi)存溢出,也不會(huì)回收這些對(duì)象。

3.15.3.2.軟引用

軟引用是用來(lái)描述一些有用但并不是必需的對(duì)象,在Java中用java.lang.ref.SoftReference類(lèi)來(lái)表示。對(duì)于軟引用關(guān)聯(lián)著的對(duì)象,只有在內(nèi)存不足的時(shí)候JVM才會(huì)回收該對(duì)象。因此,這一點(diǎn)可以很好地用來(lái)解決OOM的問(wèn)題,并且這個(gè)特性很適合用來(lái)實(shí)現(xiàn)緩存:比如網(wǎng)頁(yè)緩存、圖片緩存等。

import java.lang.ref.SoftReference;public class Main {public static void main(String[] args) {SoftReference<String> sr = new SoftReference<String>(new String("hello"));System.out.println(sr.get());} }

3.15.3.3.弱引用

弱引用也是用來(lái)描述非必需對(duì)象的,當(dāng)JVM進(jìn)行垃圾回收時(shí),無(wú)論內(nèi)存是否充足,都會(huì)回收被弱引用關(guān)聯(lián)的對(duì)象。在java中,用java.lang.ref.WeakReference類(lèi)來(lái)表示。下面是使用示例:

import java.lang.ref.WeakReference;public class Main {public static void main(String[] args) {WeakReference<String> sr = new WeakReference<String>(new String("hello"));System.out.println(sr.get());System.gc(); //通知JVM的gc進(jìn)行垃圾回收System.out.println(sr.get());} }

3.15.3.4.虛引用

虛引用和前面的軟引用、弱引用不同,它并不影響對(duì)象的生命周期。在java中用java.lang.ref.PhantomReference類(lèi)表示。如果一個(gè)對(duì)象與虛引用關(guān)聯(lián),則跟沒(méi)有引用與之關(guān)聯(lián)一樣,在任何時(shí)候都可能被垃圾回收器回收。
要注意的是,虛引用必須和引用隊(duì)列關(guān)聯(lián)使用,當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)它還有虛引用,就會(huì)把這個(gè)虛引用加入到與之 關(guān)聯(lián)的引用隊(duì)列中。程序可以通過(guò)判斷引用隊(duì)列中是否已經(jīng)加入了虛引用,來(lái)了解被引用的對(duì)象是否將要被垃圾回收。如果程序發(fā)現(xiàn)某個(gè)虛引用已經(jīng)被加入到引用隊(duì)列,那么就可以在所引用的對(duì)象的內(nèi)存被回收之前采取必要的行動(dòng)。

import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue;public class Main {public static void main(String[] args) {ReferenceQueue<String> queue = new ReferenceQueue<String>();PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);System.out.println(pr.get());} }

3.15.3.5.軟引用和弱引用進(jìn)一步說(shuō)明

在SoftReference類(lèi)中,有三個(gè)方法,兩個(gè)構(gòu)造方法和一個(gè)get方法(WekReference類(lèi)似):

public class SoftReference<T> extends Reference<T> {/*** Timestamp clock, updated by the garbage collector*/static private long clock;/*** Timestamp updated by each invocation of the get method. The VM may use* this field when selecting soft references to be cleared, but it is not* required to do so.*/private long timestamp;/*** Creates a new soft reference that refers to the given object. The new* reference is not registered with any queue.** @param referent object the new soft reference will refer to*/public SoftReference(T referent) {super(referent);this.timestamp = clock;}/*** Creates a new soft reference that refers to the given object and is* registered with the given queue.** @param referent object the new soft reference will refer to* @param q the queue with which the reference is to be registered,* or <tt>null</tt> if registration is not required**/public SoftReference(T referent, ReferenceQueue<? super T> q) {super(referent, q);this.timestamp = clock;}/*** Returns this reference object's referent. If this reference object has* been cleared, either by the program or by the garbage collector, then* this method returns <code>null</code>.** @return The object to which this reference refers, or* <code>null</code> if this reference object has been cleared*/public T get() {T o = super.get();if (o != null && this.timestamp != clock)this.timestamp = clock;return o;}}

get方法用來(lái)獲取與軟引用關(guān)聯(lián)的對(duì)象的引用,如果該對(duì)象被回收了,則返回null。

在使用軟引用和弱引用的時(shí)候,我們可以顯示地通過(guò)System.gc()來(lái)通知JVM進(jìn)行垃圾回收,但是要注意的是,雖然發(fā)出了通知,JVM不一定會(huì)立刻執(zhí)行,也就是說(shuō)這句是無(wú)法確保此時(shí)JVM一定會(huì)進(jìn)行垃圾回收的。

3.15.3.6.虛引用進(jìn)一步說(shuō)明:

虛引用中有一個(gè)構(gòu)造函數(shù),可以看出,其必須和一個(gè)引用隊(duì)列一起存在。get()方法永遠(yuǎn)返回null,因?yàn)樘撘糜肋h(yuǎn)不可達(dá)。

public class PhantomReference<T> extends Reference<T> {/*** Returns this reference object's referent. Because the referent of a* phantom reference is always inaccessible, this method always returns* <code>null</code>.** @return <code>null</code>*/public T get() {return null;}/*** Creates a new phantom reference that refers to the given object and* is registered with the given queue.** <p> It is possible to create a phantom reference with a <tt>null</tt>* queue, but such a reference is completely useless: Its <tt>get</tt>* method will always return null and, since it does not have a queue, it* will never be enqueued.** @param referent the object the new phantom reference will refer to* @param q the queue with which the reference is to be registered,* or <tt>null</tt> if registration is not required*/public PhantomReference(T referent, ReferenceQueue<? super T> q) {super(referent, q);} }

總結(jié)

以上是生活随笔為你收集整理的3.内存分配、逃逸分析与栈上分配、直接内存和运行时常量池、基本类型的包装类和常量池、TLAB、可达性分析算法(学习笔记)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

av在线在线| 黄色a视频免费 | 国产美女网站在线观看 | 91成人短视频在线观看 | 黄色av免费看 | 视频在线一区二区三区 | 久草视频免费 | 在线电影 你懂得 | 黄色av播放 | av线上看| 人人玩人人弄 | 久久在线免费视频 | a级一a一级在线观看 | 81国产精品久久久久久久久久 | 国产精品99爱 | 在线视频区 | 日韩欧美精品一区二区三区经典 | 五月色丁香| 丁香久久综合 | 六月丁香在线视频 | 中文区中文字幕免费看 | 综合亚洲视频 | 日韩欧美国产精品 | 91中文字幕 | 亚洲va男人天堂 | 色婷婷国产精品一区在线观看 | 成人毛片一区 | 97色在线| 日本在线观看一区二区 | 天天摸夜夜操 | 蜜桃av人人夜夜澡人人爽 | 91人人视频在线观看 | 六月激情久久 | 天天添夜夜操 | 一级黄色在线视频 | 国产成人精品一区二区三区在线 | 日韩电影中文字幕 | 日韩午夜一级片 | 欧美日本高清视频 | 91黄色视屏 | 久久最新 | 日韩两性视频 | av不卡网站 | 久久蜜臀一区二区三区av | 久久人人爽人人片 | 射久久久 | 91最新在线| 国产二区视频在线观看 | 亚洲国产999| 97超碰色| 一区二区不卡视频在线观看 | 色综合久久88色综合天天免费 | 国产视频不卡一区 | 在线欧美中文字幕 | www.啪啪.com| 最新中文字幕 | 国产精品午夜在线 | 91在线产啪| 国产 在线观看 | 国产98色在线 | 日韩 | 国产一区二区不卡视频 | 波多野结衣在线视频一区 | 97人人艹| 精品一区二区亚洲 | 欧美另类69 | 欧美不卡在线 | 337p日本大胆噜噜噜噜 | 国产福利91精品一区 | 97成人精品视频在线播放 | 五月婷婷综合在线 | av在线网站免费观看 | 99久久这里有精品 | 亚洲国内精品 | 亚洲精品美女久久久久 | 亚州视频在线 | 久久亚洲精品电影 | 日本护士三级少妇三级999 | 午夜资源站| 国产a国产a国产a | 免费成人结看片 | 九九涩涩av台湾日本热热 | 91视频在线播放视频 | 国产精品久久人 | 国产福利小视频在线 | 精品免费 | 午夜a区 | 久精品在线观看 | 在线观看免费一区 | 中文在线a∨在线 | 国产美女被啪进深处喷白浆视频 | 天天干 夜夜操 | 国产精品久久久久久久久久久久冷 | 婷婷丁香花 | 操老逼免费视频 | 亚洲成人999 | 99精品免费在线观看 | 免费网站色 | 亚洲精品在线观看的 | 欧美日韩调教 | 91av网址 | 四虎免费av| 亚洲精品资源在线观看 | 久久久精品欧美一区二区免费 | 久久国产视频网站 | av黄色在线播放 | 国产一区在线免费观看 | 91精品国产自产在线观看永久 | 91在线一区二区 | 高清av中文在线字幕观看1 | 亚洲精品在线播放视频 | 午夜久久美女 | 久久超碰在线 | 久久久黄视频 | 亚洲视频综合在线 | 久久久影视 | 一区二区三区日韩在线 | 精品视频久久 | 粉嫩av一区二区三区四区 | av电影 一区二区 | 日日干av | 久久字幕精品一区 | 黄色在线观看污 | 一区二区不卡 | 99久久久| 久久久精品99 | 在线观看成人毛片 | 国产一区二区高清视频 | av电影 一区二区 | 久久三级视频 | 成在人线av| 精品久久久一区二区 | 国产 色| 精品国产人成亚洲区 | 福利精品在线 | 香蕉在线视频播放网站 | 91黄视频在线| 日韩一二三区不卡 | 丁香在线 | 蜜桃久久久 | 在线色亚洲 | 麻豆视频国产 | 欧美大片在线看免费观看 | 国产精品欧美一区二区 | 九九久久影视 | 中文超碰字幕 | 国产麻豆精品传媒av国产下载 | 99久久精品免费看国产一区二区三区 | av中文在线播放 | 国产视频亚洲精品 | 亚洲视频999| 天天草天天操 | 日韩精品一区二区三区中文字幕 | 日韩av视屏在线观看 | av资源在线看 | 伊人中文网 | 色吊丝在线永久观看最新版本 | 有码一区二区三区 | 丝袜美腿在线 | 国产一区二区在线播放 | 国产成人精品亚洲日本在线观看 | 亚洲精品国产品国语在线 | 色伊人网 | 中文字幕影片免费在线观看 | 国产 日韩 欧美 在线 | 免费观看黄色12片一级视频 | 91久久久久久国产精品 | 日日干夜夜草 | 国产精品热视频 | 亚洲人久久久 | 久久久精品福利视频 | 亚洲精品美女久久 | 麻豆视频在线免费观看 | 国产不卡精品视频 | 国产高清免费观看 | 91xav| 久久精品女人毛片国产 | 91手机电视| 亚洲国产精品日韩 | 国产精品美女久久久 | 国产精品午夜av | 91麻豆国产福利在线观看 | 婷婷六月天丁香 | 久久久男人的天堂 | 天天夜夜操| 91精品免费 | 最新国产视频 | 亚洲精品视频在线观看视频 | 开心色激情网 | 国产高清永久免费 | 中文字幕2021 | 一区二区三区四区五区在线视频 | 九草视频在线观看 | 伊人久久国产精品 | 麻豆视频在线免费 | 99久免费精品视频在线观看 | 黄色片视频在线观看 | av性在线 | 91精品国产99久久久久久久 | 国产五月天婷婷 | 中文国产字幕在线观看 | 天天玩天天干 | 亚洲专区中文字幕 | 五月天婷亚洲天综合网精品偷 | 爱色婷婷| 欧美成人在线免费 | 亚洲精品视频免费观看 | 亚洲视频2 | 国产一区二区免费 | 一区二区不卡高清 | 亚洲视频免费在线观看 | 国产精品一区二区三区久久久 | 国产又粗又猛又黄 | 久久草草影视免费网 | 免费视频久久久 | 69xx视频 | 黄色成人毛片 | 中文字幕在线观看视频一区二区三区 | 91最新地址永久入口 | 91九色免费视频 | 日本精品视频免费观看 | 国产精品岛国久久久久久久久红粉 | 中文字幕黄色网 | 亚洲永久精品在线观看 | 在线免费看片 | 日本mv大片欧洲mv大片 | 日韩综合色 | 国产视频网站在线观看 | 高清国产一区 | 成人网在线免费视频 | 91丨九色丨国产丨porny精品 | 亚洲视频在线免费看 | 国产成人三级在线播放 | 毛片3| 久久免费看视频 | 97网| 日韩欧美视频一区二区三区 | 久av在线 | 天天干,天天射,天天操,天天摸 | 国产精品一区一区三区 | 在线观看免费91 | 中文字幕在线观看免费观看 | 国产日韩欧美综合在线 | 亚洲永久精品一区 | 国产资源在线视频 | 国产精品九九热 | 色综合久久中文综合久久牛 | 日韩专区在线观看 | 69精品视频在线观看 | 国产精品日韩久久久久 | 欧美aa级| 探花视频在线观看免费版 | 色福利网 | 日本久草电影 | 91看片在线免费观看 | 日本黄色免费电影网站 | 中文在线中文资源 | 午夜精品中文字幕 | 天天婷婷 | 国产精品一区二 | a视频免费在线观看 | 久久久这里有精品 | 亚洲首页| 国产日韩一区在线 | 99九九视频 | 欧美在线观看视频免费 | 久久草在线视频国产 | 在线成人高清电影 | 亚洲欧洲精品视频 | 久久免费成人精品视频 | 最近免费中文字幕 | 亚洲综合色网站 | 日韩av线观看| 久久五月婷婷丁香社区 | 亚洲三级精品 | 在线视频免费观看 | 国产不卡在线观看 | 日韩av成人在线观看 | 91在线视频导航 | 国产精品成人av久久 | 亚洲国产中文字幕在线观看 | 国产精品欧美在线 | 国产黄色片一级三级 | 亚洲激情在线播放 | 最近中文字幕完整视频高清1 | 久久亚洲综合国产精品99麻豆的功能介绍 | 色国产在线 | 麻豆系列在线观看 | 五月激情婷婷丁香 | 成人播放器 | 91传媒在线观看 | 国产精品欧美在线 | 免费视频你懂得 | 日韩一级电影在线 | 日韩精品一区二区三区外面 | 亚洲精品日韩在线观看 | 99久久久久国产精品免费 | 午夜男人影院 | 国产69精品久久久久99尤 | 精品国产一区二区三区不卡 | 麻豆视频在线播放 | 在线成人性视频 | 婷婷深爱五月 | 国产成人一区二区三区久久精品 | 91理论片午午伦夜理片久久 | 色婷婷97| 91亚洲精品久久久中文字幕 | 色偷偷网站视频 | 亚洲综合在线五月天 | 亚洲精品成人网 | 亚洲国产精品久久久久婷婷884 | 日日骑| 天天天干夜夜夜操 | 久久国产热视频 | 97成人资源站 | 精品美女在线视频 | 亚洲精品在线观看中文字幕 | 五月婷婷爱 | 成人av影视 | 成人精品福利 | 日韩美女久久 | 丁香综合五月 | 久久久久久高清 | 在线综合 亚洲 欧美在线视频 | 色综合中文字幕 | 97国产小视频 | 亚洲精品欧美成人 | 亚洲综合最新在线 | 热久久视久久精品18亚洲精品 | 亚洲美女视频在线 | 久久经典国产 | 久久久久久久久久久影院 | 免费一级特黄毛大片 | 日韩三级在线观看 | 国产免费大片 | 色香蕉在线 | 国产精品ⅴa有声小说 | 国产精品观看在线亚洲人成网 | www.黄色片网站 | 久久精品一区二区三区中文字幕 | 亚洲国产小视频在线观看 | 中文字幕视频三区 | 日本中文字幕久久 | 91热爆视频 | www.看片网站 | 亚洲精品国产综合99久久夜夜嗨 | 91免费视频网站在线观看 | 99av国产精品欲麻豆 | 免费看久久久 | av日韩不卡 | 成人午夜电影网 | www久久国产 | 中文字幕人成一区 | 97国产精品久久 | 97网在线观看 | 最新国产在线观看 | 亚洲午夜久久久影院 | 亚洲视频资源在线 | 五月丁色| 1000部国产精品成人观看 | 91精品系列| 色资源网免费观看视频 | 国产欧美日韩精品一区二区免费 | 国产视频一 | 亚洲 精品在线视频 | 在线视频麻豆 | 亚洲精品久久久久www | 久久国产品 | 国产91欧美| 色丁香综合 | 精品一区久久 | 欧美激情视频免费看 | 免费在线观看午夜视频 | 久久国产综合视频 | 韩国视频一区二区三区 | 亚洲精品免费在线播放 | 一本一本久久a久久精品综合小说 | 中文字幕日韩av | avhd高清在线谜片 | 久久精品毛片 | 久久黄色美女 | 免费看三级 | 99在线视频播放 | 亚洲另类视频在线观看 | 国产成人久久 | 中文字幕日本在线 | 久久国产精品99久久久久久丝袜 | 国产精品你懂的在线观看 | 中国成人一区 | 51久久夜色精品国产麻豆 | 一区二区三区精品在线视频 | 国产视频欧美视频 | 国产精品一区久久久久 | 国产亚洲在线观看 | 激情综合久久 | 日韩字幕| 久久五月天综合 | 亚州精品一二三区 | 国产美女精品视频 | 国产尤物视频在线 | 美女视频永久黄网站免费观看国产 | 久久精品影片 | 狠狠干中文字幕 | 激情网在线视频 | 亚洲 精品在线视频 | 色综合久久久久综合体桃花网 | 在线色资源 | 色噜噜在线观看 | 免费日韩| 一区二区三区视频在线 | 韩国一区视频 | 亚洲美女精品区人人人人 | 中文日韩在线视频 | 婷婷综合国产 | 亚洲一区二区三区四区在线视频 | 色婷婷激情综合 | 综合婷婷 | 日日摸日日 | 亚州av成人| 激情婷婷欧美 | 久久怡红院 | 欧洲精品二区 | av一区二区三区在线观看 | 亚洲乱码在线观看 | 日操操| 一区在线播放 | 亚洲精品午夜国产va久久成人 | 九九热在线视频免费观看 | 亚洲在线视频网站 | 亚洲日韩精品欧美一区二区 | 福利视频一区二区 | 亚洲另类视频在线观看 | 一级特黄aaa大片在线观看 | 日韩激情片在线观看 | 男女拍拍免费视频 | 91在线成人| 亚洲va欧美va人人爽春色影视 | 久久综合免费 | 免费成人黄色av | 韩国精品一区二区三区六区色诱 | 中文字幕乱码在线播放 | 日韩精品免费在线观看 | av资源免费看 | www.av在线播放 | www.久久久精品 | 99中文字幕| 欧美日韩午夜在线 | 成人电影毛片 | 狠狠色婷婷丁香六月 | 福利视频午夜 | 2019中文字幕网站 | 国产黄色精品在线 | 精品一区二区精品 | 婷婷激情综合五月天 | 国产欧美精品在线观看 | www日韩精品 | 精品国产一区二区三区久久久蜜臀 | 久久你懂的 | 亚洲欧美日韩国产精品一区午夜 | 日韩欧美精品一区 | 美女黄频在线观看 | 又黄又爽又湿又无遮挡的在线视频 | 国产色视频网站2 | 久久国产欧美日韩 | 国产中文在线视频 | 黄色日视频 | 欧美日韩一级久久久久久免费看 | 公与妇乱理三级xxx 在线观看视频在线观看 | 成人在线一区二区三区 | 国产精品久久久久9999 | 91一区一区三区 | 在线中文字幕观看 | 三级黄色网址 | 九九九九九国产 | 亚洲精品福利在线 | 亚洲国产精品成人女人久久 | 成年人三级网站 | 欧美日韩精品在线视频 | 午夜精品一区二区三区在线播放 | 久久精品国产一区二区三区 | 在线 欧美 日韩 | 一个色综合网站 | 91片黄在线观 | 亚洲区精品 | 十八岁以下禁止观看的1000个网站 | 992tv又爽又黄的免费视频 | 国产中文字幕久久 | 日韩欧美国产精品 | 免费观看国产精品视频 | 天天干一干 | 2021国产在线 | 人人草人人做 | 一区二区伦理电影 | 久久久免费 | 男女日麻批 | 中文字幕在线观看免费高清电影 | 色大片免费看 | 99精品国产在热久久 | 色婷婷国产精品一区在线观看 | av在线播放快速免费阴 | 91大神精品视频在线观看 | 九九免费精品 | 久草在线国产 | 日日夜夜狠狠 | 国产成人高清 | 久久高清免费观看 | 日韩av美女 | 成 人 黄 色 免费播放 | 午夜久久久精品 | 免费碰碰 | 日韩理论电影在线观看 | 九九精品久久 | 色偷偷网站视频 | 久久久久久久久久久久av | 日韩精品视频在线观看免费 | 色吊丝在线永久观看最新版本 | 国内视频在线观看 | 久久免费电影网 | 五月婷婷毛片 | 伊人天天干| 亚洲国产一区二区精品专区 | 最新一区二区三区 | 天天做综合网 | jizz18欧美18 | av在线色| 免费国产黄线在线观看视频 | 国产91在线观看 | 亚洲一区二区视频在线播放 | 国产最新精品视频 | 亚洲精品在线网站 | 日韩三级.com | 91黄色影视 | 91亚洲精品久久久久图片蜜桃 | 国产亚洲精品久久 | 亚洲综合色婷婷 | 亚洲精品裸体 | 国产不卡免费av | 精品国产伦一区二区三区免费 | 日韩欧美一区二区三区免费观看 | 国产91免费在线 | 手机在线小视频 | 日韩视频中文字幕在线观看 | 天天天操天天天干 | 黄色大全视频 | 国产成人中文字幕 | 看片网站黄色 | 五月婷影院 | 天天干天天干天天干天天干天天干天天干 | 在线欧美小视频 | 麻豆一区二区 | 天天天操天天天干 | 国产一级免费播放 | 人人爽人人爽人人片av | 日韩av一卡二卡三卡 | 91在线成人 | 国产精品区一区 | 国产色区 | 玖玖爱在线观看 | 欧美激情h | 一区二区三区动漫 | 日韩精品国产一区 | 六月丁香激情综合 | 亚洲免费精彩视频 | 五月天婷婷在线播放 | 亚洲精品国产精品国自产观看 | 国产一二区视频 | 日日夜夜婷婷 | 中文字幕一区三区 | 超碰免费久久 | 国内成人综合 | 久久综合欧美 | 亚洲国内精品在线 | 色就是色综合 | 麻豆国产精品一区二区三区 | 在线性视频日韩欧美 | 91网站观看 | 国产精品自在线拍国产 | 欧美激情视频三区 | 少妇bbb搡bbbb搡bbbb| 国产欧美中文字幕 | 精品久久精品 | 亚欧日韩av| 欧美aa级| 99久久精品久久久久久清纯 | 黄色特一级 | 亚洲高清av在线 | 成人免费一级片 | 国产日韩欧美在线观看 | 97免费中文视频在线观看 | 中文字幕在线播放日韩 | 精品国产精品久久一区免费式 | www狠狠操| 69视频国产 | 久久久久福利视频 | 免费看一及片 | 97精品国产91久久久久久久 | 成人精品在线 | 男女视频91 | 91精品国产综合久久福利不卡 | 国产亚洲视频系列 | 黄色影院在线免费观看 | 深夜激情影院 | 亚洲综合射 | 色综合天天色 | 日日干av| 激情久久婷婷 | 久久国产精品免费 | 久草视频免费看 | 超碰免费久久 | 国产亚洲一区二区三区 | 国产高清黄色 | 欧美在线一级片 | 91精品啪在线观看国产线免费 | 中文字幕精品视频 | 丁香激情婷婷 | 日韩午夜一级片 | 精品久久视频 | 亚洲午夜精品电影 | 久久久色 | 中文字幕网站视频在线 | 99视频久| 日韩综合色 | 九九99| 久久久精品国产免费观看一区二区 | 四虎影视www | www.国产在线观看 | 中文字幕美女免费在线 | 国产午夜小视频 | 操操色 | 亚洲专区一二三 | 1024手机看片国产 | 免费观看一级成人毛片 | 天天躁天天躁天天躁婷 | 色噜噜日韩精品一区二区三区视频 | 狠狠操天天射 | 色先锋av资源中文字幕 | 久久久精品99 | av免费看av | 欧美国产日韩在线视频 | 探花视频免费在线观看 | 国产免费三级在线观看 | 黄色三级免费片 | 99r在线播放 | 91看片在线免费观看 | 午夜99| 欧美性生爱 | 久99久在线视频 | 久久国产精品久久w女人spa | 亚洲综合在线视频 | 久久久免费国产 | 中文字幕一区av | 九九免费观看全部免费视频 | 手机看片99| 久久精品国产一区二区电影 | 亚洲精品综合欧美二区变态 | 国产视频首页 | 亚洲激精日韩激精欧美精品 | 91九色性视频 | 91精品在线免费 | 久久久久久久久久福利 | 国产精品久久久久久久久久尿 | 国内精品亚洲 | 在线国产视频一区 | 亚洲国产中文字幕在线观看 | 在线 欧美 日韩 | 丁香激情综合 | 手机在线黄色网址 | 天天操天天干天天爽 | 免费人人干 | 岛国精品一区二区 | 麻豆国产视频 | 日韩专区在线 | 国产欧美日韩一区 | 在线观看中文字幕一区二区 | 欧洲精品在线视频 | 国产精品九九九 | 深夜福利视频一区二区 | 久艹在线播放 | 狠狠干天天操 | 欧美在线观看禁18 | 蜜臀av免费一区二区三区 | 国产小视频网站 | 欧美在线观看视频 | 国产精品wwwwww | 久久大视频 | 超碰在线日韩 | 国产精品av在线免费观看 | 天天天干天天射天天天操 | 97日日 | 中文字幕高清视频 | 99精品国产兔费观看久久99 | 免费99精品国产自在在线 | 亚洲91中文字幕无线码三区 | 欧美黄色成人 | 高清av影院| 欧美二区三区91 | 欧美日韩精品网站 | 国产精品久久久久久超碰 | 高清免费在线视频 | 91桃花视频 | 国产精品扒开做爽爽的视频 | 中文字幕在线观看一区二区三区 | 黄色小说网站在线 | 色五月成人 | 久久久久久亚洲精品 | 亚洲免费成人av电影 | caobi视频 | 欧美黄色高清 | 国产一级黄 | 国产精品久久久久久久久久ktv | 久草精品网 | 亚洲日日射| 热久久精品在线 | 在线导航av | 日日碰狠狠躁久久躁综合网 | 久久视频在线免费观看 | 91网在线观看 | 国产精品专区h在线观看 | 狠狠狠色丁香婷婷综合久久五月 | 日本三级人妇 | 六月丁香婷 | 在线播放日韩av | 色香蕉在线| 就色干综合| 99爱视频在线观看 | 国产69精品久久久久9999apgf | 东方av在| www.超碰| 精品国产一区二区三区在线 | 91成人免费视频 | 婷婷六月色 | 精品久久久久久亚洲综合网站 | 亚洲午夜久久久久久久久久久 | 激情图片久久 | 97av视频 | 免费涩涩网站 | 久草免费新视频 | 麻豆视频在线播放 | 久久免费观看少妇a级毛片 久久久久成人免费 | 99精品在线视频观看 | 99在线免费视频 | 亚洲成人精品 | 五月天天天操 | 婷婷综合电影 | 久久久久久久久久久久电影 | 国产1级毛片 | 精品国产一区二区三区噜噜噜 | 中文字幕久久亚洲 | 成人av.com | 久久精品高清视频 | av综合网址 | 日韩视频1 | 国产小视频在线 | 黄色aa久久 | 国产 日韩 在线 亚洲 字幕 中文 | 99久久日韩精品免费热麻豆美女 | 黄色大全在线观看 | 亚洲国产一二三 | 欧美成人精品欧美一级乱 | 成人在线视频一区 | 久久久久久国产精品美女 | 最近日本mv字幕免费观看 | 色是在线视频 | 特级西西444www大精品视频免费看 | 99精品视频在线观看播放 | 九九视频网站 | 精品久久久久久国产 | 日韩欧美在线免费 | 亚洲日本国产精品 | 日韩av影视 | 毛片美女网站 | 日韩欧美高清不卡 | 五月天九九 | 中文字幕在线观看视频一区 | 99精品久久精品一区二区 | 欧美日韩在线视频观看 | 91一区二区三区久久久久国产乱 | 蜜臀久久99精品久久久无需会员 | www.天天色 | 久久久综合九色合综国产精品 | 久久精品国产亚洲aⅴ | 美女福利视频 | 国产精品99久久久久人中文网介绍 | 999热线在线观看 | 美女免费黄视频网站 | 中文字幕中文字幕中文字幕 | 丁香在线| 国产一级三级 | 狠狠色丁香九九婷婷综合五月 | 视频 天天草 | 色国产精品一区在线观看 | 在线播放视频一区 | 激情校园亚洲 | 国产综合视频在线观看 | 欧美人人爱 | 国产成人久久精品一区二区三区 | 成人免费观看av | 色综合天天 | 亚州视频在线 | 国产最新在线 | 精品福利视频在线 | 中文av资源站 | 中文字幕二区在线观看 | av在线播放亚洲 | a黄色片 | 精品91久久久久 | 日本精品久久久久中文字幕 | a√天堂中文在线 | www.久久91| 黄色片亚洲 | 成人av手机在线 | 亚洲第五色综合网 | 婷婷综合影院 | 日本一区二区三区视频在线播放 | av在线h | 精品福利在线视频 | 久久视频网址 | 久久亚洲美女 | 国产精品自产拍在线观看桃花 | 九九九九九九精品任你躁 | 久久综合免费 | 欧美激情h| 国产黄色片免费 | 中文字幕在线不卡国产视频 | 亚洲人成综合 | 日日夜夜天天久久 | 日本韩国中文字幕 | 在线播放精品一区二区三区 | 精品国产aⅴ一区二区三区 在线直播av | 成人福利av | 亚洲人成人在线 | 超碰在线资源 | 欧美在线观看视频一区二区三区 | 日本在线中文在线 | 久久国产精品久久国产精品 | 伊人久久婷婷 | 99999精品视频 | 91精品推荐 | 免费午夜av | 91网在线 | 黄色1级毛片 | 一区二区三区在线影院 | 亚洲尺码电影av久久 | www.五月婷婷 | 99久久久成人国产精品 | 一区在线播放 | 91亚洲影院 | av黄色在线 | 日韩在线视频免费播放 | 美女视频黄是免费的 | 国产视频中文字幕 | 精品成人国产 | 九九色在线观看 | 久久成人精品电影 | 国产亚洲综合在线 | 伊人色播 | 国产精品一区二区久久国产 | 日韩在线电影一区二区 | 综合网色 | 97超碰人人澡 | 丁香五婷| 亚洲午夜小视频 | 久久精品国产亚洲 | 91自拍视频在线观看 | 日日弄天天弄美女bbbb | 三级大片网站 | 国产91粉嫩白浆在线观看 | 丝袜精品视频 | 五月婷社区 | 96av在线| 欧美黄污视频 | 国产不卡av在线播放 | 亚洲综合日韩在线 | 91激情视频在线 | 天天插日日插 | 国产精品美女久久久 | 精品女同一区二区三区在线观看 | 天天爽天天做 | 国产精品久久久久久一二三四五 | 久久视奸 | av电影 一区二区 | 97偷拍视频 | 日韩黄色大片在线观看 | 国产专区一 | 成人免费在线网 | 久草视频中文在线 | 欧美成人亚洲 | 久热免费| 国产一级精品在线观看 | 午夜精品视频在线 | 97国产一区二区 | 免费看三级网站 | 欧美日韩精品在线观看 | 国产精品1024 | 久久尤物电影视频在线观看 | 欧美日韩大片在线观看 | 四虎天堂 | 精品福利在线观看 | 91精品国产综合久久福利不卡 | 99视频在线免费播放 | 91丨九色丨高潮丰满 | 免费a级黄色毛片 | 欧美激情精品久久久久久变态 | 天堂va在线高清一区 | 亚洲国产精品va在线看黑人 | 亚洲精品97| 麻豆视传媒官网免费观看 | 欧美va在线观看 | 亚州精品一二三区 | 色综合咪咪久久网 | 国产日韩欧美中文 | 亚洲精品国产品国语在线 | 国产精品久久久久久久免费大片 | 午夜色大片在线观看 | 精品国产一区在线观看 | 久久精品黄色 | 国产视频一区在线免费观看 | 四虎亚洲精品 | 久草综合在线 | 国产最新精品视频 | 人人舔人人干 | 免费污片 | 亚洲无毛专区 | 视频一区二区免费 | 日本夜夜草视频网站 | www免费看| 国产精品99久久久久 | 日韩videos| 欧美成人精品在线 | 国产在线自 | 91免费看片黄 | 中文字幕在线字幕中文 | 国产一级做a | 成人激情开心网 | 日韩高清www | 免费av网站观看 | 久久久99精品免费观看app | 久久福利 | 97色国产 | 亚洲午夜久久久久久久久电影网 | 黄色av一区二区 | 精品久久国产精品 | 久久久国产精品网站 | 国产精品久久久久高潮 | 麻豆国产精品永久免费视频 | 日日干夜夜骑 | 在线看片91 | 久久精品视频在线免费观看 | 国产小视频在线 | 一区二区理论片 | 天天综合网久久 | 99久久精品国产系列 | 婷婷五天天在线视频 | 免费人做人爱www的视 | 91福利视频免费 | 伊人久久五月天 | 一级a性色生活片久久毛片波多野 | 在线影院av | 69久久99精品久久久久婷婷 | 国产女v资源在线观看 | 国产午夜精品视频 | 久久免费国产电影 | 久久伊人八月婷婷综合激情 | 国产一级大片免费看 | 黄污视频网站大全 | 激情久久小说 | 国产精品美女久久久久久久 | 国产在线观看免 | 在线精品视频在线观看高清 | 狠狠五月天| 国产午夜精品一区二区三区嫩草 | 欧美日韩精品免费观看视频 | 日本一区二区三区视频在线播放 | 色偷偷88欧美精品久久久 | 综合黄色网 | 视频精品一区二区三区 | 99久久精品免费看国产一区二区三区 | 国产精品av久久久久久无 | 在线亚洲小视频 | 97成人精品视频在线观看 | 91av在线电影 | 国内精品久久久久影院一蜜桃 | 91香蕉视频 | 亚洲成人动漫在线观看 | 91视频电影| 日韩免费在线 | 99在线国产 | 99999精品 | 色综合久久久久久久久五月 | 一区二区在线不卡 | 国产成人无码AⅤ片在线观 日韩av不卡在线 | 欧美性生爱 | 91精品久久久久久综合五月天 | 久草精品视频 | 日韩在线欧美在线 | 日本黄色黄网站 | 亚洲伊人成综合网 | 91一区二区三区久久久久国产乱 | 在线视频中文字幕一区 |