本系列相关链接

尚硅谷 宋红康 JVM教程_01_内存与垃圾回收篇——01 (20210103-20210110)
https://blog.csdn.net/wei198621/article/details/112128852

尚硅谷 宋红康 JVM教程_01_内存与垃圾回收篇——02 (20210111-20210117)
https://blog.csdn.net/wei198621/article/details/112389917

尚硅谷 宋红康 JVM教程_02_字节码与类的加载篇 (20210118~ )
https://blog.csdn.net/wei198621/article/details/112760463

todo 3 , 4

JVM参数列表:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

08 堆

P66-堆空间的概述_进程中堆的唯一性 15:28


方法区与堆,都是进程共享的,一个进程对应一个 JVM 实例
一个进程中的多个线程共享 堆及方法区,
每个线程,有一组 虚拟机栈、本地方法栈、程序计数器

Java堆区在JVM启动的时候就创建了,其空间大小就确定了。是JVM管理的最大内存空间。堆内存大小是可以调节的。


C:\developer_tools\Java\jdk1.8.0_45\bin

本机比宋老师少了几个tab,原因 需要手动安装 visual gc 插件,

VisualVM安装VisualGC插件 过程介绍文章:
https://blog.csdn.net/weixin_45759791/article/details/107332860

P67-堆空间关于对象创建和和GC的概述 17:38

《Java虚拟机规范》中对java堆的描述,所有的对象实例以及数组都应当在运行时分配在堆上面。
数组和对象,可能永远都不会存储在栈上,因为栈中保存引用,这个引用执行对象或者数组在堆中的位置。
方法结束后,堆中的数据不会马上被移除,在垃圾回收的时候才会被回收。

P68-堆的细分内存结构 12:59

现代垃圾收集器,大部分都是基于分带收集理论设计的,堆空间细分为:
JDK7: 1. 新生区 (Yong/New) 2.养老区 (Old/Tenure) 3.永久区 (Perm)
JDK8: 1. 新生区 (Yong/New) 2.养老区 (Old/Tenure) 3.元空间 (Meta)
新生区: 分为 Eden + Survivor

堆中包含: 新生区 + 养老区 + 元空间 虽然元空间在堆里面,但实际是method area 管理,
中国包含: 大陆 + 香港、澳门 + 台——湾 (同理 台——湾)

jdk 1.8 配置jvm 后效果

  • -Xms10m -Xmx10m -XX:+PrintGCDetails
  •                             打印垃圾回收细节
    
HeapPSYoungGen      total 2560K, used 2031K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)eden space 2048K, 99% used [0x00000000ffd00000,0x00000000ffefbe58,0x00000000fff00000)from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)ParOldGen       total 7168K, used 0K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)object space 7168K, 0% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffd00000)Metaspace       used 3332K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 358K, capacity 388K, committed 512K, reserved 1048576K

Metaspace

运行时环境改为jdk 1.7 后的效果

  • -Xms10m -Xmx10m -XX:+PrintGCDetails
  •                             打印垃圾回收细节
    
HeapPSYoungGen      total 3072K, used 1343K [0x00000000ffc80000, 0x0000000100000000, 0x0000000100000000)eden space 2560K, 52% used [0x00000000ffc80000,0x00000000ffdcfc48,0x00000000fff00000)from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)ParOldGen       total 7168K, used 0K [0x00000000ff580000, 0x00000000ffc80000, 0x00000000ffc80000)object space 7168K, 0% used [0x00000000ff580000,0x00000000ff580000,0x00000000ffc80000)PSPermGen       total 21504K, used 2939K [0x00000000fa380000, 0x00000000fb880000, 0x00000000ff580000)object space 21504K, 13% used [0x00000000fa380000,0x00000000fa65eed8,0x00000000fb880000)

PSPermGen

P69-堆空间大小的设置和查看 21:29

-Xms: 用于设置堆区(新生代+老年代)的起始内存,等价于 -XX:InitialHeapSize
-X : jvm的运行参数
ms: memory start

-Xmx: 最大内存

此页面有相应的参数
https://docs.oracle.com/en/java/javase/11/tools/java.html


默认堆空间大小
默认物理内存的 1/64
默认物理内存的 1/4

package com.tiza.jvm.chapter08;/*** @author leowei* @date 2021/1/9  - 14:08* 默认物理内存的   1/64
默认物理内存的    1/4 * */
public class HeapSpaceInitial {public static void main(String[] args) {long initialMemory =  Runtime.getRuntime().totalMemory()/1024/1024 ;long maxMemory = Runtime.getRuntime().maxMemory()/1024/1024;System.out.println("-Xms:" + initialMemory+ "M");System.out.println("-Xmx:" + maxMemory+ "M");System.out.println("系统内存大小为: " + initialMemory * 64.0 /1024 + "G");System.out.println("系统内存大小为: " + maxMemory * 4.0 /1024 + "G");}
}

本机示例

-Xms:243M
-Xmx:3611M
系统内存大小为: 15.1875G
系统内存大小为: 14.10546875GProcess finished with exit code 0

手动设置示例: -Xms600m -Xmx600m
开发中建议 将初始与最大设置为相同的值,原因是

jps
jstat -gc *** (进程ID) 查看进程中内存使用情况
S0C S1C S0U S1U EC EU OC OU

------------ 新生区------------------》
S0C: survive Count 幸存区 0 总数
S0U: s0 Used
S1C: survive Count 幸存区 1总数
S1U: s1 Used
EC: Eden Count 伊甸园区
EU: Eden used
------------ 老年区 ------------------》
OC :old count 老年区
OU: old used

-XX:+PrintGCDetails --查看运行时堆参数

-XX:+PrintGCDetails

package com.tiza.jvm.chapter08;/*** @author leowei* @date 2021/1/9  - 14:08* 默认物理内存的   1/64
默认物理内存的    1/4**/
public class HeapSpaceInitial {public static void main(String[] args) {long initialMemory =  Runtime.getRuntime().totalMemory()/1024/1024 ;long maxMemory = Runtime.getRuntime().maxMemory()/1024/1024;System.out.println("-Xms:" + initialMemory+ "M");System.out.println("-Xmx:" + maxMemory+ "M");System.out.println("系统内存大小为: " + initialMemory * 64.0 /1024 + "G");System.out.println("系统内存大小为: " + maxMemory * 4.0 /1024 + "G");}
}

P70-OOM的说明与举例 09:40

Throwable

Error
Exception

平时说的异常包括 error + Exception ( 百知教育 胡大大 java基础中有介绍 )

package com.tiza.jvm.chapter08;import java.util.ArrayList;
import java.util.Random;/*** -Xms600m -Xmx600m* @author leowei* @date 2021/1/9  - 15:58*/
public class OOMtest {public static void main(String[] args) {ArrayList<Picture> list = new ArrayList<Picture>();while (true){try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}list.add(new Picture(new Random().nextInt(1024*1024)));}}}class Picture{private byte[] pixels;public Picture(int length){this.pixels =new byte[length];}
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat com.tiza.jvm.chapter08.Picture.<init>(OOMtest.java:34)at com.tiza.jvm.chapter08.OOMtest.main(OOMtest.java:21)

P71-新生代与老年代中相关参数的设置 20:37

生命周期比较短:YoungGen ( Eden ,Survivor0 , Survivor1 )
生命周期比较长: Old Gen


-XX:NewRatio=2, 默认,表示新生代占1,老年代栈2,新生代占整个堆的1/3 ;
修改示例:
-xx:NewRatio=4 , 1, 4, 1/5 ;

默认情况下: Eden:Survivor0:Survivor1 == 8:1:1

-XX:SurvivorRatio=8 —手动指定新生代各块比例为8:1:1

-Xms600m -Xmx600m -XX:SurvivorRatio=8

-xx:-UseAdaptiveSizePolicy —关闭 自适用内存分配策略
-xx:+UseAdaptiveSizePolicy —开启 自适用内存分配策略

jps
jinfo -flag NewRatio 进程ID号 — 查看 老年代/ 新生代 的比例
jinfo -flag SurvivorRatio 进程ID号 – 新生代 各块比例
jstat -gc 4988

C:\Users\wei19>jps
1952 Launcher
1988 Main
4988 OOMtest
6572 JpsC:\Users\wei19>jinfo -flag NewRatio 4988
-XX:NewRatio=2
C:\Users\wei19>jinfo -flag SurvivorRatio 4988
-XX:SurvivorRatio=8
C:\Users\wei19>jstat -gc 4988
Warning: Unresolved Symbol: sun.gc.metaspace.capacity substituted NaN
Warning: Unresolved Symbol: sun.gc.metaspace.used substituted NaN
Warning: Unresolved Symbol: sun.gc.compressedclassspace.capacity substituted NaN
Warning: Unresolved Symbol: sun.gc.compressedclassspace.used substituted NaNS0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT
25600.0 25600.0  0.0    0.0   153600.0 81136.7   409600.0     0.0       -      -      -      -         0    0.000   0      0.000    0.000

几乎所有的对象都是在Eden中被New 出来的。
绝大部分的对象都在新生代进行销毁。—80%的对象都是“朝生夕死”

-Xmn 设置新生代最大内存

P72-图解对象分配的一般过程 18:25



tenured [ˈtenjərd] 终身的;长期保有的;
promotion 美 [prəˈmoʊʃn] 促进,增进; 提升,升级;

-XX:MaxTenuringThreshold=15 —晋升的age值,默认15,

总结:
针对幸存者s0,s1 区的总结,复制后有交换,谁空谁是to.
关于垃圾回收:频繁在新生区收集,很少在养老区收集,几乎不再永久区/元空间收集

P73-对象分配的特殊情况 06:38

P74-代码举例与JVisualVM演示对象的分配过程 05:38

major gc (full gc ) : 老年代的垃圾回收

package com.tiza.jvm.chapter08;import java.util.ArrayList;
import java.util.Random;/*** @author leowei* @date 2021/1/9  - 20:22*/
public class HeapInstanceTest {byte[] buffer =new byte[new Random().nextInt(1024*200)];public static void main(String[] args) {ArrayList<HeapInstanceTest> list=new ArrayList<HeapInstanceTest>();while (true){list.add(new HeapInstanceTest());try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

P75-常用优工具概述与Jprofiler的演示 04:01

常用调优工具

JDK命令行 : (jinfo jstat javap jmap )
Eclipse:Memory Analyzer Tool
Jconsole
VisualVM
Jprofiler (客户端,及IDEA 中的插件)
Java Flight Recorder (JMC 中的 )
GCViewer
GC Easy

P76-MinorGC、MajorGC和FullGC的对比 17:26

MinorGC: YoungGC 等同于 MinorGC ,知识新生代(Eden,s0,s1)的垃圾收集
MajorGC: Old GC 等同于 MajorGC, 只是老年代的垃圾收集
FullGC: 收集整个java堆 和 方法区的垃圾收集。

GC 线程, 用户线程, STW (stop the word)
垃圾回收越少越好, GC线程调用的时候会影响用户线程的执行,具体的方式是STW (stop the word) ,调优的工作,具体也是让垃圾少,垃圾回收调用次数少。

妈妈是 GC ,垃圾回收线程
自己是用户线程,
所以收拾屋子的时候,要用户线程停止,妈妈给手机好屋子后,用户线程再执行。

Major GC:
Major GC的速度一般比Minor GC 慢10倍以上,STW 的时间更长

Full GC:
触发FullGC 的原因

老年代空间不足
方法区空间不足
系统调用 System.gc()

full GC 在开发中尽量避免。

P77-GC举例与日志分析 09:28

-Xms9m -Xmx9m -XX:+PrintGCDetails

package com.tiza.jvm.chapter08;import java.util.ArrayList;/**** -Xms10m -Xmx10m -XX:+PrintGCDetails** @author leowei* @date 2021/1/9  - 21:20*/
public class GCTest {public static void main(String[] args) {int i=0;try {ArrayList<String> list = new ArrayList<>();String a="leavint tiza .com ";while (true){list.add(a);a=a+a;i++;}} catch (Exception e) {e.printStackTrace();System.out.println(" 遍历次数: "+ i );}}
}

P78-体会堆空间分代的思想 05:09

P79-总结内存分配策略 12:56

默认有个年龄阈值,15,过15放到 老年代
-XX:MaxTenuringThreshold — 对象晋升老年代的年龄阈值

优先分配到Eden
大对象直接分配到老年代
长期存活的对象分配到老年代
只要有GC ,就会有STW ,


-Xms60m -Xmx60m -XX:+PrintGCDetails -XX:NewRatio=2 -XX:SurvivorRatio=8

package com.tiza.jvm.chapter08;/***  大对象直接进入老年代* -Xms60m -Xmx60m -XX:+PrintGCDetails -XX:NewRatio=2 -XX:SurvivorRatio=8** 经过上述参数配置后* E  S0  S1   O* 16 2  2    40* @author leowei* @date 2021/1/10  - 9:45*/
public class YoungOldAreaTest {public static void main(String[] args) {byte[] bytes = new byte[1024 * 1024 * 20];}
}

P80-堆空间为每个线程分配的TLAB 09:55

TLAB: Thread Local Allocation Buffer
堆是线程共享区域,任何线程都可以访问堆中的共享数据
为了避免多个线程操作同一地址,需要使用加锁机制,进而影响分配速度
为应对上面的问题
Eden区域为每个线程分配一个单独的私有缓冲区域,就避免了线程安全问题,此为快速分配策略。
TLBA,仅占 Eden 1% .

-xx:TLABWasteTargetPersent 设置TLAB 大小。

jinfo -flag UseTLAB 6276

package com.tiza.jvm.chapter08;/**** -XX:UseTLAB*C:\Users\wei19>jps24645904 Jps96 RemoteMavenServer10088 TLABArgsTest14360 LauncherC:\Users\wei19>jinfo -flag UseTLAB 10088-XX:+UseTLAB                ---  表示使用了 TLAB *** @author leowei* @date 2021/1/10  - 10:05*/
public class TLABArgsTest {public static void main(String[] args) throws InterruptedException {System.out.println("111");Thread.sleep(1000000);}}

P81-小结堆空间的常用参数设置 18:45

