【Java校招面试】基础知识(四)——JVM

目录

  • 前言
  • 一、基础概念
  • 二、反射
  • 三、类加载器ClassLoader
  • 四、JVM内存模型
  • 后记


前言

本篇主要介绍Java虚拟机——JVM的相关内容。

“基础知识”是本专栏的第一个部分,本篇博文是第四篇博文,如有需要,可:

  1. 点击这里,返回本专栏的索引文章
  2. 点击这里,返回上一篇《【Java校招面试】基础知识(三)——多线程与并发》

一、基础概念

01. Java代码的Compile once, run anywhere是怎么实现的?
所谓Compile once, run anywhere即一次编译,随处运行。
在这里插入图片描述
Java源码首先被编译成字节码,再由不同平台的JVM进行解析,Java语言在不同的平台上运行时不需要进行重新编译,Java虚拟机在执行字节码的时候,把字节码转换成具体平台上的机器指令。

02. 为什么不直接将源码解析成机器码去执行?
1) 准备工作: 每次执行前都需要各种检查,降低执行效率;
2) 兼容性: 可以将别的语言编译成字节码,然后用JVM执行,提高兼容性。

03. JVM如何加载.class文件?
在这里插入图片描述
JVM主要由Class LoaderRuntime Data AreaExecution EngineNative Interface组成。

  • Class Loader: 依据特定格式,加载class文件到内存
  • Execution Engine: 对命令进行解析
  • Native Interface: 融合不同开发语言的原生库为Java所用
  • Runtime Data Area: JVM内存空间结构模型

二、反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。

01. 反射的代码示例
1) 目标类(包含一个私有属性、一个私有方法、一个公有方法,注意包名)

	package reflecttest;
	
	public class Target {
		// 目标类私有属性
	    private String privateField;
	    // 目标类公有方法
	    public void publicMethod(String param) {
	        System.out.println("I'm the public method of Target class.The param is " + param);
	    }
	    // 目标类私有方法
	    private void privateMethod(String param) {
	        System.out.println("I'm the private method of Target class.The param is " + param);
	    }
	}

2) 反射调用类

	public class ReflectTest {
	    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
	        Class targetClass = Class.forName("reflecttest.Target");
	        Target target = (Target) targetClass.newInstance();
	        // 通过反射引用目标类私有属性
	        testPrivateField(targetClass, target);
	        // 通过反射调用目标类公有方法
	        testPublicMethod(targetClass, target);
	        // 通过反射调用目标类私有方法
	        testPrivateMethod(targetClass, target);
	    }
	
	    private static void testPrivateField(Class clazz, Target target) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
	        Field privateField = clazz.getDeclaredField("privateField");
	        privateField.setAccessible(true);
	        System.out.println("The Origin Value of Private Field Is: " + privateField.get(target));
	        privateField.set(target, "New Value");
	        System.out.println("The Fixed Value of Private Field Is: " + privateField.get(target));
	    }
	
	    private static void testPublicMethod(Class clazz, Target target) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
	        Method publicMethod = clazz.getMethod("publicMethod", String.class);
	        publicMethod.invoke(target, "\"Calling Public Method.\"");
	    }
	
	    private static void testPrivateMethod(Class clazz, Target target) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
	        Method privateMethod = clazz.getDeclaredMethod("privateMethod", String.class);
	        privateMethod.setAccessible(true);
	        privateMethod.invoke(target, "\"Calling Private Method.\"");
	    }
	}

3) 输出结果

The Origin Value of Private Field Is: null
The Fixed Value of Private Field Is: New Value
I'm the public method of Target class. The param is "Calling Public Method."
I'm the private method of Target class. The param is "Calling Private Method."

4) 总结
  ① 流程: 先通过完全限定类名获得类对象,然后newInstance获取实例,该实例用于方法的invoke和属性的set等各种操作;
   getDeclaredMethod可以获得类的所有方法,但不能获取继承的和实现的接口函数,getMethod只可以获取public方法,但也可以获取继承的public方法和实现的接口中的方法。对于Field同理;
   对私有的属性或者方法进行操作时,需要setAccessable(true),改变访问权限。


三、类加载器ClassLoader

ClassLoader的主要工作在Class装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流并装载进系统,然后交给JVM进行连接、初始化等操作。

