jvm_share

01_JVM与Java体系结构

本次内容主要参考

尚硅谷jvm 近68小时 381集

https://www.bilibili.com/video/BV1PJ411n7xZ?from=search&seid=17328646192254483360
在这里插入图片描述

多线程 近64小时 174集

https://www.bilibili.com/video/BV1hJ411D7k2?from=search&seid=3333051564860741228

在这里插入图片描述

及其他看过的一些jvm、多线程书籍

jvm参数查询
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

安装jclasslib插件

分享规划,主要是想分享一下jvm调优方面的,但是由于调优没有固定套路,需要实际情况实际分析,所以调优之前需要了解jvm的特性,结合自己项目情况进行调整,因此本次分享主要分jvm介绍和调优两个部分
1.由于我个人觉得我技术水平挺菜的
2.此次分享内容中加入了大量我个人的主观理解
因此本次分享采用互动的形式,如果大家觉得我说的不对欢迎及时打断,指正错误

在这里插入图片描述
通过jdk9默认g1,已经说明了g1的强大
jdk11引入的号称革命性的gc zgc,在项目还未使用jdk11前,不着急了解zgc,其次是jdk11引入zgc如果指定gc为 zgc 有提示 experimental(实验性的),在jdk15上才是(production-ready 可用于生产)
在这里插入图片描述

jdk版本选择 lts 如果要使用zgc,个人建议zgc在jdk17中使用
在这里插入图片描述

在这里插入图片描述

  • 它采用解释器与即时编译器并存的架构
  • java属于半编译半解释型语言
    为啥两者并存,后面将介绍JIT的时候详细讨论

jvm执行流程
在这里插入图片描述
java编译器(将java源代码编译成字节码) -> 前段编译器
JIT编译器(字节码编译成机器可以直接执行的二进制)-> 后端编译器

这里先简单介绍一下jit,通过热点代码分析技术,将热点代码有字节码编译成机器可以直接执行的二进制,以加快执行
看过一篇阿里的文章 ,同配置的主备服务,进行切换时,假如主服务能承受的qps 1000,切换时要先将备服务预热才能切换,由于jit存在导致刚启动的服务无法达到1000 qps

03_运行时数据区概述及线程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
并发程序开发,可以选择,进程>线程>协程(对单个线程复用,个人感觉实现类似于nio)
java中new Thread 对应的是操作系统的一个线程(一一对应),这就导致java中的锁设计分了以下维度

java中的锁实现有一种分类维度:
1.jvm层实现的,一般为乐观锁(轻量级) synchronized(锁升级尚未升级到重量级锁前) AtomicInteger(cas)
2.操作系统层面实现的,一般为悲观锁(重量级) 例如 ReentrantLock

04_程序计数器

在这里插入图片描述
细节:线程的程序计数器指向的位置(指向当前线程的栈帧中的操作数栈顶部)

在这里插入图片描述

在这里插入图片描述
我个人的理解:程序计数器相当于一个游标,指向当前线程正在执行的代码,我理解它的作用是在多线程场景下,当前线程释放cpu执行权时,记录当前的执行位置,方便下次获得cpu执行权时继续在此位置执行,类似于书签的作用

在这里插入图片描述
在这里插入图片描述
同理还有ThreadLocal为什么设定为线程私有(为了将数据和当前线程绑定)
在这里插入图片描述

05_虚拟机栈

在这里插入图片描述

在这里插入图片描述

设置栈内存大小
-Xss 设置最大栈空间
在这里插入图片描述
这里有个疑问?根据翻译
设置线程栈大小,到底指的是每个线程可用的栈大小,还是整个栈空间的大小
这个地方我也是弄的不太清楚,有没有懂这一块的给解答一下?
问什么在这个问题上较真,主要是涉及到jvm调优,如果对栈调整,有两个入口

  • -Xss
  • 创建线程的时候指定栈大小

个人验证:

/**
 *  new 一个线程
 * -Xss1m
 * 16495,16907,16595,16966,16886
 *
 * new 一个线程
 * -Xss10m
 * 608881,588071,185842,605181,605641
 * 
 * 结论:增大 -Xss  可用增加递归深度
 */
public class TestStack {

    private static AtomicInteger count = new AtomicInteger();

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String next = scanner.next();