  • 官网说明:
    https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
    堆空间中常用参数

-XX:+PrintFlagsInitial 查看所有参数的默认初始值
-XX:+PrintFlagsFinal 查看所有参数的最终值

具体查看某个参数的指令 1. jps: 查看当前运行的进程 2. 如: jinfo -flag SurvivorRatio 进程ID

-Xms:初始堆空间内存 物理内存的 1/64
-Xmx:最大堆空间内存 物理内存的 1/4
-Xmn:设置新生代内存大小
-XX:NewRatio: 配置新生代与老年代在堆结构中的比例
-XX:SurvivorRatio: 设置新生代Eden vs S0/s1 空间的比例
-XX:MaxTenuringThreshold 设置新生代垃圾的最大年龄
-XX:+PrintGCDetails: 输出详细GC处理日志
打印GC 简要信息 -XX:+PrintGC -verbose:gc
-XX:HandlePromotionFailure: 是否设置空间分配担保 JDK7 以后,此参数一直是true

P82-通过逃逸分析看堆空间的对象分配策略 18:43

堆是分配对象存储的唯一选择吗?
逃逸分析: Escape Analysis
逃逸: 7.9 KM/S 逃离地球 第一宇宙速度

判断是否发生逃逸,看New的对象是否有可能在方法外被调用。

JDK7以后,HotSpot中默认开启了逃逸分析
-xx:+DoEscapeAnalysis 显式开启逃逸分析
-XX:+PrintEscapeAnalysis 查看逃逸分析的筛选结果

结论: 开发中能使用局部变量的,就不要在方法外定义。

package com.tiza.jvm.chapter08;/*** @author leowei* @date 2021/1/10  - 11:16** 判断逃逸分析的发生: new 的对象实体,是否在方法外被调用**/
public class EscapeAnalysis {public EscapeAnalysis obj;/*此方法返回的Obj实体,会在方法外部被调用*/public EscapeAnalysis getInstance(){return obj ==null ? new EscapeAnalysis() :obj;}/*为成员变量赋值,发生逃逸*/public void setObj(){this.obj =new EscapeAnalysis();}/*对象esc 的作用域尽在当前方法,没有发生逃逸*/public void useEscapeAnalysis(){EscapeAnalysis esc = new EscapeAnalysis();}/*引用成员变量发生逃逸 */public void useEscapeAnalysis2(){EscapeAnalysis esc2 = getInstance();}
}

P83-代码优化之栈上分配 07:46

代码优化

栈上分配
同步省略
标量替换

栈上分配:如果没有逃逸,在栈上分配此对象,


  •               -DoEscapeAnalysis   不开启逃逸分析
    
  •               +DoEscapeAnalysis   开启逃逸分析
    
  • -Xmx1g -Xms1g -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -----花费时间为:83ms
  • -Xmx1g -Xms1g -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -----花费时间为:4ms
  • -Xmx256m -Xms256m -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -----花费时间为:67ms (执行了垃圾回收)
  • -Xmx256m -Xms256m -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -----花费时间为:4ms

开启与关闭逃逸分析,创建对象用时对比分析,开启逃逸分析后,用时明显减少。

package com.tiza.jvm.chapter08;/*** @author leowei* @date 2021/1/10  - 12:34*                   -DoEscapeAnalysis   不开启逃逸分析*                   +DoEscapeAnalysis   开启逃逸分析* -Xmx1g -Xms1g -XX:-DoEscapeAnalysis -XX:+PrintGCDetails          -----花费时间为:83ms* -Xmx1g -Xms1g -XX:+DoEscapeAnalysis -XX:+PrintGCDetails          -----花费时间为:4ms* -Xmx256m -Xms256m -XX:-DoEscapeAnalysis -XX:+PrintGCDetails      -----花费时间为:67ms  (执行了垃圾回收)* -Xmx256m -Xms256m -XX:+DoEscapeAnalysis -XX:+PrintGCDetails      -----花费时间为:4ms**/
public class StackAllocation {public static void main(String[] args) {long start = System.currentTimeMillis();for (int i = 0; i < 10000000; i++) {   //1000 万次alloc();}long end =System.currentTimeMillis();System.out.println("花费时间为:"+ (end-start)+ "ms");try {Thread.sleep(1000000);} catch (InterruptedException e) {e.printStackTrace();}}private static void alloc(){User user = new User();       // 此对象未发生逃逸  ,所以在栈中分配 ,不用垃圾回收}static class User{}
}

P84-代码优化之同步省略 04:58

同步省略: 如果一个对象只能从一个线程被访问到,对于此对象的操作可以不考虑同步。
线程同步的代价是相当高的,同步的后果是降低并发性和性能。

P85-代码优化之标量替换 06:49

分离对象 或者 标量替换 :对象可以不存储在内存,而是存储在CPU寄存器中。(听不懂)
标量(Scalar):一个无法再分析成更小的数据。
聚合量(Aggregate): 可以再分析的叫做聚合量

开启标量替换
-XX:+EliminateAllocations 默认开启,允许将对象打散分配在栈上

package com.tiza.jvm.chapter08;import javax.swing.text.SimpleAttributeSet;/*** @author leowei* @date 2021/1/10  - 13:23* -XX:+EliminateAllocations:    开启标量替换,默认是开启的,允许将对象打散分配在栈上。* -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations* ------------------------------[GC (Allocation Failure)  25600K->1032K(98304K), 0.0007479 secs]* ------------------------------花费时间为19 ms* -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:+EliminateAllocations   * ----------------------------- 花费时间为8 ms*/
public class ScalarReplace {public static class User{public int id;public String name;}public static void alloc(){User u =new User();  // 未发生逃逸u.id =5;u.name ="www.atguigu.com";}public static void main(String[] args) {long start = System.currentTimeMillis();for (int i = 0; i < 1000000; i++) {alloc();}long end = System.currentTimeMillis();System.out.println("花费时间为" + (end - start) + " ms");}
}

P86-代码优化及堆的小结 06:31

代码优化

栈上分配
同步省略
标量替换

逃逸分析并不是十分成熟
对象实例十分分配在堆空间的。

09 方法区

P87-方法区概述_栈堆方法区间的交互关系 11:42

方法区: 永久代 (1.7 -) / 元空间 (1.8+)
栈、堆、方法区之间的配合关系:
程序计数器: 不会报 StackOverflowError ,
下图非常牛逼:包含信息能看懂,要有些功底 。
java栈中本地变量的每个占用是一个slot, long , double 占用两个单位的slot .
对象类型数据:方法区
对象实例数据:堆

P88-方法区的基本理解 17:27

/*** @author leowei* @date 2021/1/10  - 14:11** 其他操作都不做,就打开jkd  bin 下面的Visual VM  ,* 查看 监视 选项卡 看 实体个数**/
public class MethodAreaDemo {public static void main(String[] args) {System.out.println("start ... ");try {Thread.sleep(100000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("end ...");}
}

P89-Hotspot中方法区的演进 09:37

jdk7之前,方法区叫做永久代
jdk8之后,方法区叫做元空间

本质上,方法区和永久代并不等价(HotSpot虚拟机中可以认为方法区=永久代,比如 广东人 把旺财 == 狗 ), BEA JRockit / IBM J9 中并不存在永久代的概念。


永久代 与 元空间 本质上的区别
永久代:用的是java虚拟机的内存 实现方法区:
元空间:用的是本地内存实现 方法区
-XX:MaxPermSize

P90-设置方法区大小的参数 14:52

jdk7

-XX:PermSize ---------20.75M
-XX:MaxPermSize ------ 64M(32位机器) 82M(64位机器)
jps
jinfo -flag PermSize 进程ID
jinfo -flag MaxPermSize 进程ID

jdk8

-XX:MetaspaceSize ---- 21m
-XX:MaxMetaspaceSize — 由于使用本机内存,此值没有限制
jps
jinfo -flag MetaspaceSize 进程ID
jinfo -flag MaxMetaspaceSize 进程ID

C:\Users\wei19>jps
17768 MethodAreaDemo
C:\Users\wei19>jinfo -flag PermSize 17076
no such flag 'PermSize'   ---- jdk 1.8 运行环境 C:\Users\wei19>jinfo -flag MetaspaceSize 7672
-XX:MetaspaceSize=21807104
C:\Users\wei19>jinfo -flag MaxMetaspaceSize 7672
-XX:MaxMetaspaceSize=18446744073709486080
----------------------------------------------------
C:\Users\wei19>jinfo -flag PermSize 17768
-XX:PermSize=21757952   ----  jdk 1.7 运行环境

在jdk8环境中配置 jdk7时候的参数
-XX:PermSize=100m -XX:MaxPermSize=100m
会提示如下错误

start ...
end ...
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=100m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=100m; support was removed in 8.0

P91-OOM:PermGen和OOM:Metaspace举例 09:58

内存泄漏: ???

P92-方法区的内部结构1 21:14

方法区中存放大信息有: 类型信息、域信息、方法信息、常量(运行时常量池)、静态变量、JIT、

类型信息:
域信息(Field):
方法信息(Method)


C:\workspace\workspace_idea\jvmByAtguigu\chapter09\target\classes\com\tiza\jvm\chapter09>javap -v -p MethodInnerStructTest.class > MethodInnerStructTxt.txt

MethodInnerStructTxt.txt 介绍