01. 类从编译到执行的过程
1) 编译器将Test.java源文件编译成Test.class字节码文件;
2) ClassLoader将字节码转换为JVM中的Class对象;
3) JVM利用Class对象实例化Test对象。

02. ClassLoader的类型
1) BootstrapClassLoader: 启动类加载器
   使用C++编写;
   负责加载Java核心库,如java.lang;
   无法直接获取;
2) ExtClassLoader: 扩展类加载器
   使用Java编写;
   负责加载jdk_home/lib/ext目录下的jar
3) AppClassLoader: 应用类加载器
   使用Java编写;
   负责加载程序所在目录。
4) 自定义ClassLoader

03. 自定义ClassLoader的例子
1) 要加载的目标类:

	public class Target {
	    static {
	        System.out.println("I'm Static Block of Target Class.");
	    }
	}

2) 自定义ClassLoader:

	public class MyClassLoader extends ClassLoader {
	    private String classPath;
	    public MyClassLoader(String classPath) {
	        this.classPath = classPath;
	    }
	
	    @Override
	    protected Class<?> findClass(String name) throws ClassNotFoundException {
	        byte[] clazz = loadClassFromFile(name);
	        return defineClass(name, clazz, 0, clazz.length);
	    }
	
	    public byte[] loadClassFromFile(String className) {
	        String path = classPath + File.separator + className + ".class";
	        try (InputStream in = new FileInputStream(new File(path));
	              ByteArrayOutputStream out = new ByteArrayOutputStream();) {
	            int c;
	            while ((c = in.read())!= -1)
	                out.write(c);
	            return out.toByteArray();
	        } catch (Exception ex) {
	            return null;
	        }
	    }
	}

3) ClassLoader调用类

	public class ClassLoaderTest {
	    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
	        String classPath = "/path/to/class/loader";
	        MyClassLoader classLoader = new MyClassLoader(classPath);
	        Class c = classLoader.loadClass("Target");
	        c.newInstance();
	    }
	}

4) 输出结果

	I'm Static Block of Target Class.

04. ClassLoader双亲委派机制
1) AppClassLoader的父类是ExtClassLoader,ExtClassLoader的父类是BootstrapClassLoader;
2) loadClass方法源码:

		protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
	        // First, check if the class has already been loaded
	        Class c = findLoadedClass(name);
	        if (c == null) {
	            try {
	                if (parent != null) {
	                    c = parent.loadClass(name, false);
	                } else {
	                    c = findBootstrapClassOrNull(name);
	                }
	            } catch (ClassNotFoundException e) {
	                // ClassNotFoundException thrown if class not found
	                // from the non-null parent class loader
	            }
	            if (c == null) {
	                // If still not found, then invoke findClass in order
	                // to find the class.
	                c = findClass(name);
	            }
	        }
	        if (resolve) {
	            resolveClass(c);
	        }
	        return c;
	    }

3) 程序逻辑——双亲委派机制

  • 首先找缓存(findLoadedClass),没有的话就判断有没有parent,有的话就用parent来递归的loadClass,然而ExtClassLoader并没有设置parent,则会通过findBootstrapClassOrNull来加载class,而findBootstrapClassOrNull则会通过JNI方法private native Class findBootstrapClass(String name)来使用BootStrapClassLoader来加载class。
  • 然后如果parent未找到class,则会调用findClass来加载class,findClass是一个protected的空方法,可以覆盖它以便自定义class加载过程。

05. 使用双亲委派机制为什么是安全的?
举个例子,ClassLoader加载的class文件来源很多,比如编译器编译生成的class、或者网络下载的字节码。而一些来源的class文件是不可靠的,比如我可以自定义一个java.lang.Integer类来覆盖jdk中默认的Integer类,例如下面这样:

	package java.lang;
	
	public class Integer {
	    public Integer(int value) {
	        System.exit(0);
	    }
	}

初始化这个Integer的构造器是会退出JVM,破坏应用程序的正常进行。 如果使用双亲委派机制的话,该Integer类永远不会被调用。因为委托BootStrapClassLoader加载后会加载JDK中的Integer类而不会加载自定义的这个,因此就保证了安全性。