        TestStack testStack = new TestStack();

        Thread thread = new Thread("t1"){
            @Override
            public void run() {
                try {
                    testStack.add();
                } finally {
                    System.out.println(count.get());
                }
            }
        };

        thread.start();

    }

    public void add() {
        count.incrementAndGet();
        add();
    }
}

我这里提一个问题,这个代码有没有问题,main方法执行完thread.start()结束,虚拟机会不会退出,导致Thread(“t1”)无法继续执行?
在这里插入图片描述
补充
第一种:程序执行完,没有可执行的代码了
第二种:如果出现异常未被处理
还有一种不存在非守护线程了

这里Thread(“t1”)是非守护线程,由于它的存在不会导致jvm退出

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我个人建议设置成守护线程

疑问:线程a创建的守护线程b,如果a结束,那么b还存在吗?

package test2;

import java.util.concurrent.TimeUnit;

/**
 * @author zhaoxingbang
 * @date 2021/5/31
 */
public class Test7 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("main线程的守护线程正在执行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.setDaemon(true);
        t1.start();

        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("非守护线程正在执行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t2.setDaemon(false);
        t2.start();

        TimeUnit.SECONDS.sleep(3);


    }
}

在这里插入图片描述
通过arthas,main线程已经退出,但是他创建的守护线程还在运行

在这里插入图片描述

一个jvm运行实例对应一个RunTime实例,可以做一下jvm层面上的功能

在这里插入图片描述
添加关闭钩子,用于释放资源,his集群中分布式锁基于数据库实现,关闭前释放资源,释放锁,Tomcat stop.sh 等
在这里插入图片描述
调用gc
在这里插入图片描述
在这里插入图片描述
查看jvm内存相关使用情况

在这里插入图片描述
java调用其他语言(如python爬虫模块)

/**
 * @author zhaoxingbang
 * @date 2021/5/26
 *
 * -Xss1m  16683:18031 ,16790:20163, 21297:23541  , 18543:25401, 26129:16810
 * 结论:增加线程数量 总递归深度并未变化,所以-Xss  是设置单个线程的栈空间
 */
public class TestStack2 {

    private static AtomicInteger count1 = new AtomicInteger();
    private static AtomicInteger count2 = new AtomicInteger();

    private static CyclicBarrier barrier = new CyclicBarrier(2);

    public static void main(String[] args) {

        TestStack2 testStack1 = new TestStack2();
        TestStack2 testStack2 = new TestStack2();

        Thread thread1 = new Thread("t1"){
            @Override
            public void run() {
                try {
                    barrier.await();
                    testStack1.add1();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(count1.get());
                }
            }
        };

        Thread thread2 = new Thread("t2"){
            @Override
            public void run() {
                try {
                    barrier.await();
                    testStack2.add2();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(count2.get());
                }
            }
        };

        thread1.start();
        thread2.start();


    }

    public void add1() {
        count1.incrementAndGet();
        add1();
    }

    public void add2() {
        count2.incrementAndGet();
        add2();
    }
}

你以为这就完了吗,没完!

public class TestStack {

    private static AtomicInteger count = new AtomicInteger();

    public static void main(String[] args) {

        TestStack testStack = new TestStack();

        Thread thread = new Thread(null,() -> {
            try {
                testStack.add();
            } finally {
                System.out.println(count.get());
            }
        }, "t1", 1111111111);

        thread.start();

    }

    public void add() {
        count.incrementAndGet();
        add();
    }
}

在这里插入图片描述
结论还可以通过Thread构造方法设置栈大小,对单个线程进行设置,根据注释此参数高度依赖于操作系统,可能失效(优先于-Xss)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
验证
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
编译不通过。。。。。
去年考中移成研院机试遇到的类似题,我当时想的是哪个傻逼面试官出的这种问题,后面才发现原来小丑使我自己。。

在这里插入图片描述

这里提前提一下垃圾回收算法
1.垃圾判断算法:可达性分析(java,根据gcRoot判定的),引用计数算法(python)
2.垃圾回收算法:标记-清除算法(Mark-Sweep),复制算法(Copying),标记-整理算法(Mark-Compact)