  Compiled from "MethodInnerStructTest.java"//01: 类型信息
public class com.tiza.jvm.chapter09.MethodInnerStructTest extends java.lang.Object implements java.lang.Comparable<java.lang.String>, java.io.Serializableminor version: 0//  02  域信息public int num;descriptor: Iflags: ACC_PUBLICprivate static java.lang.String str;descriptor: Ljava/lang/String;flags: ACC_PRIVATE, ACC_STATIC
//03  方法信息    (包含构造器及方法 )public com.tiza.jvm.chapter09.MethodInnerStructTest();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=1, args_size=1...public void test1();descriptor: ()V    // V =voidflags: ACC_PUBLICCode:   // stack=3  操作数栈的深度=3   局部变量表的长度=2   args_size=1 就是本身 thisstack=3, locals=2, args_size=1     ...public static int test2(int);descriptor: (I)Iflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=3, args_size=1...       

P93-方法区的内部结构2 08:13

静态变量和类关联在一起,随着类的加载而加载,它们成为类数据在逻辑上的一部分。
类变量被所有类的实例所共享,即使没有类实例时你也可以访问它。

全局常量: static final ,每个全局常量在编译的时候就被分配了。

C:\workspace\workspace_idea\jvmByAtguigu\chapter09\target\classes\com\tiza\jvm\chapter09>javap -v -p Order.class > Order.txt-------Order.java
class Order{// 查看 Order.class 文件中下面两个域 编译后的效果 public static int count=1;public static final int number=2;。。。
}-------Order.class public static int count;descriptor: Iflags: ACC_PUBLIC, ACC_STATICpublic static final int number;descriptor: Iflags: ACC_PUBLIC, ACC_STATIC, ACC_FINALConstantValue: int 2     // 声明为final static的变量在此时就已经赋值了 

对比static vs static final

P94-class文件中常量池的理解 18:12

运行时常量池:
Constant Pool :常量池,可以看做一张表,虚拟机指令根据这张常量表,找到要执行的类名,方法名,参数类型,字面量等类型。

P95-运行时常量池的理解 06:38

P96-图示举例方法区的使用 —<综合知识点需要多看> 16:45

系统讲解简单的方法加数据中涉及到的栈 、 程序计数器、 方法区 中具体的操作

P97-方法区在jdk6、jdk7、jdk8中的演进细节 25:21

只有HotSpot才有永久代。

jdk6 : 静态变量放在永久代上
jdk7 : 静态变量放在堆上, 字符串常量池放在堆上 (仍然有永久代的概念)
jdk8 : 静态变量放在堆上, 字符串常量池放在堆上; 类型信息、字段、方法、常量保存在本地内存元空间 (没有永久代,用元空间替换它)

heap: static variable 静态变量; StringTable 字符串常量池
永久代为什么要被元空间替换?

P98-StringTable为什么要调整位置 05:27

StringTable (Interned String) : 字符串常量池

P99-如何证明静态变量存在哪 11:15

package com.tiza.jvm.chapter09;/*** @author leowei* @date 2021/1/11  - 23:19** jdk6  jdk7* -Xms200m -Xmx200m -XX:PermSize=300m -XX:MaxPermSize=300m -XX:+PrintGCDetails** jdk8* -Xms200m -Xmx200m -XX:MetaspaceSize=300m -XX:MaxMetaspaceSize=300m -XX:+PrintGCDetails** 这块只能证明new 的 大数据放在了堆空间的老年代  */
public class StaticFieldTest {private static byte[] arr=new byte[1024*1024*100];  // 100MBpublic static void main(String[] args) {System.out.println( StaticFieldTest.arr);}/*    JDK 6  7   打印结果   ----数据放在了   ParOldGen  这个空间内HeapPSYoungGen      total 59712K, used 3072K [0x00000000fbd60000, 0x0000000100000000, 0x0000000100000000)eden space 51200K, 6% used [0x00000000fbd60000,0x00000000fc0600a0,0x00000000fef60000)from space 8512K, 0% used [0x00000000ff7b0000,0x00000000ff7b0000,0x0000000100000000)to   space 8512K, 0% used [0x00000000fef60000,0x00000000fef60000,0x00000000ff7b0000)PSOldGen        total 136576K, used 102400K [0x00000000f3800000, 0x00000000fbd60000, 0x00000000fbd60000)object space 136576K, 74% used [0x00000000f3800000,0x00000000f9c00010,0x00000000fbd60000)PSPermGen       total 307200K, used 3814K [0x00000000e0c00000, 0x00000000f3800000, 0x00000000f3800000)object space 307200K, 1% used [0x00000000e0c00000,0x00000000e0fb98e0,0x00000000f3800000)*//*    JDK 8 打印结果   ----数据放在了   ParOldGen  这个空间内HeapPSYoungGen      total 59904K, used 6206K [0x00000000fbd80000, 0x0000000100000000, 0x0000000100000000)eden space 51712K, 12% used [0x00000000fbd80000,0x00000000fc38f8c8,0x00000000ff000000)from space 8192K, 0% used [0x00000000ff800000,0x00000000ff800000,0x0000000100000000)to   space 8192K, 0% used [0x00000000ff000000,0x00000000ff000000,0x00000000ff800000)ParOldGen       total 136704K, used 102400K [0x00000000f3800000, 0x00000000fbd80000, 0x00000000fbd80000)object space 136704K, 74% used [0x00000000f3800000,0x00000000f9c00010,0x00000000fbd80000)Metaspace       used 3333K, capacity 4496K, committed 4864K, reserved 1056768Kclass space    used 358K, capacity 388K, committed 512K, reserved 1048576K*/
}

P100-方法区的垃圾回收行为 11:10

《Java虚拟机规范》 中没有明确要求方法区进行垃圾回收。
一句话,方法区的垃圾回收很难做

P101-运行时数据区的总结与常见大厂面试题说明 06:25

10 对象的实例化内存布局与访问定位

P102-对象实例化的几种方式 10:05


P103-字节码角度看对象的创建过程 06:12

package com.tiza.jvm.chapter10;/*** @author leowei* @date 2021/1/12  - 0:04*/
public class ObjectTest {public static void main(String[] args) {Object obj = new Object();}
}

javap -v -p ObjectTest.class

P104-对象创建的六个步骤 22:07

P105-对象的内存布局 —<综合知识点需要多看> 11:00

package com.tiza.jvm.chapter10;/*** @author leowei* @date 2021/1/12  - 20:36*/
public class CustomerTest {public static void main(String[] args) {Customer cust = new Customer();}
}
package com.tiza.jvm.chapter10;/*** @author leowei* @date 2021/1/12  - 20:37*/
public class Customer {int id=1001;String name;           // 字符串常量   jdk7 以后 ,放在 堆中 Account acct;{name ="匿名客户";}public Customer(){acct=new Account();}}class Account{}

对象在内存中的布局:
对象头 (Header) ;
实例数据(Instance Data);
对齐填充 (Padding);

P106-对象访问定位 07:48

对象访问方式有两种:

  1. 句柄访问
  2. 直接指针访问(Hotspot采用)
  1. 句柄访问
    缺点: 堆空间中的句柄池要占用空间

  2. 直接指针访问(Hotspot采用)

11 直接内存 Direct Memory

P107-直接内存的简单体验 07:53

直接内存,不是JVM的一部分。也不是《java虚拟机规范》中的具体要求

package com.tiza.jvm.chapter11;import java.nio.ByteBuffer;
import java.util.Scanner;/*** @author leowei* @date 2021/1/12  - 21:05*/
public class BufferTest {private static final int BUFFER = 1024*1024*1024; // 1GBpublic static void main(String[] args) {ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);System.out.println("直接内存分配完毕,请求指示!");Scanner scanner = new Scanner(System.in);scanner.next();System.out.println("直接内存开始释放");byteBuffer=null;System.gc();scanner.next();}
}

直接内存分配完毕,请求指示! 的时候看13864 占用空间 1G 空间
system.in 后 ,再次看 13864 占用的大小

P108-使用本地内存读写数据的测试 07:49

通常,访问直接内存的速度会优于Java堆,所以出于性能的考虑,读写频繁的场合会考虑使用直接内存。
java的NIO 库,允许JAVA程序使用直接内存,用于数据缓冲

视频中介绍了,使用直接内存,使用传统IO 两种方式 复制 一步 1G 的电影,比较其速度。

P109-直接内存的00M与内存大小的设置 10:44

直接内存可以通过MaxDirectMemorySize设置
直接内存的缺点:

分配回收成本较高
不受JVM内存回收管理
`package com.tiza.jvm.chapter11;

import java.nio.ByteBuffer;
import java.util.ArrayList;

/**

  • @author leowei
  • @date 2021/1/12 - 21:44
  • 本机测试 180 次
  • java.lang.OutOfMemoryError: Direct buffer memory
    /
    public class BufferTestOutOfMemory {
    private static final int BUFFER=1024
    1024*20; //20M
    public static void main(String[] args) {
    ArrayList list = new ArrayList();
    int count=0;
    while(true){
    //不断的将20M 的数据往 list 中加 看oom 时候的报错,及count
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER);
    list.add(byteBuffer);
    count++;
    try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }finally {
    System.out.println(count);
    }
    }
    }
    }
    `
package com.tiza.jvm.chapter11;import sun.misc.Unsafe;import java.lang.reflect.Field;/*** -Xmx20m -XX:MaxDirectMemorySize=10m** @author leowei* @date 2021/1/12  - 21:49** java.lang.OutOfMemoryError*/
public class MaxDirectMemorySizeTest {private static final long _1MB = 1024 * 1024;public static void main(String[] args) throws IllegalAccessException{Field unsafeField = Unsafe.class.getDeclaredFields()[0];unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe)unsafeField.get(null);while(true){unsafe.allocateMemory(_1MB);}}
}

12 执行引擎

P110-执行引擎的作用及工作过程概述 18:47

执行引擎的作用: 将字节码指令解释/编译为对应平台上的本地机器指令。也就是说,执行引擎充当了将高级语言翻译为机器语言的译者的角色。

P111-Java程序的编译和解释运行的理解 10:11


对应上图第一部分灰色部分

JAVA是半编译半解释型的语言

解释器:Interpreter 对字节码采用逐行解释的方式执行
JIT编译器: Just In Time Compiler .

P112-机器码_指令_汇编_高级语言理解与执行过程 15:40

机器码:二进制编码方式表示的指令。
指令: mov,inc等指令
指令集:x86指令集; ARM指令集;
汇编语言: 由于指令的可读性太差,汇编语言使用助记符带起机器的操作码,用地址符号代替指令的地址
高级语言:更接近人类的语言。
字节码: 比机器码更加抽象的一种中间状态的二进制码,字节码为了实现特定软件运行和硬件环境无关。

P113-解释器的使用 11:00

解释器:充当翻译者的角色。现在使用解释器的方式比较低效,JVM现在增加即时编译器
JAVA中的解释器分两种

字节码解释器
模板解释器

P114-HotspotVM为何解释器与JIT编译器并存 17:32

JIT编译器:Just In Time
HotSpot 采用 解释器与JIT编译器 混合执行。
JIT特点是比解释器速度快。
解释器保留的原因是: 当程序启动后,解释器立即执行,省去了编译的环节,响应速度快。


运行一下代码,打开jvisualvm jconsole 查看效果

package com.tiza.jvm.chapter12;import java.util.ArrayList;/*** @author leowei* @date 2021/1/12  - 23:35*/
public class JITTest {public static void main(String[] args) {ArrayList<String> list = new ArrayList<String>();for (int i = 0; i < 1000; i++) {list.add(" 狗升科技  ");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

jvisualvm

C:\Users\wei19>jvisualvm

jconsole

C:\Users\wei19>jconsole

P115-热点代码探测确定何时JIT 16:53

编译器:

前端编译器: 将.java文件转换为.class文件 (Sun 的Javac ;Eclipse JDT )
后端编译器:JIT编译器 Just In Time Compiler (HotSpot VM )

解释器: — 凉菜
JIT编译器:— 热菜(大菜)


热点代码: 一个被多次调用的方法,或者一个方法体内部循环次数较多的循环体。
hotSpot VM 采用热点探测的方式是基于计数器的热点探测。
针对热点代码,JIT编译器会启用。
client模式下:1500 次
server模式下: 10000 次 默认都是server模式 -----64-Bit Server
通过-XX:CompileThreshold 来认为设定

C:\Users\wei19>java -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

方法调用计数器

热度衰减

-XX:-UseCounterDecay

-XX:CounterHalfLifeTime

回边计数器

循环体代码执行的次数

P116-Hotspot设置模式_C1与C2编译器 15:20

java -Xint -version

int=interpreted

java -Xcomp -version

comp=compiled

java -Xmixed -version

C:\Users\wei19>java -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)C:\Users\wei19>java -Xint -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, interpreted mode)C:\Users\wei19>java -Xcomp -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, compiled mode)C:\Users\wei19>java -Xmixed -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)


测试三种方式的代码我就不写了,详见:
https://www.bilibili.com/video/BV1PJ411n7xZ?p=116

client server

hotSpot 默认有两个JIT编译器
-client
-server ---- 64 位的系统默认就是server版的,设置client也会被忽略掉。
请客的例子

解释器 ----- 凉菜
JIT编译器 ----- 热菜 (如烤鸭)

client模式 ----- 热菜 烤鸭 快速模式 快一些
server模式 ----- 热菜 烤鸭 经典模式 慢一些吃起来效果更好

P117-Graal编译器与AOT编译器 07:41

Graal编译器

JDK10 后 Hotspot加入了全新的即时编译器 Graal编译器
编译效果等同于C2编译器

c1
c2
Graal

AOT编译器

AOT: Ahead Of Time Compile
JDK9中引入,

解释器
JIT编译器
AOT编译器

13 StringTable

P118-String的不可变性 21:34

20210113
String 实例化方式两种:

String s1=“tiza”;
String s2=new String(“tiza”);

jdk9 及以后有个比较大的变化是,String的构成由 char[] 变为 byte[] ,jeps254是对此项变化的说明。 http://openjdk.java.net/jeps/254

jdk8 –

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[];

jdk9 +

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {private final byte[] value;
}

1.字符串重新赋值
2.字符串加值
3.字符串替换值 三个都会重新指定内存区域赋值