06. 类的加载方式
1) 隐式加载: new,程序在运行过程中,遇到需要用new生成对象时,隐式调用类加载器将对应的类加载到JVM中。
2) 显式加载: loadClass,forName等

区别:
1) 显式加载需要先获取类对象,然后调用其newInstance方法获取对象,而隐式加载不需要;
2) new支持调用目标类带参数的构造方法,而newInstance方法则不支持传入参数。

07. 类的装载过程
在这里插入图片描述
1) 加载: 通过ClassLoader加载class文件字节码,生成Class对象;
2) 链接:
  ① 校验: 检查加载的类的正确性和安全性;
  ② 准备: 为类变量分配存储空间并设置类变量初始值;
  ③ 解析: JVM将常量池中的符号引用转换为直接引用。
3) 初始化: 执行类变量赋值和静态代码块

08. loadClass和forName的区别
1) Class.forName得到的class是已经初始化完成的;
2) ClassLoader.loadClass得到的class是还没有链接的。

应用场景:
1) 有的类有静态代码块(如mysql-connector),因此需要使用Class.forName;
2) Spring中由于IoC大量使用LazyLoad技术,即延时加载,在bean真正被调用时才完成初始化。这里用到的是ClassLoader.loadClass。


四、JVM内存模型

01. 从线程的角度看JVM内存模型
在这里插入图片描述

  • 线程私有的内存: 程序计数器、虚拟机栈、本地方法栈
  • 所有线程共享的内存: 元空间(MetaSpace)、常量池、堆

1) 程序计数器(Program Counter Register): 一块较小的内存空间,是当前线程所执行的字节码行号指示器 (逻辑计数器)。改变计数器的值来选取下一条需要执行的字节码指令。和线程是一对一关系,即线程私有;对Java方法,记录的是正在执 行的虚拟机字节码指令的地址;对Native方法,其值为Undefined。 由于只记录行号,不会发生内存泄漏。

2) 虚拟机栈(Stack): 是Java方法执行的内存模型。每个方法被执行时,都会 创建一个栈帧(方法运行期间的基础数据结构)。
在这里插入图片描述
  ① 局部变量表和操作数栈
  局部变量表: 包含方法执行过程中的所有变量
  操作数栈: 入栈、出栈、复制、交换、产生/消费变量
  ② 通过一个简单的加法函数例子来深入理解

	public static int add(int a, int b){
		int c = 0;
		c = a + b;
		return c;
	}

  假设对add方法传入(1, 2),其过程为:
在这里插入图片描述
  ③ 递归为什么会引发java.lang.StackOverflowError异常?
  递归过深,栈帧数超出虚拟栈的深度。解决方法:限制递归深度或者换用迭代方法。
  ④ 总结: 方法执行完成后,栈帧会被自动释放掉,因此不需要GC来回收。
3) 本地方法栈: 与虚拟机栈类似,主要作用于标注了native的方法。
4) 元空间(Meta Space): JDK 8中把类的元数据放在元空间,而JDK 7及以前的版本是放在永久代(PermGen)中。
  MetaSpace与PermGen相比的优势:
   元空间使用本地内存,永久代使用jvm内存;
   字符串常量池存在永久代中,容易出现性能问题和内存溢出,Meta Space没有了字符串常量池,它被移动到了堆中;
   类和方法的信息大小难以确定,给永久代的大小指定带来困难;
   永久代会为GC带来不必要的复杂性,回收效率偏低;
   方便HotSpot与其他JVM(如Jrockit)的集成。
5) 堆(Heap): 对象实例的分配区域,GC管理的主要区域;

02. 从存储的角度看JVM内存模型
1) JVM三大性能调优参数-Xms、-Xmx、-Xss的含义?

  • -Xms堆大小的初始值
  • -Xmx堆大小能扩容的最大值
  • -Xss规定了每个线程虚拟机栈(堆栈)的大小;

通常-Xms和-Xmx设置成一样的,因为在堆扩容时会发生内存抖动,影响程序运行时的稳定性。
2) 内存分配策略

  • 静态存储: 编译时确定每个数据目标在运行时的存储空间需求;
  • 栈式存储: 数据区需求在编译时未知,运行时模块入口已确定;
  • 堆式存储: 编译时或运行时模块入口都无法确定,动态分配。

