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

本系列相关链接

尚硅谷 宋红康 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
  •                             打印垃圾回收细节
    
Heap
 PSYoungGen      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 1056768K
  class space    used 358K, capacity 388K, committed 512K, reserved 1048576K

Metaspace

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

  • -Xms10m -Xmx10m -XX:+PrintGCDetails
  •                             打印垃圾回收细节
    
Heap
 PSYoungGen      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.10546875G

Process 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 space
	at 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 Jps

C:\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 NaN
 S0C    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>jps
 2464
 5904 Jps
 96 RemoteMavenServer
 10088 TLABArgsTest
 14360 Launcher

 C:\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.Serializable
  minor version: 0

//  02  域信息
  public int num;
    descriptor: I
    flags: ACC_PUBLIC

  private static java.lang.String str;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC
//03  方法信息    (包含构造器及方法 )
  public com.tiza.jvm.chapter09.MethodInnerStructTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
      ...
  public void test1();
    descriptor: ()V    // V =void
    flags: ACC_PUBLIC
    Code:   // stack=3  操作数栈的深度=3   局部变量表的长度=2   args_size=1 就是本身 this
      stack=3, locals=2, args_size=1     
      ...
  public static int test2(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      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: I
    flags: ACC_PUBLIC, ACC_STATIC

  public static final int number;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: 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];  // 100MB

    public static void main(String[] args) {
        System.out.println( StaticFieldTest.arr);
    }
    
    /*    JDK 6  7   打印结果   ----数据放在了   ParOldGen  这个空间内
    
    Heap
 PSYoungGen      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  这个空间内
  Heap
 PSYoungGen      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 1056768K
  class 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; // 1GB

    public 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 String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

jdk9 +

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

1.字符串重新赋值
2.字符串加值
3.字符串替换值 三个都会重新指定内存区域赋值
在这里插入图片描述

    @Test
    public 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
11324

C:\Users\wei19>jinfo -flag StringTableSize 12168
-XX:StringTableSize=1009

jdk 7 8

C:\Users\wei19>jps
8032 Launcher
18644 RemoteMavenServer
3924 Jps
3208 StringTest2
11324

C:\Users\wei19>jinfo -flag StringTableSize 3208
-XX:StringTableSize=60013

-XX:StringTableSize=10

C:\Users\wei19>jps
17760 StringTest2
18644 RemoteMavenServer
7464 Launcher
10124 Jps
11324

C:\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 {

    @Test
    public 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);  // true
        System.out.println(s3==s5);  // 只要有变量就放在(非字符串常量池的)堆中 所以 false
        System.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);  //true

        System.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

在这里插入图片描述

    @Test
    public 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的方式 
     */
    @Test
    public 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
    }

在这里插入图片描述

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

    @Test
    public 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);              //用时: 5174
        stringTest5.methodByStringBuilder(100000);  //用时:  4
        long 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 dup
          4 ldc #3 <ab>
          6 invokespecial #4 <java/lang/String.<init>>
          9 astore_1
          10 return
          */

        //这个语句做了什么
        //String str = new String("ab");
        String str2 = new String("ab").intern();
        /*
        0 new #2 <java/lang/String>
        3 dup
        4 ldc #3 <ab>
        6 invokespecial #4 <java/lang/String.<init>>
        9 invokevirtual #5 <java/lang/String.intern>
        12 astore_1
        13 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  false
        System.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: true
        System.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: true
        System.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: true
        System.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]; // 5M
    Object 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 只能执行一次 
    @Override
    protected 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];  // 1MB

    public 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方法 
    }

    @Override
    protected 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];  // 10M
        System.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);// null
        System.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;
        }

        @Override
        public 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 space
            e.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;
        }

        @Override
        public 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.exe

C:\Users\wei19>jinfo -flag UseParallelGC 21476
-XX:+UseParallelGC

C:\Users\wei19>jinfo -flag UseParallelOldGC 21476
-XX:+UseParallelOldGC

C:\Users\wei19>jinfo -flag UseG1GC 21476
-XX:-UseG1GC

C:\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

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值