以及:分区算法(g1及以后的新垃圾回收器使用),分代收集算法(Generational Collection),注意两者完全不一样,可以类比数据库分库中:水平分库、垂直分库都是分库,但是两者不一样

其中上面是经典的垃圾回收算法,下面两个后面被提出的,类似设计模式23种经典模式和其他(仓壁模式断路器)

这里涉及到调优如果能确定变量的声明周期,尽量把变量的生命周期缩小(局部变量,成员变量,线程变量ThreadLocal,静态变量),减少gc负担(标记阶段安全点等待,逃逸分析)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里说一下 字节码指令 :操作符 + 操作数

例如 bipush + 15
istore_1 等价于 istore + 1 (对其封装)

同理,类似于Integer
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
接下来我化身执行引擎的解释器来带大家执行一下上面代码
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
字节跳动面试题 i++ 和 ++i的区别

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

类似可以想到 jmm java内存模型(由于内存和cpu缓存速度差很大,将内存中变量缓存到寄存器上,提高性能) 引发的问题,线程读到的是寄存器中的数据 和 内存中的不一致, volatile关键字,保证多线程环境下可见性(及时刷回内存)
volatile 可见性(实现:缓存一致性协议),有序性

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我个人理解:
在这里插入图片描述

class编译后的字节码,可以看到上面有常量池
整个class在jvm运行被加载到方法区(又叫元空间,jdk7中叫永久代)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


0: ldc           #5                  // String zxb
2: astore_1
3: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_1
7: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return

上图是javap 反编译生成的字节码
ldc           #5
表示  加载  #5
其中 #5  就是符号引用
但运行时要替换成直接引用,这就是动态链接的作用

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

这里说一下,我面试被问到过一个问题:谈谈你对多态的理解?
上面就是答案

关于优化方面
静态连接性能好于动态链接(因为在动态链接时,执行引擎在执行方法时需要查找,先找当前类有没有方法的实现,没有在去父类找,直到找到为止),如果只考虑性能方面不建议使用多态,实际情况不现实

final 关键字加在方法上可以在实现静态连接提高性能,缺点不可重写了

在这里插入图片描述

在这里插入图片描述

方法重写的本质(多态)
在这里插入图片描述