3) Java内存模型中堆和栈的区别
联系: 引用对象、数组时,栈里定义变量保存堆中目标的首地址;
在这里插入图片描述
区别:
  ① 管理方式: 栈自动释放,堆需要GC;
  ② 空间大小: 一般情况下栈比堆小;
  ③ 碎片相关: 栈产生的碎片远小于堆;
  ④ 分配方式: 栈支持静态和动态分配,堆仅支持动态分配;
  ⑤ 效率: 栈的效率比堆高。

4) Meta Space、堆、线程独占部分的联系
一个HelloWorld的例子:

	public class HelloWorld{
		private String String name;
		public void setName(String name){
			this.name = name;
		}

		public void sayHello(){
			System.out.println("Hello " + name);
		}

		public static void main(String[] args){
			int a = 1;
			HelloWorld hw = new HelloWorld();
			hw.setName("test");
			hw.sayHello();
		}
	}

内存分配:
在这里插入图片描述
5) JDK 6和JDK 6+中intern方法的区别

	String s = new String("Test");
	s.intern();
  • JDK 6: 当调用intern方法时,如果字符串常量池已创建该字符串对象,则返回池中的该字符串的引用。否则,将此字符串对象添加到字符串常量池中,并返回该字符串对象的引用。
  • JDK 6+: 当调用intern方法时,如果字符串常量池已创建该字符串对象,则返回池中的该字符串的引用。否则,如果该字符串对象已经存在于Java堆中,则将堆中对此对象的引用添加到字符串常量池,并且返回该引用;如果堆中不存在,则在池中创建该字符串并返回其引用。

6) 不同JDK中intern方法的功能区别
  ① 代码

		public static void main(String[] args){
			String s = new String("a");
			s.intern();
			String s2 = "a";
			System.out.println(s == s2);

			String s3 = new String("a") + new String("a");
			s3.intern();
			String s4 = "aa";
			System.out.println(s3 == s4);
		}

  ② JDK 6中(输出结果false、false)
  PermGen只存放堆中的副本,不能存放引用,所以s3==s4false
在这里插入图片描述
  ③ JDK 6+中(输出结果false、true)
  堆中的字符串常量池可以存放字符串的引用,所以s3==s4true
在这里插入图片描述
03. 对象在内存中的布局
1) 对象头(Header): 对象头分为Mark WordClass Metadata Address两部分,如果对象类型是数组,还要有第三个部分Array Length,即数组长度。

长度内容说明
32/64 bitsMark Word存储对象的hashCode或锁信息等
32/64 bitsClass Metadata Address存储对象类型数据的指针
32 bitsArray Length数组长度(如果对象类型是数组)

Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的 数据,它会根据对象的状态复用自己的存储空间。

   在32位的HotSpot虚拟机中,如果对象处于未被锁定的状态下,那么Mark Word的32bit空间中的25bit用于存储对象哈希码,4bit用于存储对象分代年龄,2bit 用于存储锁标志位,1bit固定为0。

锁状态25 bits4 bits1 bit是否是偏向锁2 bits锁标志位
无锁对象的hashCode对象的分代年龄001

   在 32 位系统下,存放 Class 指针的空间大小是 4 字节,Mark Word 空间大小也是4字节,因此头部就是 8 字节,如果是数组就需要再加 4 字节表示数组的长度。

锁状态25 bits4 bits1 bit2 bits
23 bits2 bits是否是偏向锁锁标志位
轻量级锁指向栈中锁记录的指针00
重量级锁指向互斥量(重量级锁)的指针10
GC标记11
偏向锁线程IDEpoch对象分代年龄101

   在64位系统及64位JVM下,开启指针压缩,那么头部存放Class指针的空间大小还是4字节,而Mark Word区域会变大,变成8字节,也就是头部最少为12字节。

锁状态25 bits31 bits1 bit4 bits1 bit2 bits
cms free分代年龄偏向锁锁标志位
无锁unusedhashCode001
偏向锁ThreadID(54 bits), Epoch(2 bits)101

2) 实例数据(Instance Data): 实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。这部分的存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在 Java源码中定义顺序的影响。

3) 对齐填充(Padding): 不是必然存在的,没有特别的含义,它仅起到占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,也就是说对象的大小必须是8字节的整数倍。对象头部分是8字节的倍数,所以当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。