    @Testpublic void test(){// 通过字面量的方式定义,如下:s1,s2 都执行字符串常量池的"abc"(jdk7 8是在堆空间 ; jdk 6 在方法区)String s1="abc";String s2 ="abc";System.out.println(s1==s2);  //true// 通过 new ,放在不同区域String str1=new String("abc");String str2=new String("abc");System.out.println(str1==str2);  //false}

P119-String底层Hashtable结构的说明 15:57

字符串常量池中是不会存储相同内容的字符串的。

jps
jinfo -flag StringTableSize **
-XX:StringTableSize=10

jdk 6

C:\Users\wei19>jps
11232 Jps
18644 RemoteMavenServer
12168 StringTest2
13256 Launcher
11324C:\Users\wei19>jinfo -flag StringTableSize 12168
-XX:StringTableSize=1009

jdk 7 8

C:\Users\wei19>jps
8032 Launcher
18644 RemoteMavenServer
3924 Jps
3208 StringTest2
11324C:\Users\wei19>jinfo -flag StringTableSize 3208
-XX:StringTableSize=60013

-XX:StringTableSize=10

C:\Users\wei19>jps
17760 StringTest2
18644 RemoteMavenServer
7464 Launcher
10124 Jps
11324C:\Users\wei19>jinfo -flag StringTableSize 17760
-XX:StringTableSize=10

P120-String内存结构的分配位置 09:46

String的内存分配
8种基本类型及String,系统提供了常量池。访问速度更快,更节省内存。

package com.tiza.jvm.chapter13;import java.util.HashSet;
import java.util.Set;/*** jdk6中* -XX:PermSize=6m  -XX:MaxPermSize=6m -Xms6m -Xmx6m*  java.lang.OutOfMemoryError: PermGen space** jdk7 8 中* -XX:MetaspaceSize=6m -XX:MaxMetaspaceSize=6m -Xms6m -Xmx6m*java.lang.OutOfMemoryError: GC overhead limit exceeded** @author leowei* @date 2021/1/13  - 22:39*/
public class StringTest3 {public static void main(String[] args) {Set<String> set = new HashSet<String>();short i=0;while (true){set.add(String.valueOf(i++).intern());   //intern()  在字符串常量池中分配}}
}

P121-两个案例熟悉String的基本操作 11:20

package com.tiza.jvm.chapter13;/*** @author leowei* @date 2021/1/13  - 23:09*/
public class Memory {public static void main(String[] args) {int i=1;Object obj = new Object();Memory mem = new Memory();mem.foo(obj);}private void foo(Object param){String str = param.toString();System.out.println(str);}
}

P122-字符串拼接操作的面试题讲解 14:01

package com.tiza.jvm.chapter13;import org.junit.Test;/*** @author leowei* @date 2021/1/13  - 23:28*/
public class StringTest5 {@Testpublic void test2(){String s1="javaEE";String s2="hadoop";String s3="javaEEhadoop";String s4="javaEE"+ "hadoop";   // 编译期 优化String s5=s1+"hadoop";         // 如果拼接字符串出现变量 ,相当于在堆空间New String()String s6="javaEE"+ s2;String s7=s1+s2;System.out.println(s3==s4);  // trueSystem.out.println(s3==s5);  // 只要有变量就放在(非字符串常量池的)堆中 所以 falseSystem.out.println(s3==s6);  // false 同上System.out.println(s3==s7);   // false 同上System.out.println(s5==s6); // false 同上System.out.println(s5==s7); // false 同上System.out.println(s6==s7); // false 同上System.out.println("----------------------");// intern 判断字符串常量池中是否存在 javaEEhadoop ,如果存在,返回常量池中javaEEhadoop的地址//如果字符串常量池中不存在javaEEhadoop,则在常量池中加载一份,并返回此此对象的地址String s8 = s6.intern();    //放到字符串常量池中System.out.println(s3==s8);  //trueSystem.out.println("=====================");String str1="a";String str2="b";String str3="ab";String str4= s1+s2;String str5= "a"+"b";System.out.println( str3==str4);   // false   str3 放在字符串常量表   str4 放在堆空间System.out.println( str3==str5);   // true     str5 在编译时候 自动变为  "ab" ;}
}

P123-字符串变量拼接操作的底层原理 17:21

    @Testpublic void test41(){String s1="a";String s2="b";String s3="ab";String s4=s1+s2;    // s4 相当于 new StringBuild().append("a").append("b");System.out.println(s3==s4);  //false}/*字符串拼接操作不一定使用StringBuilder如果拼接符号左右两边都是字符串常量或者常量引用 ,则仍然使用编译期优化,即非StringBuilder的方式 */@Testpublic void test42(){final String s1="a";       // final 定义的不能当做变量看,要当做常量看final String s2="b";String s3="ab";String s4=s1+s2;            // s4 相当于  "a" + "b"System.out.println(s3==s4);   //true}

 @Testpublic void test43(){String s1="javaEEhadoop";final String s4="javaEE";String s5 =s4+"hadoop";System.out.println(s1==s5);  // true   s4 用final 修饰,相当于 是个常量了}@Testpublic void test43() {String s1 = "javaEEhadoop";String s4 = "javaEE";String s5 = "javaEEhadoop";System.out.println(s1 == s5);}

P124-拼接操作与append操作的效率对比 10:01

public class StringTest5 {public static void main(String[] args) {long timeStart = System.currentTimeMillis();StringTest5 stringTest5 = new StringTest5();// stringTest5.methodByString(100000);              //用时: 5174stringTest5.methodByStringBuilder(100000);  //用时:  4long timeEnd = System.currentTimeMillis();System.out.println("用时:"+ (timeEnd-timeStart));}public void methodByString(int times){String str="";for (int i = 0; i < times; i++) {str= str+ "a";  // 每次循环都创建一个StringBuilder}}public void methodByStringBuilder(int times){StringBuilder str = new StringBuilder();   // new StringBuilder(times);for (int i = 0; i < times; i++) {str.append("a");}}
}

P125-intern()的理解 11:46

new String().intern() ; new String 可以使用intern() 方法。
intern方法会从字符串常量池中查询当前字符串是否存在,若不存在,会将当前字符串放入常量池中。

(“abc”).intern() = “abc”
(“a”+“b”+“c”).intern() =“abc”
任意字符串调用intern() 方法的返回结果。
和以常量形式出现的字符串示例,
结果相同
Intern()方法,确保当前字符串在(堆空间)下的字符串常量池中只有一份拷贝。
可以节约空间,加快执行速度。

 * 如何保证变量S指向的字符串常量池中的数据呢?* 有两种方式:* 方式一:  String s ="wltest";  //字面量的方式* 方式二:  String s =new String("wltest").intern();*          String s =new StringBuilder("wltest").toString().intern();
   /*0 new #2 <java/lang/String>3 dup4 ldc #3 <ab>6 invokespecial #4 <java/lang/String.<init>>9 astore_110 return*///这个语句做了什么//String str = new String("ab");String str2 = new String("ab").intern();/*0 new #2 <java/lang/String>3 dup4 ldc #3 <ab>6 invokespecial #4 <java/lang/String.<init>>9 invokevirtual #5 <java/lang/String.intern>12 astore_113 return*///jdk6//  1. 堆空间创建一个对象  ab//  2. 堆空间字符串常量池中创建一个对象  ab//jdk7 / 8//  1. 堆空间创建一个对象  ab//  2. 堆空间字符串常量池中创建一个对象(其存放地址) 地址其指向堆空间中的ab

P126-new String()到底创建了几个对象 12:25


package com.tiza.jvm.str_intern;/*** @author leowei* @date 2021/1/15  - 7:26* String str = new String("ab");* new String("ab") 会创建几个对象?*     两个对象 一个是:new 关键字在堆空间创建的*              另一个是 字符串常量池中的对象**  String strPlus =new String("a")+ new String("b"); 创建了几个对象?*  对象1:  new StringBuilder()*  对象2:   new String("a");*  对象3:  常量池中的"a" ;*  对象4:   new String("b");*  对象5:  常量池中的"b" ;*  深入剖析 StringBuilder 的 toString()*      对象6  new String("ab");*      强调一下,toString() 的调用,在字符串常量池中没有 ab*/
public class StringNewTest {public static void main(String[] args) {/*  String str = new String("ab");*/String strPlus =new String("a")+ new String("b");}
}

package com.tiza.jvm.str_intern;/*** @author leowei* @date 2021/1/15  - 7:57**  String s = new String("1");*  1. 堆空间(常量池)中的 1   ,2.栈空间 的地址*  s.intern();*  调用此防范前常量池中已经存在了1 ,什么也不做*  String s2="1";*  s2 是 常量池中的地址*/
public class StringIntern {public static void main(String[] args) {String s = new String("1");s.intern();String s2 = "1";System.out.println(s == s2);  //JDK7/8 :  false  JDK6:  FALSE/*  String s3 = new String("1") + new String("1");s3.intern();String s4 = "11";System.out.println(s3 == s4);  //JDK7/8 :  TRUE  JDK6:  FALSE*/}
}

P127-关于intern()的面试难题 13:40

/*** @author leowei* @date 2021/1/15  - 7:57** 如何保证变量S指向的字符串常量池中的数据呢?* 有两种方式:* 方式一:  String s ="wltest";  //字面量的方式* 方式二:  String s =new String("wltest").intern();*          String s =new StringBuilder("wltest").toString().intern();**  String s = new String("1");*  1. 堆空间中的开辟一个区域存放“1”  ,字符串常量池中开辟一个区域放“1”  (6中放永久代  7,8 放堆中)*  s.intern();*  堆空间字符串常量池中找是否有“1” ,(当前是有)什么也不做*  String s2="1";*  s2 是 常量池中的地址*/
public class StringIntern {public static void main(String[] args) {String s = new String("1");s.intern();  //调用此方法之前 字符串常量池中已经有1了,所以什么也不做String s2 = "1";System.out.println(s == s2);  //JDK7/8 :  false  JDK6:  FALSE/*  String s3 = new String("1") + new String("1");s3.intern();String s4 = "11";System.out.println(s3 == s4);  //JDK7/8 :  TRUE  JDK6:  FALSE*/}
}




package com.tiza.jvm.str_intern;/*** @author leowei* @date 2021/1/15  - 7:57** 如何保证变量S指向的字符串常量池中的数据呢?* 有两种方式:* 方式一:  String s ="wltest";  //字面量的方式* 方式二:  String s =new String("wltest").intern();*          String s =new StringBuilder("wltest").toString().intern();**  String s = new String("1");*  1. 堆空间中的开辟一个区域存放“1”  ,字符串常量池中开辟一个区域放“1”  (6中放永久代  7,8 放堆中)*  s.intern();*  堆空间字符串常量池中找是否有“1” ,(当前是有)什么也不做*  String s2="1";*  s2 是 常量池中的地址*/
public class StringIntern {public static void main(String[] args) {/*  String s = new String("1");s.intern();  //调用此方法之前 字符串常量池中已经有1了,所以什么也不做String s2 = "1";System.out.println(s == s2);  //JDK7/8 :  false  JDK6:  FALSE*/String s3 = new String("1") + new String("1");   // s3 变量的地址: new String("11")//执行完上一行代码后,字符串常量池是否存在 11  ,答  没有// 原因是  s3 地址是 StringBuilder 中的 toString 方法的new String() ,此new String () 比较特殊// ,只会执行在堆中分配 , 不会执行在字符串常量池中分配s3.intern();         //在字符串常量池中生成  11// jdk6:  创建一个新的对象“11”,有了新的地址// jdk7:  此时常量池中没有创建11 ,而是创新了一个指向对空金new String("ss")的地址 。String s4 = "11";    //s4变量记录的地址:使用的是上一行代码执行时,在常量池中生成的"11"的地址System.out.println(s3 == s4);  //JDK7/8 :  TRUE  JDK6:  FALSE}
}

P128-面试的拓展问题 06:21

package com.tiza.jvm.str_intern;/*** Author: tz_wl* Date: 2021/1/15 13:46* Content:*/
public class StringIntern02 {public static void main(String[] args) {method1();method2();}private static void method1() {String s3 = new String("1") + new String("1");//创建有stringbuilder 堆空间 “1” 字符串常量池“1”  堆空间 “1” 字符串常量池“1”  堆空间“11”s3.intern();//判断“11” 在字符串常量池中是否存在“11” ,当前不存在,所以要在 字符串常量池中开启一个空间放“11”//jdk6 中 在常量池中  开辟一个区域放“11”//jdk7 8  常量池中   开辟一个区域 (由于堆中有了“11”) 放一个指向堆空间 “11” 的地址 (想想如果变量未1000个字符,此处指用4位引用地址就可以了,为了节省空间)String s4 = "11";System.out.println(s3==s4);  //jkd 6 false    jdk  7 8  true}private static void method2() {String s3 = new String("1") + new String("1");//创建有stringbuilder 堆空间 “1” 字符串常量池“1”  堆空间 “1” 字符串常量池“1”  堆空间“11” ,常量池中不存在“11”  s3实际指向的是堆空间new StringBuilder(**)的地址String s4 = "11";         // 在字符串常量池中  开辟一个空间放 11  其地址放在s4 里面s3.intern();              //判断“11” 在字符串常量池中是否存在“11” ,当前存在,所以什么也不做//jdk 6     ://jdk 7 8   :String s5 = s3.intern();  // s5 指向 字符串常量池中的“11”   也就是s4指向的地址  System.out.println(s3==s4);  //jkd 6 false    jdk  7 8  falseSystem.out.println(s4==s5);  //jkd 6 true    jdk  7 8  true}}

String intern()使用的总结
jdk6中,将这个字符串对象尝试放入串池

如果串池中有,则不会放入,返回以后串池中的对象的地址
如果没有,把此对象复制一份,放入串池,并返回串池中的对象地址

jdk7开始,将这个字符串对象尝试放入串池。

如果串池中有,则不会放入,返回以后传池中的对象的地址
如果没有,把对象引用地址复制一份,翻入串池,并返回串池中的引用地址

总结就是 6 放真实数据 ; 7,8 放引用地址(目的是节省空间)

P129-intern()的课后练习1 08:05

package com.tiza.jvm.str_intern;/*** Author: tz_wl* Date: 2021/1/15 14:39* Content:*/
public class StringInternEx01 {//public static void main(String[] args) {method1();}private static void method1() {String s= new String("a")+ new String("b");   //s 存放的是 堆空间 中"ab" 的地址//上述代码           会在堆中new 一个对象放"ab",但是不会在字符串常量中开辟空间放"ab"// new String("ab") 会在堆中new 一个对象放"ab",   也会在字符串常量中开辟空间放"ab"String s2 = s.intern();//jdk6     查看 字符串常量池中是否有空间已经存放“ab” ,当前没有,会在字符串常量池中开启一个空间放"ab",并将其地址给s2//jdk7/8                                                 ,会在字符串常量池中开启一个空间,其存放堆中“ab”的地址System.out.println(s2=="ab");  // jkd6: true   jkd7/8: trueSystem.out.println(s=="ab");   // jkd6: false  jkd7/8: true}
}

package com.tiza.jvm.str_intern;/*** Author: tz_wl* Date: 2021/1/15 14:39* Content:*/
public class StringInternEx01 {//public static void main(String[] args) {method1();method2();}private static void method1() {String s= new String("a")+ new String("b");   //s 存放的是 堆空间 中"ab" 的地址//上述代码           会在堆中new 一个对象放"ab",但是不会在字符串常量中开辟空间放"ab"// new String("ab") 会在堆中new 一个对象放"ab",   也会在字符串常量中开辟空间放"ab"String s2 = s.intern();//jdk6     查看 字符串常量池中是否有空间已经存放“ab” ,当前没有,会在字符串常量池中开启一个空间放"ab",并将其地址给s2//jdk7/8                                                 ,会在字符串常量池中开启一个空间,其存放堆中“ab”的地址System.out.println(s2=="ab");  // jkd6: true   jkd7/8: trueSystem.out.println(s=="ab");   // jkd6: false  jkd7/8: true}private static void method2() {String x= "ab";String s= new String("a")+ new String("b");   //s 存放的是 堆空间 中"ab" 的地址//上述代码           会在堆中new 一个对象放"ab",但是不会在字符串常量中开辟空间放"ab"String s2 = s.intern();//jdk6     查看 字符串常量池中是否有空间已经存放“ab” ,当前  有,也就是x的内容 , s2 = x//jdk7/8                                                 ,会在字符串常量池中开启一个空间,其存放堆中“ab”的地址System.out.println(s2=="ab");  // jkd6: true   jkd7/8: trueSystem.out.println(s=="ab");   // jkd6: false  jkd7/8: false}
}

P130-intern()的课后练习2 04:04

package com.tiza.jvm.str_intern;/*** Author: tz_wl* Date: 2021/1/15 15:13* Content:*/
public class stringInternEx02 {public static void main(String[] args) {mehtod1();mehtod2();}private static void mehtod1() {// s1 地址是堆空间"ab" 的地址,此时 字符串常量池中,没有"ab"String s1= new String("a")+ new String("b");s1.intern();// jdk 6   时候 : 此时将在字符串常量池中开辟一个空间,放入"ab"// jdk 7/8 时候 : 此时将在字符串常量池中开辟一个空间,放入 堆空间中 “ab” 的地址, 也就是栈中地址指向堆“ab” ,字符串常量池中地址指向堆中“ab" String s2="ab";   //s2 是字符串常量池中"ab" 的地址System.out.println(s1==s2);   //jdk6 false  ;  jdk7/8 true}private static void mehtod2() {// s1 地址是堆空间"ab" 的地址,此时 字符串常量池中有"ab"String s1 = new String("ab");s1.intern();  // 此时字符串常量池中有了“ab” 所以什么也不做String s2="ab";   //s2 是字符串常量池中"ab" 的地址System.out.println(s1==s2);   //jdk6 false ;  jdk7/8 false  }
}

P131-intern()的空间效率测试 12:31

arr[i] = new String(String.valueOf(data[i % data.length])); //用时:7075

arr[i]= new String(String.valueOf(data[i%data.length])).intern(); //用时:2280

package com.tiza.jvm.str_intern;/*** Author: tz_wl* Date: 2021/1/15 15:29* Content:  使用intern 测试执行效率** 使用jvisualvm 查看  抽样器  内存**/
public class StringInternSpaceTest {static final int MAX_COUNT = 1000 * 10000;    //  1000万static final String[] arr =new String[MAX_COUNT];public static void main(String[] args) {Integer[] data = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};long start = System.currentTimeMillis();for (int i = 0; i < MAX_COUNT; i++) {arr[i] = new String(String.valueOf(data[i % data.length]));          //用时:7075//arr[i]= new String(String.valueOf(data[i%data.length])).intern();     //用时:2280}long end = System.currentTimeMillis();System.out.println("用时:"+(end-start));try {Thread.sleep(100000);} catch (InterruptedException e) {e.printStackTrace();}}
}

使用JProfiler 9.2.1 进行测试

jProfiler地址:https://blog.csdn.net/wei198621/article/details/112109608

结论:

P132-StringTable的垃圾回收测试 05:32

PrintStringTableStatistics 参数的使用

String 的垃圾回收:

  • -Xms15m -Xmx15m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails
package com.tiza.jvm.str_intern;/*** Author: tz_wl* Date: 2021/1/15 16:26* Content:** jdk 1.8** String 的垃圾回收:* -Xms15m -Xmx15m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails**/
public class StringInternGCTest {public static void main(String[] args) {for (int i = 0; i < 100; i++) {String.valueOf(i).intern();// valueof ()  -->  Integer.toString(i);  -->   return new String(buf, true);}}
}

20210115

P133-G1垃圾收集器的String去重操作 08:38

去重指的是去除堆空间中的相同值
官方去重说明文档:
http://openjdk.java.net/jeps/192

14 垃圾回收概述

P134-垃圾回收相关章节的说明 08:18

P135-什么是GC,为什么需要GC 19:45

垃圾回收的三个问题

哪些内存需要回收
什么时候回收
如何回收

垃圾是指,运行程序中没有任何指针指向的对象。
内存溢出:没有空间被新的对象使用,就出现内存溢出了
内存泄漏:内存空间已经不使用了,但是进行回收的时候,无法回收相应的内存

P136-了解早期垃圾回收行为 04:08

P137-Java自动内存管理介绍 08:11

java自动进行内存分配及内存回收,这样可以降低内存泄漏和内存溢出的风险
把程序员从繁重的内存管理中释放出来,可以更加专注业务开发。
对于java开发人员来讲,自动的内存管理是个黑匣子,一旦出现内存溢出,无法快速定位问题和解决问题。
这样就需要添加必要的监控和调节工具。

堆空间是垃圾回收器的重点区域。

频繁回收YOUNG
较少回收OLD
基本不动perm(元空间)

15 垃圾回收相关算法

标记阶段:

引用计数算法
可达性分析算法

清除阶段:

标记-清除算法
复制算法
标记-压缩算法
分代收集算法
增量收集算法
分区算法

哪些是垃圾,如何回收;
标记阶段,清除阶段;

P138-垃圾回收相关算法概述 09:17

标记阶段:

引用计数算法
可达性分析算法

P139-引用计数算法的原理及优缺点 13:47

引用计数器算法有个严重的问题,无法处理循环引用的问题,这是个致命的缺陷,导致JAVA垃圾回收器没有使用此算法

P140-Java代码举例_Python的引用计数实施方案 08:25

package com.tiza.jvm.chapter15;/**** -XX:+PrintGCDetails* @author leowei* @date 2021/1/16  - 7:13*/
public class RefCountGC {private byte[] bigSize =new byte[5*1024*1024]; // 5MObject reference=null;public static void main(String[] args) {//MethodWithoutGC();MethodWithGC();}private static void MethodWithoutGC() {RefCountGC obj1 = new RefCountGC();RefCountGC obj2 = new RefCountGC();obj1.reference =obj2;obj2.reference =obj1;obj1 = null;obj2=null;}private static void MethodWithGC() {RefCountGC obj1 = new RefCountGC();RefCountGC obj2 = new RefCountGC();obj1.reference =obj2;obj2.reference =obj1;obj1 = null;obj2=null;System.gc();// obj1  obj2 存在循环引用问题,如果使用的是标记清除算法,无法及时回收,// 但是单效果是由回收的,所以反证java平台没有使用垃圾回收算法}
}

P141-可达性分析算法与GC Roots 12:41

可达性分析算法也叫做 根搜索算法、追踪性垃圾收集算法
Java\C# 用的都是(Tracing Garbage Collection )追踪性垃圾收集算法。
GC Roots : 跟对象,一组活跃的引用

Java 语言中哪些放在GC Roots ?
如果要使用可达性分析算法来判断内存是否可以回收,需要此分析工作在一个能够保障一致性的快照中进行。
因为有一致性的要求必须“Stop The World” —STW ,没有不停顿的可达性分析算法,即使是号称不会停顿的CMS收集器也需要STW。

P142-对象的finalization机制 18:34

当垃圾回收器发现没有任何一个引用指向一个对象,即:垃圾回收此对象之前,总会先调用这个对象的finalize()方法。
finalize() 方法允许在子类中被重写,用于在对象被回收时进行资源的释放。常用在finalize()方法中的操作有 : 关闭 文件、套接字、数据库连接等
虚拟机中的对象有三种状态:可触及、可复活、不可触及

P143-代码演示可复活的对象 07:37

package com.tiza.jvm.chapter15;/*** @author leowei* @date 2021/1/16  - 9:21*/
public class CanReliveObj {private static CanReliveObj obj;//注释,或者取消注释,看看打印的区别// finalizer 只能执行一次 @Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("重写finalize()方法");obj =this;}public static void main(String[] args) {obj =new CanReliveObj();try {//对象第一次删除空自己obj=null;System.gc();System.out.println(" first time doing gc");//finalizer线程的优先级比较低,暂停2秒,等待它被执行Thread.sleep(2000);if(obj==null){System.out.println("obj is dead ");}else {System.out.println("obj is still alive");}//对象第二次删除空自己obj=null;System.gc();System.out.println(" second time doing gc ");if(obj==null){System.out.println("obj is dead ");}else {System.out.println("obj is still alive");}} catch (InterruptedException e) {e.printStackTrace();}}
}

P144-使用MAT查看GC Roots 13:42

MAT: Memory Analyzer Tool ,Java 堆内存分析器,用于查找内存泄漏以及查看内存消耗情况。
MAT基于ECLIPSE开发,是免费的性能分析工具,地址: http://www.eclipse.org/mat

mat 打开之前生成的.hprof 文件

P145-使用JProfiler进行GC Roots溯源 06:38



P146-使用JProfiler分析OOM 03:32

package com.tiza.jvm.chapter15;import java.util.ArrayList;/*** @author leowei* @date 2021/1/16  - 11:16* -Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError** https://www.bilibili.com/video/BV1PJ411n7xZ?p=146**/
public class HeapOOM {byte[] buffer = new byte[1*1024*1024];  // 1MBpublic static void main(String[] args) {ArrayList<HeapOOM> list = new ArrayList<HeapOOM>();int count=0;try {while (true){list.add(new HeapOOM());count++;}} catch (Exception e) {System.out.println("count="+ count);e.printStackTrace();}}
}

P147-标记-清除算法原理及优缺点 16:08

JVM中常用的三种垃圾收集算法:

Mark-Sweep 标记-清除算法
Copying 复制算法
Mark-Compact 标记压缩算法

标记-清除算法 McCarthy 等人1960年就提出了这种算法,并应用于Lisp语言。

注意,标记的是可达对象(非清除对象)
标记清除算法的优点:
标记清除算法的缺点:

效率不算高
在进行GC的时候,要停止整个运行的应用程序,导致用户体验差
清理出来的内存是不连续的,产生内存碎片(见上图),需要维护一个空闲列表。

P148-复制算法原理及优缺点 14:01

复制算法适用于,朝生夕死的,也就是留存率低的(copy对象少)
优点:

没有标记,清除过程,实现简单,运行高效
没有碎片问题
缺点:
需要两倍的内存空间
G1这种拆分大量region的GC,对象的位置要变,需要改变栈引用地址,开销不小。

P149-标记-压缩算法原理及优缺点 11:16

标记整理算法(标记压缩算法、标记清除压缩算法),是在标记清除算法的基础上,优化的结果
标记压缩是个移动式的算法

优点:

无内存碎片化的问题
消除了复制算法,内存减半的问题
缺点:
效率低于复制算法
移动对象的同时,需要调整引用地址
移动过程中,需要全程暂停用户应用程序,STW (所有三个算法都有次问题)

指针碰撞 (Bump the Pointer)

P150-不同指标上对比三种算法 04:38

相对来说 标记-压缩算法 更均衡一些,

P151-分代收集算法的说明 12:36

P152-增量收集算法原理及优缺点 09:14

一大块 分成很多小块 ,交替收集垃圾

P153-分区算法的说明 03:59

G1 使用分区算法

16 垃圾回收相关概念

System.gc()
内存溢出
内存泄漏
STW: Stop The Word
垃圾回收的并行与并发
安全点与安全区域
引用:

强引用
软引用
弱引用
虚引用
终结器引用

P154-垃圾回收相关概念的概述 10:11

P155-System.gc()的理解 08:47

System.gc() 触发的是Full GC .
System.gc() 无法保证对垃圾收集器的调用
System.gc() 只是提醒垃圾回收器触发垃圾回收,一般无需手动触发。

package com.tiza.jvm.chapter16;/*** @author leowei* @date 2021/1/16  - 13:39*/
public class SystemGCTest {public static void main(String[] args) {new SystemGCTest();System.gc();  // 提醒JVM 执行垃圾收集行为,但是不能确定,是否马上执行//System.gc()  调用   Runtime.getRuntime().gc();// System.runFinalization();  //强制调用失去引用对象的finalize方法 }@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("SystemGCTest 重写了  finalizer ()  方法 ");}
}

P156-手动gc理解不可达对象的回收行为 10:18

package com.tiza.jvm.chapter16;/*** @author leowei* @date 2021/1/16  - 15:17** -XX:+PrintGCDetails* https://www.bilibili.com/video/BV1PJ411n7xZ?p=156**/
public class LocalVarGC {public void localVarGC1() {//回收不了byte[] buffer = new byte[10 * 1024 * 1024];  // 10MSystem.gc();}public void localVarGC2() {byte[] buffer = new byte[10 * 1024 * 1024];buffer=null;System.gc();}public void localVarGC3() {{byte[] buffer = new byte[10 * 1024 * 1024];  //没有被释放掉}System.gc();}public void localVarGC4() {{byte[] buffer = new byte[10 * 1024 * 1024];}int value=10;  // 由于这步  垃圾被回收System.gc();}public void localVarGC5() {localVarGC1();System.gc();   //被回收}public static void main(String[] args) {LocalVarGC local = new LocalVarGC();local.localVarGC5();}
}

P157-内存溢出的分析 11:40

内存溢出 (OOM Out Of Memory )
内存泄漏

javadoc中对OutOfMemoryError的解释是:没有空闲内存,并且垃圾收集器也无法提供更多的内存。

P158-内存泄漏的分析 13:04

内存泄漏: Memory Leak,也叫做存储渗漏。对象不再被程序用到,但是GC由不能回收它们。
1.单例模式
2.与外部资源关联,就需要手动关闭关联:

P159-StopTheWorld事件的理解 10:57

STW: Stop-the-World
GC时间发生时,产生应用程序的停顿,程序没有任何响应,像卡死一样。
警察办案,要现场保持不要有人随意漏洞
老妈打扫房屋,不要再丢垃圾了。
所有的GC,都有STW
CMS G1 等垃圾回收器都有STW ,
STW是JVM在后台自动发起和自动完成的。

https://www.bilibili.com/video/BV1PJ411n7xZ?p=159

P160-程序的并行与并发 06:33

并发Concurrent: 同一个处理器,同一个时间段,多个程序看似“同时运行”。
并行 Paraller: 多个CPU,真正的同时执行。

P161-垃圾回收的并行与并发 03:39

并行 Parallel:
串行 Serial :
并发 Concurrent : 用户线程与垃圾收集线程同时执行 (注意不是并行)。

P162-安全点与安全区域的说明 09:01

SafePoint 安全点:
程序在运行的时候,并不是任何时间点都能停顿下来的,在可以停顿的位置叫做安全点“safePoint”
抢先式中断:
主动式中断:
safe Region 安全区域

P163-Java中几种不同引用的概述 10:54

需求: 内存够,保留;内存不够,抛弃;----如缓存。
强引用、软引用、弱引用、虚引用 有什么区别?

如下四种,强软弱虚 使用强度一次递减。 继承 java.lang.ref.Reference
强引用: Stong Reference
软引用: Soft Reference
弱引用: Weak Reference
虚引用: Phantom Reference

强引用(Strong Reference): <引用关系在>, 死也不回收
软引用(Soft Reference):<引用关系在>, 内存不足则回收
弱引用(Weak Reference):<引用关系在>,发现即回收
虚引用(Phantom Reference):,

P164-强引用:不回收 06:35



package com.tiza.jvm.chapter16;/*** @author leowei* @date 2021/1/16  - 20:38*/
public class StrongReferenceTest {public static void main(String[] args) {// method01();//method02();method03();}private static void method01() {StringBuffer str = new StringBuffer("hello tiza!");System.gc();   // 由于gc不一定立即执行所以需要有个3秒的延时try {Thread.sleep(3000);      //} catch (InterruptedException e) {e.printStackTrace();}System.out.println(str);  // hello tiza!}private static void method02() {StringBuffer str = new StringBuffer("hello tiza!");str=null;System.gc();   // 由于gc不一定立即执行所以需要有个3秒的延时,本示例实测不sleep也没有问题,正常回收try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(str);  //null}private static void method03() {StringBuffer str = new StringBuffer("hello tiza!");  // str  指向堆空间分配的StringBuffer地址StringBuffer str1=str;                               // str1 指向堆空间分配的StringBuffer地址str=null;System.gc();   // 由于gc不一定立即执行所以需要有个3秒的延时try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(str);// nullSystem.out.println(str1);// hello tiza!}
}

P165-软引用:内存不足即回收 16:30

软引用示例: 缓存
当内存足够时,不会回收软引用的可触及对象;
当内存不足时,会回收软引用的可触及对象。

package com.tiza.jvm.chapter16;import java.lang.ref.SoftReference;/*** @author leowei* @date 2021/1/16  - 20:57*/
public class SoftReferenceTest {public static class User{public int id;public String name;public User(int id, String name) {this.id = id;this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}}public static void main(String[] args) {//method01();//method02();method03();}// 最基本的写法,不涉及垃圾回收private static void method01() {//创建对象,建立软引用   一步到位的写法,一行等于三行SoftReference<User> userSoftRef = new SoftReference<User>(new User(1, "leo"));//从软引用中重新获得强引用对象System.out.println(userSoftRef.get());//User{id=1, name='leo'}}// 垃圾回收前  垃圾回收后 看效果  由于堆空间足够大,所以打印没有问题private static void method02() {//创建对象,建立软引用   一步到位的写法,一行等于三行SoftReference<User> userSoftRef = new SoftReference<User>(new User(1, "leo"));//从软引用中重新获得强引用对象System.out.println(userSoftRef.get());//User{id=1, name='leo'}System.gc();System.out.println("After GC:");System.out.println(userSoftRef.get());//User{id=1, name='leo'}}// 如果设置参数   -Xms10m -Xmx10m  -XX:+PrintGCDetails     ,  新生代 : 老年代 =  1:2   =  3.3  :  6.7  ;// 新生代 只有3.3M  再分成  8:1:1 ,不够放7M 空间的private static void method03() {//创建对象,建立软引用   一步到位的写法,一行等于三行SoftReference<User> userSoftRef = new SoftReference<User>(new User(1, "leo"));//从软引用中重新获得强引用对象System.out.println(userSoftRef.get());  //User{id=1, name='leo'}System.gc();System.out.println("After GC:");System.out.println(userSoftRef.get());  //User{id=1, name='leo'}   //此时堆空间内存充足try {// 配合 “参数  -Xms10m -Xmx10m” 的设置,让系统认为资源紧张,执行垃圾回收Byte[] b = new Byte[7 * 1024 * 1024  ];  //7M} catch (Exception e) {// java.lang.OutOfMemoryError: Java heap spacee.printStackTrace();   //此处会报错,报错之前会执行gc ,gc 会回收 软引用  userSoftRef}finally {// 在报OOM之前,垃圾回收器会回收软引用的可达对象。System.out.println(userSoftRef.get());   //所以 Null}}
}

P166-弱引用:发现即回收 08:02

三级缓存

内存----一级
本地(硬盘)----二级
网络----三级


WeakHashMap ,

package com.tiza.jvm.chapter16;import java.lang.ref.WeakReference;/*** @author leowei* @date 2021/1/16  - 21:40*/
public class WeakReferenceTest {public static class User{public int id;public String name;public User(int id, String name) {this.id = id;this.name = name;}@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +'}';}}public static void main(String[] args) {method01();}// 弱引用特点 ,触发即回收private static void method01() {//创建对象,建立弱引用   一步到位的写法,一行等于三行WeakReference<User> userSoftRef = new WeakReference<User>(new User(1, "leo"));//从弱引用中重新获得强引用对象System.out.println(userSoftRef.get());//User{id=1, name='leo'}System.gc();System.out.println("After GC:");System.out.println(userSoftRef.get());//null  不管内存空间是否充足,都会回收它 }
}

P167-虚引用:对象回收跟踪 13:29

虚引用 Phantom Reference --幽灵引用,幻影引用

package com.tiza.jvm.chapter16;import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;/*** @author leowei* @date 2021/1/16  - 21:58** 这个里面有详细的过程,太复杂了,没有实操* https://www.bilibili.com/video/BV1PJ411n7xZ?p=167**/
public class PhantomReferenceTest {public static void main(String[] args) {ReferenceQueue<PhantomReferenceTest> phantomQueue = new ReferenceQueue<PhantomReferenceTest>();PhantomReferenceTest obj = new PhantomReferenceTest();PhantomReference<PhantomReferenceTest> phantomRef = new PhantomReference<PhantomReferenceTest>(obj, phantomQueue);phantomRef.get();   //  强软弱虚  虚的什么也取不到数据 }
}

P168-终结器引用的介绍 01:45

FinalReference – 宋老师也没有讲,一笔带过

17 垃圾回收器

P169-垃圾回收器章节概览 05:07

P170-垃圾回收器的分类 15:31

垃圾收集器 在JVM规范中没有规定死,不同厂商有不同的实现。
JAVA不同版本新特性:

1.语法层面: Lambda表达式、switch、自动装箱拆箱、enum
2.API层面: Stream API、 Optional、 新的日期时间、集合框架、String
3.底层优化: JVM优化、GC优化、元空间的引入、静态域、字符串常量池

垃圾回收器分类

按线程数分类: 串行垃圾回收器、 并行垃圾回收器
按工作模式分类: 独占式垃圾回收器、 并发式垃圾回收器
按碎片处理方式: 压缩式垃圾回收器、 非压缩式垃圾回收器

单CPU使用串行垃圾回收器效果比并行的要好
对并发能力强的环境,用并行垃圾回收器效果好

P171-GC性能指标的整体说明 09:16

评估GC性能指标:

吞吐量: 运行用户代码时间/ (运行用户代码时间 + 内存垃圾回收时间 )
暂停时间: 执行垃圾收集器时,程序的工作线程被暂停的时间
内存占用: Java堆区所占内存大小

P172-吞吐量与暂停时间的对比说明 09:41

上面是 两三天 洗一次衣服
下面是 一天 洗一次衣服
暂停时间 pause time
用户交互的,关注的是低延迟,

P173-垃圾回收器的发展迭代史 17:06

Garbage Collection
Garbage Collector 垃圾回收器

java 垃圾回收器有哪些?

SerialGC
Parallel GC
CMS Concurrent Mark Sweep (jdk 1.4 )
G1 (JDK1.7) ----当前使用最多
Epsilon <No-Op 无操作> — (JDK11)
ZGC---------------------以后一定强推此
Shenandoah GC ------ (JDK12)

垃圾收集器发展史

7款经典垃圾收集器

串行回收器:Serial ; Serial Old ;
并行回收器:ParNew ; Parallel Scavenge ; Parallel Old
并发回收器:CMS ; G1 ;

https://www.oracle.com/technetwork/java/javase/tech/memorymanagement-whitepaper-1-150020.pdf

P174-垃圾回收器的组合关系 12:49

为什么需要这么多的收集器?
比如单CPU 用串行的 serial GC ,比用 Parallel Scavenge GC 效果更好,所以要选择适合自己的垃圾回收器。

P175-如何查看默认的垃圾回收器 06:22

-XX:_PrintCommandLineFlags
jinfo -flag

package com.tiza.jvm.chapter17;import java.util.ArrayList;/*** @author leowei* @date 2021/1/16  - 23:29* -XX:+PrintCommandLineFlags             ----- 1* jinfo -flag UseParallelGC 21476        ----- 2***
-XX:InitialHeapSize=266089344 -XX:MaxHeapSize=4257429504
-XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
-XX:-UseLargePagesIndividualAllocation
-XX:+UseParallelGC  ---- 显示使用的 垃圾回收器C:\Users\wei19>jps
1648
16592
12516 Launcher
19604 Jps
21476 GCUseTest
15576 Main
19560 jprofiler.exeC:\Users\wei19>jinfo -flag UseParallelGC 21476
-XX:+UseParallelGCC:\Users\wei19>jinfo -flag UseParallelOldGC 21476
-XX:+UseParallelOldGCC:\Users\wei19>jinfo -flag UseG1GC 21476
-XX:-UseG1GCC:\Users\wei19>**/
public class GCUseTest {public static void main(String[] args) {ArrayList<byte[]> list = new ArrayList<byte[]>();while (true){byte[] arr = new byte[100];list.add(arr);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}
}

P176-Serial与Serial Old垃圾回收器的介绍 08:54

Serial回收器: 串行回收器, JDK1.3之前回收新生代唯一的选择
新生代
Serial GC 收集器采用复制算法、串行回收和Stop-the-World机制执行内存回收
老年代
Serial Old GC 使用标记压缩算法

Serial回收器的优势: 对于单CPU环境,无线程切换,简单高效
HotSpot虚拟机中,使用-XX:+UseSerialG 指定年轻代、老年代都使用串行收集器,也就是说新生代用Serial GC; 老年代用 Serial Old GC

P177-如何设置使用Serial垃圾回收器 04:43

package com.tiza.jvm.chapter17;import java.util.ArrayList;/*** @author leowei* @date 2021/1/16  - 23:29** -XX:+PrintCommandLineFlags  -XX:+UseSerialGC* 新生代用Serial GC; 老年代用 Serial Old GC***/
public class GCUseTest {public static void main(String[] args) {ArrayList<byte[]> list = new ArrayList<byte[]>();while (true){byte[] arr = new byte[100];list.add(arr);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}
}

P178-ParNew垃圾回收器的介绍 07:22

ParNew:并行回收器 , 采用复制算法,STW机制,可以说ParNew是Serial收集器的多线程版本。
Par = Parallel ; New 表示只能处理新生代
ParNew 是JVM在Server模式下新生代的默认垃圾收集器。
-XX:+UseParNewGC 表示年轻代使用并行垃圾收集器,不影响老年代
-XX:ParallelGCThreads 限制线程数量,默认开启和CPU数据相同的线程数。

-XX:+UseConcMarkSweepGC ---- CMS

P179-如何设置使用ParNew垃圾回收器 03:58

P180-Parallel与Parallel Old垃圾回收器的介绍 08:56

Parallel Scavenge: 吞吐量优先策略
适合后台运算,无需太多交互的场景,如批量处理,科学计算等 。通常用于服务器端。
Parallel Scavenge: —新生代
Parallel Old GC : ----老年代 标记-压缩算法

-XX:+PrintCommandLineFlags
JDK8 默认Parallel -XX:+UseParallelGC
JDK9 默认 G1 -XX:+UseG1GC

P181-Parallel垃圾回收器的相关参数设置 17:29

-XX:+UseParallelGC ------- 相互激活,设置一个就可以了
-XX:+UseParallelOldGC ------- 相互激活,设置一个就可以了
-XX:ParallelGCThreads

-XX:MaxGCPauseMillis ----垃圾收集器最大停顿时间 STW时间
-XX:GCTimeRatio -----------衡量吞吐量值 (垃圾收集时间/(垃圾收集时间+用户程序运行时间))
-XX:+UseAdaptiveSizePolicy -----设置Parallel Scavenge收集器的自适用调节策略

P182-CMS垃圾回收器概述与工作原理 12:45

CMS回收器:低延迟
JDK1.5时期,Hotspot推出的一款强交互应用。现在大部分应用在互联网B/S服务器上,这类服务器注重相应速度,希望系统停顿时间最短。CMS 采用 标记-清除算法,也会使用Stop-the-World
CMS= Concurrent-Mark-Sweep
CMS作为老年代回收器,配合使用Serial / ParaNew 回收新生代。

CMS的工作原理:

初始标记(Initial-Mark) ---------标记出GC Roots 对象
并发标记(Concurrent-Mark)------标记出GC Roots 关联的所有对象(耗时较长)
重新标记(Remark)------------------修正第二部的数据
并发清除(Concurrent-Sweep)----清理标记阶段所有对象,释放内存空间

P183-CMS的特点与弊端分析 15:58

CMS使用标记-清除算法,会产生一些内存碎片,无法使用指针碰撞(Bump the Pointer),只能选择空闲列表(Free List ) 执行内存分配。
标记清除有内存碎片,为什么不用标记压缩呢?
答:有并发处理,不可以用并行压缩改变内存地址,
CMS的优点:

并发收集
低延迟

CMS弊端:

产生内存碎片,不利于处理大对象。
CMS收集器堆CPU资源非常敏感
CMS收集器无法处理浮动垃圾 ,第二个阶段并发标记阶段用户线程产生的最新垃圾,叫做浮动垃圾。

设置参数:
-XX:+UseCMSCompactAtFullCollection —指定执行完Full GC 后,堆内存空间进行压缩整理,避免内存碎片的产生。不过停顿时间较长。
-XX:CMSFullGCsBeforeCompaction ---- 设置执行多少次 Full GC 后,才对内存空间进行压缩整理。
-XX:ParallelCMSThreads -----设置CMS线程数量

P184-CMS垃圾回收器的参数设置 09:06

设置参数:
-XX:+UseConcMarkSweepGC —使用CMS(用于老年代) 自动触发 ParNew 用于新生代
-XX:CMSInitiatingOccupanyFraction — 提前进行垃圾回收设置 (68%–JDK5)(92%–JDK6)
-XX:+UseCMSCompactAtFullCollection —指定执行完Full GC 后,堆内存空间进行压缩整理,避免内存碎片的产生。不过停顿时间较长。
-XX:CMSFullGCsBeforeCompaction ---- 设置执行多少次 Full GC 后,才对内存空间进行压缩整理。
-XX:ParallelCMSThreads -----设置CMS线程数量

P185-CMS的小结及后续JDK版本中的变化 03:45

口令:
最小化内存: Serail GC
最大化吞吐量: Parallel GC
最小化中断(低延时): CMS

JDK9 开始废弃 CMS ; JDK14 删除CMS .

P186-认识G1垃圾回收器 14:52

G1:区域分代化 (G1: G First )
G1的目标:在延迟可控的情况下,获得尽可能高的吞吐量,希望达到“全功能收集器”的目标。但是后期ZGC会替换G1;
为什么名字叫G1 (G First)
G1是个并行的回收器,把堆内存分割为很多不相关的区域(Region-物理上不连续的)
G1的侧重点在于回收垃圾最大的区间(Region),所以我们给G1取了一个名字垃圾优先(Garbage First)

G1:主要针对多核处理器及大容量内存的适用场景
JDK9以后默认垃圾回收器为G1

P187-G1垃圾回收器的优势和不足 20:24

G1回收器特点

1 并行与并发

并行性 —多个线程同时工作,STW
并发性 —

2 分代收集

G1 会区分年轻代、老年代 ,但是G1由于有Region,G1不要求区域连续。

3 空间整合

Region内部适用的是复制算法,整体上看是标记压缩算法,所以不存在内存碎片。

4 可预测的停顿时间模型 (soft real-time 软实时)

由于分区,G1可以选择部分区域进行回收,缩小了回收范围,对全局停顿有较好的控制。
每次回收价值最大的Region

G1的缺点:

G1 占用额外的内存空间,内存在6-8 G 以上使用G1好, 小于6-8G ,CMS 更好,


P188-G1的参数设置 09:12

-XX:+UseG1GC ----- 手动指定G1用于垃圾回收
-XX:G1HeapRegionSize ----设置每个Region大小
-XX:MaxGCPauseMillis -----GC停顿时间指标(默认200ms)
-XX:ParallelGCThread -------STW工作时GC线程值
-XX:ConcGCThreads ----设置并发标记的线程数
-XX:InitiatingHeapOccupancyPercent --------设置触发并发GC周期的java堆占用率阈值,超过此值就触发GC,默认45

G1启动三步骤

1.开启G1垃圾收集器
2.设置堆的最大内存
3.设置最大停顿时间

P189-G1在生产环境的适用场景 03:58

G1针对服务器端的应用,针对具有大内存、多处理器的机器,普通大小的堆了表现不好。
最主要应用于低延时,具有大堆应用。
G1可以替换CMS

P190-region的使用介绍 11:36

分区Region:化整为零
G1收集器,将JAVA堆划分为2048个大小相同的独立Region块,每个Region块的大小在1~32M,且为2的N次幂 。 1M,2M,4M,8M,16M,32M;
可以通过-XX:G!HeapRegionSize进行设定。

eden,survivor,old,humongous
对象大小大于1.5个Region大小是,使用humongous.
指针碰撞 Bump the pointer
TLAB

P191-G1垃圾回收器的主要回收环节 08:14

G1回收三个环节

年轻代GC Young GC
老年代并发标记过程 Concurrent Marking
混合回收 Mixed GC

P192-记忆集与写屏障 08:24

Remembered Set (R Set — 记忆集)

P193-G1垃圾回收过程的详细说明 24:16

P194-G1垃圾回收的优化建议 04:11

P195-7种经典的垃圾回收器总结与调优建议 14:02

没有最好的垃圾收集器,更没有万能的收集器
调优永远是针对特定场景、特定需求,不存在一劳永逸的收集器。

P196-常用的显示GC日志的参数 13:30

垃圾回收设置参数:

-verbose:gc
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeSTamps
-XX:+PrintGCDateStamps
-XX:+PrintHeapAtGC

P197-GC日志中垃圾回收数据的分析 09:03

DefNew
PSYoungGen
ParNew
ParOldGen
garbage-first heap

Allocation Failure
PSYoungGen

Minor GC
PSYoungGen:
Full GC

P198-举例说明日志中堆空间数据如何解读 10:16

P199-日志分析工具的使用 07:29

日志查看工具

GCViewer,
GCEasy — 在线官网 gceasy.io (建议使用)
GCHisto

P200-新时期的Epsilon和Shenandoah垃圾回收器 13:21

Serial GC
CMS GC —JDK9中废弃,JDK14版本中已经移除
G1 GC — 不断改进中 JDK9开始默认GC
Epsilon GC — http://openjdk.java.net/jeps/318
Shenandoah GC ----- RedHat 研发的
ZGC

P201-革命性的ZGC的性能介绍 09:03

https://docs.oracle.com/en/java/javase/12/gctuning
JDK14开始windows可以使用

P202-其他的厂商的垃圾回收器 01:41

AliGC
Zing GC

P203-最后寄语 09:42

尚硅谷 宋红康 JVM教程_01_内存与垃圾回收篇——02相关推荐

  1. 尚硅谷 宋红康 JVM教程_02_字节码与类的加载篇

    本系列相关链接 尚硅谷 宋红康 JVM教程_01_内存与垃圾回收篇--01 (20210103-20210110) https://blog.csdn.net/wei198621/article/de ...

  2. 尚硅谷-宋红康-JVM上中下篇完整笔记-JVM上篇_内存与垃圾回收篇

    前言 一.jvm及java体系结构 1. Java及JVM简介 TIOBE语言热度排行榜 https://www.tiobe.com/tiobe-index/ 世界上没有最好的编程语言,只有最适用于具 ...

  3. 尚硅谷-宋红康-JVM上中下篇完整笔记-JVM中篇

    一.Class文件结构 1.概述 1.1 字节码文件的跨平台性 所有的JVM全部遵守Java虚拟机规范:Java SE Specifications,也就是说所有的JV环境都是一样的,这样一来字节码文 ...

  4. JVM学习笔记汇总:结合尚硅谷宋红康老师视频教程及PPT

    JVM学习笔记汇总:结合尚硅谷宋红康老师视频教程及PPT 第一章:JVM虚拟机的介绍 1.1虚拟机的分类 虚拟机通常分为两类:系统虚拟机和程序虚机.其中,系统虚拟机是指完全对物理计算机的仿真,而程序虚 ...

  5. 尚硅谷-宋红康-MySQL高级性能篇

    尚硅谷-宋红康-MySQL高级性能篇 第1章 Linux下MySQL的安装与使用 1. 安装前说明 1.1 Linux系统及工具准备 二级目录 三级目录 第1章 Linux下MySQL的安装与使用 1 ...

  6. JDBC学习笔记(1)---B站尚硅谷宋红康

    JDBC学习笔记(1)-B站尚硅谷宋红康 JDBC学习笔记(2)-B站尚硅谷宋红康 文章目录 软件架构方式介绍 JavaWeb技术概览 第1章:JDBC概述 1.1 数据的持久化 1.2 Java中的 ...

  7. JVM上篇:内存与垃圾回收篇十四--垃圾回收器

    JVM上篇:内存与垃圾回收篇十四–垃圾回收器 1. GC分类与新能指标 1.1 垃圾回收器概述 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商.不同版本的JVM来实现. 由于JDK的版本处于高 ...

  8. JVM从入门到精通(尚硅谷宋红康)

    不动笔墨不读书,先把书读厚,再把书读薄是我学习方式. 所以等理解了再整理一次笔记,目前笔记和视频一一对应. 笔记连载中 <尚硅谷2020最新版宋红康JVM> 第1章:JVM与Java体系结 ...

  9. 尚硅谷2020最新版宋红康JVM教程更新至中篇(java虚拟机详解,jvm从入门到精通)

    目录 类的加载第一步----loading 加载".class"文件的方式: 类的加载第二步----linking![在这里插入图片描述](https://img-blog.csd ...

最新文章

  1. 关于模态对话框和非模态对话框的创建、显示,以及和父对话框的传值
  2. [转]URL汉字编码问题(及乱码解决)
  3. 使用 python-nmap 进行端口扫描
  4. zookeeper介绍及集群的搭建(利用虚拟机)
  5. python yaml
  6. Vuejs --01 起步
  7. 解决 【git checkout -b dev origin/dev】报错的问题
  8. mysql经典sql语句大全_经典SQL语句大全
  9. 数控机床通信协议汇总
  10. ORA-00600: internal error code, arguments: [2037]
  11. 打jar包和使用jar包
  12. 未来计算机的硬件发展趋势,整理计算机硬件发展史以及计算机硬件发展趋势
  13. 机器学习、数据挖掘、统计建模的技术担当,20款免费预测分析软件
  14. 基于RT-Thread系统的迷你时钟
  15. linux curl t上传文件,curl 命令行上传文件
  16. 突破限制,这类网站的仅在线视频也能轻松能下载了!
  17. 卡--配合--读卡器使用 磁卡 CPU卡 IC卡、ID卡、M1卡、射频卡区别 我在项目中使用到的S50卡(M1卡的一种)S50(Mifare 1K)卡简介及存储控制原理
  18. 单片机 STM32 HAL 步进电机 Motor
  19. PowerShell自动加载账号密码
  20. 身体和灵魂,总得有一个在路上

热门文章

  1. 优秀自我简介200字_自我介绍范文200字10篇
  2. 计算机网络怎么查看连接打印机驱动,如何查找打印机驱动的方法-电脑自学网...
  3. MATLAB中删除矩阵或向量中Nan数据
  4. 在Vue.data已定义的变量,运行时却找不到变量
  5. APP 信息管理平台——需求概述
  6. 《大道至简》第一章 编程的精义 伪代码
  7. Windows server 2003 R2之一:通过域控建立OU跟USER
  8. ADT打包本地扩展ANE指令
  9. 关于微积分的所有公式定理
  10. python爬虫---某站排名100