在这里插入图片描述
虚方法表就是解决多态性能问题的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public class Test2 {

    public static void main(String[] args) {
        Test2 test2 = new Test2();

        Thread t1 = new Thread("t1"){
            @Override
            public void run() {
                test2.a();
            }
        };

        Thread t2 = new Thread("t2"){
            @Override
            public void run() {
                test2.a();
            }
        };

        t1.start();
        t2.start();
    }

    public void a() {
        System.out.println("a");
        b();
    }

    public void b() {
        System.out.println("b");
        c();
    }

    public void c() {
        System.out.println("c");
    }
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

06_本地方法接口

jni,osgi,和操作系统打交道或调用c/c++,开发驱动
现在用的不多,rest,socket,webService,读写共享文件方式

07_本地方法栈

08_堆

在这里插入图片描述
所有线程共享堆
在这里插入图片描述
例外的情况是 对象栈上分配,栈上分配的前提是逃逸分析,对象不发生逃逸
在这里插入图片描述

在这里插入图片描述

jdk 7 堆结构
在这里插入图片描述

在这里插入图片描述

8之前 和 8区别,主要区别metaspace用的是系统的直接内存

在这里插入图片描述
在这里插入图片描述
oracle 推荐 -Xms 和 -Xmx两者相等

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
演示oom及对象分配、gc过程

package test5;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * -Xms600m -Xmx600m
 * 演示工具 jvisualVm + visual gc插件
 * @author zhaoxingbang
 * @date 2021/7/5
 */
public class HeapInstanceTest {
    byte[] buffer = new byte[new Random().nextInt(1024 * 200)];

    public static void main(String[] args) throws InterruptedException, IOException {
        List<HeapInstanceTest> list = new LinkedList<>();
        System.in.read();
        while (true) {
            list.add(new HeapInstanceTest());
            TimeUnit.MILLISECONDS.sleep(10);
        }
    }
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
除了-XX:TLABWasteTargetPercent=1 指定百分比
-XX:TLABSize=0 可以指定大小

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
synchronized 锁升级过程,第一个阶段无锁

在这里插入图片描述
标量替换
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

证明

package test5;

import java.util.concurrent.TimeUnit;

/**
 *
 * -Xmx200m -Xms200m -XX:-DoEscapeAnalysis -XX:+PrintGCDetails
 * -Xmx200m -Xms200m -XX:+DoEscapeAnalysis -XX:+PrintGCDetails
 * 
 *
 * @author zhaoxingbang
 * @date 2021/7/5
 */
public class StackAllocation {

    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        for (int i = 0; i < 10000000; i++) {
            alloc();
        }

        long end = System.currentTimeMillis();

        System.out.println("花费的时间为: " + (end - start) + " ms");

        try {
            TimeUnit.SECONDS.sleep(100000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void alloc() {
        User user = new User();
        user.id = 5;
        user.name = "aaa";
    }

    static class User {
        public int id;
        public String name;
    }
}

  • -Xmx200m -Xms200m -XX:-DoEscapeAnalysis -XX:+PrintGCDetails
    第一次关闭逃逸分析
    在这里插入图片描述
    耗时:56ms
    发生了4次young gc

  • -Xmx200m -Xms200m -XX:+DoEscapeAnalysis -XX:+PrintGCDetails

在这里插入图片描述
消耗:3ms
未发生young gc

在这里插入图片描述
这里矛盾了。。。。。。下面是依据
在这里插入图片描述

09_方法区

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
避免full gc 这里建议要根据自己项目实际class的加载情况考虑一下,要不要调整 两个参数

在这里插入图片描述
方法区溢出
在这里插入图片描述

oom解决流程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
方法区存什么?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
为什么要常量池?
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述.在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
静态变量放在哪?

package jvm;

import java.util.concurrent.TimeUnit;

/**
 * 结论:静态引用对应对象实体始终都在堆空间
 * -Xms200m -Xmx300m -XX:MetaspaceSize=300m -XX:MaxMetaspaceSize=300m -XX:+PrintGCDetails
 * @author zhaoxingbang
 * @date 2021/7/8
 */
public class StaticFieldTest {

    /**
     * 100m
     */
    private static byte[] arr = new byte[1024 * 1024 *100];

    public static void main(String[] args) throws InterruptedException {
        System.out.println(StaticFieldTest.arr);

        TimeUnit.HOURS.sleep(1);
    }
}

在这里插入图片描述
结论:静态引用对应对象实体始终都在堆空间

在这里插入图片描述
staticObj:存放在堆中
instanceObj:随着test对象实例存放在堆中
localObj:存放在栈帧局部变量表中

所以静态变量在java8中存放在堆中

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
jvm选择第二种

11_直接内存

12_执行引擎

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

回边计数器作用
在这里插入图片描述
加快包含循环方法的编译触发

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里发现mix方式吞吐量最高,我又查了一下jvm参数
在这里插入图片描述

-Xcomp 说是首次执行的时候进行编译,我在想是不是首次执行的时候拖慢了速度于是,我在压测前先用postman主动调用一下,但是由于dispatcherServlet初始化很耗时,公平起见重新测试一遍

-Xint
在这里插入图片描述

在这里插入图片描述

-Xcomp
在这里插入图片描述
在这里插入图片描述

-Xmixed
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
jdk为64位时自动选择 server模式
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

13_StringTable

0.字符串常量池位置

在这里插入图片描述
字符串常量池 StringTable在堆中

1.字符串特性

不可变性
在这里插入图片描述

2.字符串常量池介绍

在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。

string有两种创建方式:

  • 直接使用双引号声明出来的String对象会直接存储在常量池中。
  • 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中

在这里插入图片描述

intern方法介绍
在这里插入图片描述
String#intern方法中看到,这个方法是一个 native 的方法,但注释写的非常明了。“如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回”。

在 jdk7后,oracle 接管了 JAVA 的源码后就不对外开放了,根据 jdk 的主要开发人员声明 openJdk7 和 jdk7 使用的是同一分主代码,只是分支代码会有些许的变动。所以可以直接跟踪 openJdk7 的源码来探究 intern 的实现。

在这里插入图片描述
它的大体实现结构就是: JAVA 使用 jni 调用c++实现的StringTable的intern方法, StringTable的intern方法跟Java中的HashMap的实现是差不多的, 只是不能自动扩容。

在这里插入图片描述
jdk7开始,默认值为60013
-XX:StringTableSize=8888

在这里插入图片描述

3.String的内存分配

在这里插入图片描述

  • 证明字符串在常量池中是唯一的
package jvm;

/**
 * -Xms10m -Xmx10m
 * @author zhaoxingbang
 * @date 2021/7/13
 */
public class StringTest2 {

    public static void main(String[] args) {


        char [] a = {'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'};

        String s3 = new String(a);
        String s4 = new String(a);

        String s1 = "aaaaaaaa";
        String s2 = "aaaaaaaa";
        s3.intern();

        System.out.println();
    }
}

4.字符串拼接

在这里插入图片描述

package jvm;

import org.junit.Test;

/**
 * @author zhaoxingbang
 * @date 2021/7/13
 */
public class StringTest3 {

    @Test
    public void test1() {
        String s1 = "a" + "b" + "c";
        String s2 = "abc";
        System.out.println(s1 == s2);
    }

    @Test
    public void test2() {
        String s1 = "javaEE";
        String s2 = "hadoop";

        String s3 = "javaEEhadoop";
        String s4 = "javaEE" + "hadoop";

        String s5 = s1 + "hadoop";
        String s6 = "javaEE" + s2;
        String s7 = s1 + s2;

        System.out.println(s3 == s4);
        System.out.println(s3 == s5);
        System.out.println(s3 == s6);
        System.out.println(s3 == s7);
        System.out.println(s5 == s6);
        System.out.println(s5 == s7);
        System.out.println(s6 == s7);

        String s8 = s6.intern();
        System.out.println(s3 == s8);
    }
}

5.使用intern优化代码

package jvm;

import java.util.concurrent.TimeUnit;

/**
 * @author zhaoxingbang
 * @date 2021/7/14
 */
public class StringIntern {

    static final int MAX_COUNT = 1000 * 10000;

    static final String[] arr = new String[MAX_COUNT];

    public static void main(String[] args) throws InterruptedException {
        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] = String.valueOf(data[i % data.length]);  // 6729

            arr[i] = String.valueOf(data[i % data.length]).intern(); // 1068

        }
        long end = System.currentTimeMillis();

        System.out.println("话费的时间为: " + (end - start));

        TimeUnit.HOURS.sleep(1);
    }
}

在这里插入图片描述
在这里插入图片描述

6.StringTable垃圾回收测试

package jvm;

/**
 * @author zhaoxingbang
 * @date 2021/7/15
 *
 * -Xms15m -Xmx15m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails
 *
 */
public class StringGCTest {

    public static void main(String[] args) throws InterruptedException {
//        int count = 0;
//        int count = 100;
//        int count = 10000;
        int count = 1000000;

        for (int i = 0; i < count; i++) {
            String.valueOf(i).intern();
        }
    }
}

14_垃圾回收概述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
oracle 关于gc的介绍
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.html

gc区域
在这里插入图片描述

在这里插入图片描述

15_垃圾回收相关算法

标记算法

判断对象存活一般有两种方式:引用计数算法,可达性分析算法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.mat gc root溯源

在这里插入图片描述

package jvm;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;

/**
 * @author zhaoxingbang
 * @date 2021/7/31
 */
public class GCRootsTest {

    public static void main(String[] args) throws InterruptedException {
        List<Object> numList = new ArrayList<>();

        Date birth = new Date();

        for (int i = 0; i < 100; i++) {
            numList.add(String.valueOf(i));
        }

        System.out.println("数据加载完毕,请操作:");
        new Scanner(System.in).next();
        numList = null;
        birth = null;

        System.out.println("numList birth 已置空,请操作:");
        new Scanner(System.in).next();

        System.out.println("结束");
    }
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2.JProfiler gc root溯源

在这里插入图片描述

在这里插入图片描述

1.介绍

在这里插入图片描述

2.图示

在这里插入图片描述

3.优缺点

在这里插入图片描述
在这里插入图片描述

1.介绍

在这里插入图片描述

2.图示

在这里插入图片描述

3.优缺点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.介绍

在这里插入图片描述

2.图示

在这里插入图片描述
在这里插入图片描述

3.优缺点

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

15_垃圾回收概述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值