后记

JVM的知识点也不少,为便于阅读,我将其分为了基础概念反射类加载器ClassLoaderJVM内存模型4个部分。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/16302.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

营收、利润增速第一!海尔智家为何领跑?

“企业只有保持领先的能力&#xff0c;才有可能取得经济成果。” 管理学大师德鲁克曾如此强调。所谓“领先”&#xff0c;就是独一无二的、有价值的东西。利润&#xff0c;是企业在某个领域取得领先优势后&#xff0c;必然获得的回报。 这种“领先优势”&#xff0c;在各行业…

Linux基础IO【重定向及缓冲区理解】

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 文章目录 &#x1f307;前言&#x1f3d9;️正文1、文件描述符1.1、先描述&#xff0c;再组织1.2、files_struct1.3、分配规则…

跨平台Office文档预览原生插件,非腾讯X5,支持离线,稳定高可用

引言 2023年4月13日零时起&#xff0c;腾讯浏览服务内核文档能力正式下线&#xff0c;要实现真正离线文档预览&#xff0c;于是有了这边文章。 前面写了多篇关于<跨平台文件在线预览解决方案>&#xff0c;不管使用pdf.js、LibreOffice&#xff0c;还是永中DCS&#xff…

单列文本数据快速导入表格

文本数据导入Excel似乎是个老生常谈&#xff0c;方法也有很多&#xff0c;例如 使用文本编辑器打开文本文件&#xff0c;拷贝粘贴到Excel然后分类Power Query中的【从文本/CSV】如下图所示。 但是这个需求略有不同&#xff0c;文本数据为单列&#xff0c;每7行数据为一组&am…

MYSQL-数据库管理(下)

查看数据库信息 show database 查看数据库中的表信息 use 数据库名 #切换到书库中 show tables show tables in mysql 显示数据表的结构&#xff08;字段&#xff09; describe user; Field:字段名称 type:数据类型 Null :是否允许为空 Key :主键 Type:数据类型 Null :是否…

缓存空间优化实践

导读 缓存 Redis&#xff0c;是我们最常用的服务&#xff0c;其适用场景广泛&#xff0c;被大量应用到各业务场景中。也正因如此&#xff0c;缓存成为了重要的硬件成本来源&#xff0c;我们有必要从空间上做一些优化&#xff0c;降低成本的同时也会提高性能。 下面以我们的案…

【Git】Gitee免密push(TencentCloudLinux)

前提&#xff1a; 我用的是腾讯云的Centos(Linux)服务器 我创建好了仓库 我配置过git 可以正常用密码push 以上自行解决 我们直接配置公钥解决免密push 1.在服务器上创建公钥 在用户根目录创建 公钥 邮箱写自己的 随意写 我写的是gitee绑定的邮箱 ssh-keygen -t ed25519 -C…

数据结构(六)—— 二叉树(2)遍历

文章目录 递归三要素一、深度优先遍历&#xff08;前中后序&#xff09;1.1 递归遍历1.1.1 前序&#xff08;中左右&#xff09;1.1.2 中序&#xff08;左中右&#xff09;1.1.3 后序&#xff08;左右中&#xff09; 1.2 迭代遍历1.2.1 前序1.2.2 后序1.2.3 中序 二、广度优先遍…

Renesas瑞萨A4M2和STM32 CAN通信

刚好拿到一块瑞萨开发板&#xff0c;捣鼓玩下CAN&#xff0c;顺便试下固件升级。 A4M2 工程创建 详细可以参考&#xff0c;我之前写的文章 Renesa 瑞萨 A4M2 移植文件系统FAT32 CAN0 配置信息&#xff0c;使能FIFO&#xff0c;接收标准帧 ID为0x50&#xff0c;数据帧。 代…

密码学【java】初探究加密方式之对称加密

文章目录 一 常见加密方式二 对称加密2.1 Cipher类简介2.2 Base算法2.3 补充&#xff1a;Byte&bit2.4 DES加密演示2.5 DES解密2.6 补充&#xff1a;对于IDEA控制台乱码的解决方法2.7 AES加密解密2.8 补充&#xff1a; toString()与new String ()用法区别2.9 加密模式2.9.1 …

内网渗透(六十二)之 NTLM Realy 攻击

NTLM Realy 攻击 NTLM Realy 攻击其实应该称为Net-NTLM Realy 攻击,它发生在NTLM认证的第三步,在Response 消息中存在Net-NTLM Hash,当攻击者获得了 Net-NTLM Hash 后,可以重放Net-NTLM Hash 进行中间人攻击。 NTLM Realy 流程如图所示,攻击者作为中间人在客户端和服务器…

【C++】异常,你了解了吗?

在之前的C语言处理错误时&#xff0c;会通过assert和错误码的方式来解决&#xff0c;这导致了发生错误就会直接把程序关闭&#xff0c;或者当调用链较长时&#xff0c;就会一层一层的去确定错误码&#xff0c;降低效率&#xff0c;所以c针对处理错误&#xff0c;出现了异常&…

奥斯汀独家对话|从机构的「拉扯」中成长的美国加密监管

‍前言 4月25日&#xff0c;在美国得克萨斯州的首府奥斯汀&#xff0c;这座充满活力和创造力的城市&#xff0c;欧科云链研究院与来自哥伦比亚商学院的Austin Campbell教授就美国加密监管以及其相关话题进行了一次深入探讨。双方讨论了美国整体的监管问题、监管逻辑、最新的稳…

git把我本地文件传到我的指定的仓库

在使用Git将本地文件推送到指定仓库之前&#xff0c;请确保已经安装了Git并进行了基本配置。接下来&#xff0c;遵循以下步骤将本地文件推送到远程仓库&#xff1a; 兄弟先赏析悦目一下&#xff0c;摸个鱼 首先&#xff0c;在本地文件夹中打开命令行界面&#xff08;在Windows上…

SpringBoot整合Mybatis-Plus、Jwt实现登录token设置

Spring Boot整合Mybatis-plus实现登录常常需要使用JWT来生成用户的token并设置用户权限的拦截器。本文将为您介绍JWT的核心讲解、示例代码和使用规范&#xff0c;以及如何实现token的生成和拦截器的使用。 一、JWT的核心讲解 JWT&#xff08;JSON Web Token&#xff09;是一种…

【P1】Jmeter 准备工作

文章目录 一、Jmeter 介绍1.1、Jmeter 有什么样功能1.2、Jmeter 与 LoadRunner 比较1.3、常用性能测试工具1.4、性能测试工具如何选型1.5、学习 Jmeter 对 Java 编程的要求 二、Jmeter 软件安装2.1、官网介绍2.2、JDK 安装及环境配置2.3、Jmeter 三种模式2.4、主要配置介绍2.4.…

外卖项目优化-02-

文章目录 瑞吉外卖项目优化-Day02课程内容前言1. MySQL主从复制1.1 介绍1.2 搭建1.2.1 准备工作1.2.2 主库配置1.2.3 从库配置 1.3 测试 2. 读写分离案例2.1 背景介绍2.2 ShardingJDBC介绍2.3 数据库环境2.4 初始工程导入2.5 读写分离配置2.6 测试 3. 项目实现读写分离3.1 数据…

HTML(四) -- 多媒体设计

目录 1. 视频标签 2. 音频标签 3. 资源标签&#xff08;定义媒介资源 &#xff09; 1. 视频标签 属性值描述autoplayautoplay如果出现该属性&#xff0c;则视频在就绪后马上播放。controlscontrols表示添加标准的视频控制界面&#xff0c;包括播放、暂停、快进、音量等…

【LLM】离线部署ChatGLM-6B模型

目录 前言 准备环境 打包环境 下载/上传模型 部署模型 前言 甲方出手&#xff0c;天下我有&#x1f929;。圆梦了圆梦了~一直想整一台GPU服务器尝尝鲜&#xff0c;奈何钱包空空&#xff0c;虽然有可以在CPU上部署的方案&#xff0c;但效果却不是让人那么满意&#xff0c…

跟着杰哥学强化学习:多臂老虎机问题

多臂老虎机问题 现在有3台外观一模一样的老虎机,每个老虎机的赔率是不同的,摇动一次需要1块钱,现在给你100块钱,如何获取最大的收益。 如果我们知道了每个老虎的赔率,那么只要选择收益最高的那个老虎机就可以了,但现在问题是并不知道每个老虎机的收益。为了简单,我们假…