【JVM】JVM基础教程(一)

news2025/1/19 20:18:44

目录

初识JVM

JVM是什么?

JVM的功能

解释、即时编译和运行

内存管理

常见的JVM

JVM虚拟机规范

HotSpot的发展历程

JVM的组成

字节码文件详解

应用场景

以正确姿势打开字节码文件

​编辑字节码文件的组成

基本信息

 Magic魔数

主副版本号

常量池

接口

字段

方法

案例分析

案例分析

案例分析

属性

常用工具

javap -v命令

IDEA插件-jclasslib

阿里Arthas

入门使用

监控面板

查看字节码信息

案例分析

类的生命周期(重点)

应用场景

生命周期概述

加载阶段

查看内存中的对象

连接阶段

验证阶段

版本号的检测

准备阶段

静态变量分配内存&初始值

解析阶段

初始化阶段

何时初始化?

clinit指令

初始化的要点

案例分析

案例分析

案例分析

案例分析

常量折叠



初识JVM

JVM是什么?

JVM(Java Virtual Machine),中文名Java虚拟机

JVM就是一款软件,用来运行Java字节码文件

将字节码文件【解释】(配合JIT、AOT编译器)成【机器码】,计算机能直接运行机器码

机器码是计算机能够直接运行的指令集,机器码由二进制数字组成,通常以0和1的形式表示。这些指令被处理器(CPU)解码和执行

关于JIT和AOT,可以看我另一篇博客:【Java基础面试题003】Java的JIT | AOT是什么?_java jit面试-CSDN博客


JVM的功能

JVM 包含内存管理、解释执行虚拟机指令、即时编译三大功能

解释、即时编译和运行

  • 将字节码文件,解释+即时编译成机器码,交给计算机执行
  • 即时编译,对热点代码进行优化,提升执行效率

(无论JVM到底是编译字节码还是解释字节码,最终都是生成机器码)

  • 由于JVM需要实时解释虚拟机指令,所以Java语言如果不做任何优化,性能不如C、C++等语言

  •  JVM提供了即时编译(JIT)进行性能的优化,尤其是C2编译器,最终能勉强接近C、C++的运行性能(接近,还是勉强~~),在特性场景下有机会实现超越

  • Java需要实时解释,主要是为了支持跨平台特性

内存管理

  • 自动给对象、方法等分配内存
  • 自动的垃圾回收机制,回收不再使用的对象

常见的JVM

常见的JVM有HotSpot、GraalVM、OpenJ9等,另外DragonWell龙井JDK也 提供了一款功能增强版的JVM。其中使用最广泛的是HotSpot虚拟机

看得出Java的生态真的很好,不过我用的JDK是这一家的

Adoptium Eclipse Temurin:Home | Adoptium

JVM虚拟机规范

官网地址:Java SE Specifications (oracle.com)

《Java虚拟机规范》由Oracle制定,内容主要包含了Java虚拟机在设计和实现时需要遵守的规范,主 要包含class字节码文件的定义、类和接口的加载和初始化、指令集等内容。

《Java虚拟机规范》是对虚拟机设计的要求,而不是对Java设计的要求,也就是说虚拟机可以运行在 其他的语言比如Groovy、Scala生成的class字节码文件之上。

HotSpot的发展历程

JVM的组成


字节码文件详解

应用场景

  • 面试回答

学习字节码文件的组成可以更深入的理解Java代码,说点实际的,最起码面试的时候有用

比如下列面试题:

如果从Java语法层面回答这个题,面试官可能会觉得你的道行不深,如果从字节码文件中的字节码指令来回答这个题,面试官可能会觉得你对JVM的了解还不错


这里给个例子复习一下后自增

(i++先返回值用来赋值操作,后自增,但是自增的值"丢失"了,没有被保存下来,可以理解为自增操作被赋值操作覆盖了)

        int i = 0;
        i = i++;
        System.out.println(i); // 0

        int j = 0;
        j = ++j;
        System.out.println(j); // 1

  • 解决版本冲突

  • 系统升级


以正确姿势打开字节码文件

一般的记事本,包括高级记事本,打开字节码文件,会部分乱码,毕竟.java文件编译成.class文件用的编码格式有很多,不单纯是单一的UTF8啥的

为了更直观查看.class文件,可以用下面这一款工具【jclasslib】

官网:GitHub - ingokegel/jclasslib: jclasslib bytecode editor is a tool that visualizes all aspects of compiled Java class files and the contained bytecode.

版本号:v6.0.5


字节码文件的组成

下面围绕这段代码进行分析字节码文件的组成

public interface Test {
    void printOneLine(int age);
}
public class MyTest implements Test{
    // 常量
    private static final String str1 = "黄小桃";
    private static final String str2 = "黄小桃";

    private int age;

    public MyTest(){}

    public MyTest(int age){
        this.age = age;
    }

    public int getAge(){
        return this.age;
    }
    @Override
    public void printOneLine(int age){
        System.out.println(str1 + "那年" + age + "岁");
    }

    public static void main(String[] args) {
        MyTest myTest = new MyTest(19);
        myTest.printOneLine(myTest.getAge());
    }
}

 

基本信息

 Magic魔数
  • 文件是无法通过文件扩展名来确定文件类型的,文件扩展名可以随意修改,不影响文件的内容
  • 软件使用文件的头几个字节(文件头)去校验文件的类型,如果软件不支持该种类型就会出错
  • Java字节码文件中,将文件头称为Magic魔数

 (魔数:是指文件的前四个字节,用于标识该文件确实是一个有效的Java字节码文件。这个魔数的值是固定的。魔数帮助系统快速识别文件,不需要检查文件内容)

下面给一个魔数的例子:

0000: CAFEBABE (魔数)
0004: 0000 0034 (次版本号、主版本号)
...
主副版本号

就是编译字节码文件的JDK版本号

  • 主版本号用来标识大版本号,JDK1.0 - 1.1使用了45.0 - 45.3,JDK1.2是46之后没升级一个大版本就加1;
  • 副版本号是当主版本号相同时作为区分不同版本的标识,一般只需要关心主版本号

版本号的作用主要是判断当前字节码的版本和运行时的JDK是否兼容


案例:主版本号不兼容错误

两种解决方案:

  • 升级JDK版本(容易引发其他的兼容性问题,需要大量的测试)
  • 将第三方依赖的版本号降低或者更换依赖,以满足JDK版本的需求(建议采用)

常量池

避免重复定义相同的内容,节省空间

  • 常量池中的数据都有一个编号,编号从1开始。在字段或者字节码指令中通过编号可以快速的找到对应的数据
  • 字节码指令中通过编号引用到常量池的过程称之为符号引用

查看源码,有两个常量的内容一致

查看字段,发现有三个字段,常量值索引为#21

根据索引查看常量内容

补充说明:

如果常量名与常量内容相同,那么加载到常量池中,为了节省空间,将直接指向内容,而非先指向类型再指向内容

接口

字段

方法

字节码中的方法区是存放字节码指令的核心位置,字节码指令的内容存放在方法的Code属性中

方法部分,换一个代码案例

public class MyTest {
    public static void main(String[] args) {
        int i = 0;
        int j = i + 1;
    }
}

这些指令是什么意思呢?可以查看源文档

左键点击指令,查看规范

字面意思就是将这个指令放入操作栈,至于操作栈,下面我会说明

局部变量表是根据源代码从上到下、从右到左执行的顺序,加载到局部变量表的

案例分析

初始环境

先根据顺序加载所有局部变量到局部变量数组中

这个就是局部变量表

iconst_0

对于int i = 0;先将常量池的常量0加载到操作数栈

istore_1

对于int i = 0;再将操作数栈中的0存入局部变量数组下标为1的局部变量i中

iload_1 & iconst_1

对于int j = i + 1;将局部变量数组下标为1的局部变量i的值(0)加载到操作数栈, 并且将常量1加载到操作数栈

iadd

对于int j = i + 1;对操作数栈中的常量们进行加法运算

istore_2

对于int j = i + 1;将操作数栈中的常量值存入局部变量数组[2]的局部变量j中

return

结束方法(main方法)

JVM清空操作数栈,局部变量数组被销毁,内存空间释放

现在再来分析一个案例

案例分析

public class MyTest {
    public static void main(String[] args) {
        int i = 0;
        i = i++;
    }
}

初始环境

iconst_0

对于int i = 0;先加载常量值0到操作数栈中

istore_1

对于int i = 0;再将常量值0弹入到局部变量i

iload_1

对于i = i++;从语法上看,是先执行i++,将局部变量i的值加载到操作数栈中

iinc 1 by 1

查看源文档

所以是用常数1增加局部变量i

注意看细节,这次的加法是在局部变量中运行的,这也是回答面试的关键点

istore_1

将操作数栈的常量值存入局部变量i,也就是将0替换了1,导致最终结果还是0

return

JVM清空操作数栈,局部变量数组被销毁,内存被释放

最后回答,这种从字节码指令层面的回复,会比Java语法层面的"先增后增"回复好一些

Java语法层面说 n = i++是先返回值给n,再自增,对应到字节码指令层面,就是先iload_ 到操作数栈,n获取的是操作数栈中的数,再 iinc 1 by 1自增

如果是++i

案例分析

public class MyTest {
    public static void main(String[] args) {
        int i = 0, j = 0, k = 0;
        i++;
        j = j + 1;
        k += 1;
    }
}

这么看的话,性能排序是 (i++) = (k+=1) > (j = j + 1)

属性

常用工具

javap -v命令

  • javap是JDK自带的反编译工具,可以通过控制台查看字节码文件的内容。适合在服务器上查看字节码文件内 容
  • 直接输入javap查看所有参数。
  • 输入javap -v 字节码文件名称 查看具体的字节码信息。(如果jar包需要先使用 jar –xvf命令解压)

IDEA插件-jclasslib

这款软件也有idea插件版本,建议开发时使用IDEA插件版本

阿里Arthas

Java应用诊断器,一款宝藏工具,这里我必须吹一波阿里云,真的很棒!下文会频繁的使用这个工具

简介 | arthas (aliyun.com)

链接:https://arthas.aliyun.com/arthas-boot.jar

还有很多中下载安装方式,自己查看官网

入门使用
public class MyTest {
    public static void main(String[] args) throws Exception{
        while (true){
            Thread.sleep(1000);
        }
    }
}

这个代码死循环睡眠,模拟程序持续运行

运行jar包

会显示所有Java相关进程,我们要用第4个

输入4,回车

看到当前程序的进程是2388,然后下面的所有命令都只对2388这个进程生效

监控面板

arthas的功能很多,本章节先学习监控面板&&查看字节码信息这两个功能,其余的后面可能会出现

查看官方文档

dashboard -i 2000 -n 3

查看字节码信息

dump命令

dump -d [存放路径] [目标类的全限定名]

jad命令

反编译命令

案例分析


类的生命周期(重点)

类的生命周期描述了一个类加载、使用、卸载的整个过程

类的生命周期是高频面试、笔试点,也是后续大量知识点的基础部分,很重要

比如下列的输出结果是:

public class MyTest {
    public static void main(String[] args) {
        System.out.print("A");
        new MyTest();
        new MyTest();
    }

    public MyTest(){
        System.out.print("B");
    }

    {
        System.out.print("C");
    }

    static{
        System.out.print("D");
    }
}

public class MyTest {
    public static void main(String[] args) {
        new BO2();
        System.out.println(BO2.a);
    }
}

class AO2{
    static int a = 0;
    static {
        a = 1;
    }
}

class BO2 extends AO2{
    static {
        a = 2;
    }
}

应用场景

生命周期概述

加载阶段

1.加载(Loading)阶段的第一步是类加载器根据类的全限定名,通过不同渠道以二进制流的方式获取字节码信息

开发者可以使用Java代码拓展不同的渠道

2.类加载器在加载完类之后,Java虚拟机会将字节码中的信息保存到方法区

3.字节码信息加载到方法区中之后,JVM在方法区生成一个InstanceKlass对象,保存类的所有信息,里面还包含实现特定功能比如多态的信息

4.同时,JVM还会在堆中生成一份与方法区中数据类似的java.lang.Class对象

作用是:在Java代码中去获取类的信息&&存储静态字段的数据(JDK8及之后)

对于开发者来说,只需要访问堆中的Class对象而不需要访问方法区中所有信息,这样JVM就能很好地控制开发者访问数据的范围

查看内存中的对象

推荐使用JDK自带的hsdb工具查看JVM内存信息。

在JDK/lib目录下

进到lib目录,启动命令

java -cp sa-jdi.jar sun.jvm.hotspot.HSDB

输入java进程PID连接,查看虚拟机内存信息

连接阶段

验证阶段

1.连接(Linking)阶段的第一个环节是验证,验证的主要目的是检测Java字节码文件是否遵守了《Java虚拟机规范》中的约束这个阶段一般不需要开发者参与

2.主要包含如下四个部分,具体详见《Java虚拟机规范》:

  • 文件格式验证,比如文件是否以0xCAFEBABE开头,也就是魔数,主次版本号是否满足当前Java虚拟机版本要求
  • 元信息验证,例如类必须有父类(则super不能为空)
  • 验证程序执行指令的语义,比如方法内的指令执行中跳转到不正确的位置
  • 符号引用验证,例如是否访问了其他类中private的方法等
版本号的检测

Hotspot JDK8中虚拟机源码对版本号检测的代码如下,你能读懂它的含义吗:

准备阶段
静态变量分配内存&初始值
  • 准备阶段为静态变量(static)分配内存设置初始值
  • 注意:本章涉及到的内存结构只讨论JDK8及以后的版本
  • 准备阶段只会给静态变量赋初始值,而每一种基本数据类型和引用数据类型都有其初始值
  • final修饰的基本数据类型的静态变量,准备阶段直接会将代码中的值进行赋值,而没有初始化阶段

解析阶段

解析阶段主要是常量池中的符号引用替换为内存地址直接引用

符号引用就是在字节码文件中使用编号来访问常量池中的内容

直接引用不再使用编号,而是使用内存中地址进行访问具体的数据

初始化阶段

准备阶段:为静态变量(static)分配内存&设置初始值

初始化阶段:为静态变量(static)赋值,执行静态代码块中的代码

注意两者的区别,途中value的最终值应该是1,而不是0

初始化阶段会执行字节码文件中clinit部分的字节码指令。

public class MyTest {
    public static int value = 1;
    static{
        value = 2;
    }
    public static void main(String[] args) {

    }
}

重点来了

何时初始化?

(访问一个类的静态变量或者静态方法,注意变量是final修饰的并且等号右边是常量不会触发初始化。)

1.调用Class.forName(String className)。

2.new一个该类的对象时。

3.执行Main方法的当前类。

4.直接引用静态变量或静态方法

5.使用反射机制访问类的构造器(例如 Constructor<?> constructor = Class.forName("A").getDeclaredConstructor();

6.静态导入(比如 import static ...

7.如果有一个枚举类型,并且在其中使用了静态代码块,访问任何枚举值时也会导致整个枚举类被加载,这样其静态代码块会被执行。

clinit指令

clinit指令在特定情况下不会出现,比如:如下几种情况是不会进行初始化指令执行的。

1.无静态代码块且无静态变量赋值语句。

2.有静态变量的声明,但是没有赋值语句。

3.静态变量的定义使用final关键字,这类变量会在准备阶段直接进行初始化。

初始化的要点

数组的创建不会导致数组中元素的类进行初始化

final修饰的变量如果赋值的内容需要执行指令才能得出结果,会执行clinit方法进行初始化

案例分析

面试题一

public class MyTest {
    public static void main(String[] args) {
        System.out.print("A");
        new MyTest();
        new MyTest();
    }

    public MyTest(){
        System.out.print("B");
    }

    {
        System.out.print("C");
    }

    static{
        System.out.print("D");
    }
}

先执行clinit部分,也就是static代码块

先搞清楚新指令的作用,查看文档

getstatic

从字节码中获取静态字段值,载入操作栈顶中

ldc

将常量池中的值载入操作数栈中

invokevirtual

处理栈顶操作

OK,现在分析字节码指令

第一行,通过 getstatic 获取 System.out 的 PrintStream 对象,并将其放入操作数栈顶。

也就是定义中的获取静态字段(System.out)的值(PrintStream对象)

这个PrintStream对象就是用来打印的

第二行,从常量池取出字符串D(将常量存入常量池就编译源代码的时候做的,并不需要什么显式的字节码指令,这里可以直接从常量池获取),取出后加载到操作数栈上

第三行,invokevirtual调用printStream类的print方法,打印栈顶的字符串D

第四行,return结束静态代码块

执行main方法前,首先初始化类的static代码块
输出结果:D

现在开始执行main方法

前三行
输出结果:A

第四行,new创建MyTest类的实例,是对象的一个引用(此时对象还没有初始化),也就是所谓的this,还将此引用推送到操作数栈中

第五行,dup复制一份对象的引用,放在操作数栈顶上(每次调用对象都会dup一次,应该是为了每个引用互不影响)

第六行,invokespecial调用构造方法开始初始化刚刚new出来的空对象

看3 - 5行,打印字符串C

看6 - 8行,打印字符串B

书接上回

第七行,pop弹出栈顶的对象引用,对象引用处理完毕

8 - 11行,重复一次上述操作

输出结果:CBCB

案例分析

public class MyTest {
    public static void main(String[] args) {
        System.out.println(BO2.a);
    }
}

class AO2{
    static int a = 0;
    static {
        a = 1;
    }
}

class BO2 extends AO2{
    static {
        a = 2;
    }
}

分析:初始化的条件有三个,上面我记的有


所以B02.a这一行就先获取的是父类A02的静态变量a,执行了父类A02的static代码块,将a赋值为1

此时字节码执行return结束了静态代码块的初始化操作

这时不再执行子类B02的static代码块,理由是,初始化操作通过return指令已经结束,如果要执行子类B02的static代码块,需要重新初始化,比如可以加一行new B02();

new的操作会再次访问B02的父类A02,发现AO2的静态代码块已经加载过了,不会再触发了,于是开始加载子类的,所以会赋值a为2

案例分析

这个好理解,new的数据,分配了10个空间,每个元素都在准备阶段设置了初始值,A类的默认值是null,所以这个数组的元素全是null

再来复习一下初始化的条件

案例分析

public class MyTest {
    public static void main(String[] args) {
        System.out.println(A.a);
    }
}

class A{
    // public static final int a = 1;
    public static final int a = Integer.valueOf(1);
    static {
        System.out.println("A类的静态代码块初始化");
    }
}

常量折叠

引入一个概念,编译时常量,A类的public static final int a = 1;

JVM做了优化,在编译时编译器直接将a的值嵌入到代码中,不需要运行时初始化了,直接跳过了初始化阶段

public class MyTest {
    public static void main(String[] args) {
        System.out.println(A.a);
    }
}

class A{
     public static final int a = 1;
//    public static final int a = Integer.valueOf(1);
    static {
        System.out.println("A类的静态代码块初始化");
    }
}

仔细对比字节码指令,发现第二种,压根没有getstatic A的clinit初始化操作,而是iconst_1直接从常量池把常量1拿到了操作数栈中,初始化操作直接跳过了

下一章:【JVM】JVM基础教程(二)-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mybaits的优点缺点?

大家好&#xff0c;我是锋哥。今天分享关于【Mybaits的优点&缺点?】面试题。希望对大家有帮助&#xff1b; Mybaits的优点&缺点? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 MyBatis 是一个优秀的持久层框架&#xff0c;通常用于 Java 应用程序中&…

gulp应该怎么用,前端批量自动化替换文件

背景 最近公司准备把所有项目中用到的国际化相关的key规范化&#xff0c;原因是: 一直以来公司的app和web端 在针对相同的需求以及相同的国际化语言&#xff0c;需要设置不同的两份国际化文件&#xff0c;难以维护旧版的国际化文件中&#xff0c;存在的大量值重复&#xff0c…

POI遍历行所有单元格的两种方式,getPhysicalNumberOfCells方式有问题,勿用

今天看POI源码的时候&#xff0c;发现HSSFWorkbook类型的工作簿&#xff0c;行数据是用TreeMap<Integer, HSSFRow>存储的&#xff0c;列数据是用HSSFCell[]数组来存的&#xff1b;XSSFWorkbook类型的工作簿&#xff0c;行数据是用SortedMap<Integer, XSSFRow>存储的…

NUMA-非统一内存访问架构

NUMA&#xff08;Non-Uniform Memory Access&#xff09; 是一种计算机内存架构&#xff0c;主要用于多处理器系统。NUMA架构中的每个处理器都连接到自己的本地内存&#xff0c;并且可以访问其他处理器的内存&#xff0c;但访问其他处理器的内存速度较慢。 内核通过调度优化进…

WPF+LibVLC开发播放器-LibVLC在C#中的使用

LibVLC在C#中的使用 安装包Nuget使用控件使用播放器初始化加载视频文件 视频教程&#xff1a; 使用WPFLibVLC快速开发一个播放器 安装包Nuget 安装下面两个包,必须安装两个 一个是相关框架对应的包&#xff0c;Winform就安装LibVLCSharp.Winform;WPF就安装LibVLCSharp.WPF&am…

用GPT零负担学单片机之点亮一颗cpu 第3节 训练or特征匹配?用GPT开发嵌入式

用GPT零负担学单片机之点亮一颗cpu 第3节 训练or特征匹配&#xff1f;AI写代码 大家好,我是小杰学长 如果你是大学生 遇到电子技术 学习 成长 入行难题 我曾经通过大学比赛赚钱 从事嵌入式AI 航天军工 用特别的学习和求职方法线下半年带50学弟学妹入行开发 主页佳喔威信&…

基于Java Springboot在线招聘APP且微信小程序

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 微信…

动力商城-05 阿里云短信服务

1.添加依赖 <dependency><groupId>com.aliyun</groupId><artifactId>dysmsapi20170525</artifactId><version>2.0.24</version></dependency>2.控制层 Api(tags "短信业务接口管理") RequestMapping("p/sms&…

深入解析 HTML Input 元素:构建交互性表单的核心

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

HAMR技术进入云存储市场!

2024年12月3日&#xff0c;Seagate宣布其Mozaic 3系列HAMR&#xff08;热辅助磁记录&#xff09;硬盘获得了来自一家领先云服务提供商&#xff08;可能AWS、Azure或Google Cloud其中之一&#xff09;以及其他高容量硬盘客户的资格认证。 Seagate的Mozaic 3技术通过引入热辅助磁…

图数据库 | 12、图数据库架构设计——高性能计算架构

在传统类型的数据库架构设计中&#xff0c;通常不会单独介绍计算架构&#xff0c;一切都围绕存储引擎展开&#xff0c;毕竟存储架构是基础&#xff0c;尤其是在传统的基于磁盘存储的数据库架构设计中。 类似地&#xff0c;在图数据库架构设计中&#xff0c;项目就围绕存储的方…

【工具变量】地级市城市全社会用电量数据(2006-2021年)

一、数据范围&#xff1a;覆盖中国300多个地级市 二、包含指标&#xff1a; 省份、地级市、年份、全社会用电量。 三、数据来源&#xff1a;国家电网查询数据。对于极大部分城市&#xff0c;国网售电量就是全社会用电量(往年的售电量和全社会用电量数据相同&#xff09;,此外…

请求响应:常见参数接收及封装(数组集合参数及日期参数)

数组参数 在前端页面的表单中&#xff0c;存在复选框元素&#xff0c;当提交表单到后端的时候&#xff0c;会将复选框中的全部内容提交到后端进行处理&#xff0c;由于复选框中往往存在很多数据&#xff0c;并且同复选框中数据名称相同&#xff0c;这样的请求参数叫做数组参数…

兔子的寿命有多长?

在宠物的世界里&#xff0c;兔子以其灵动的身姿、柔软的皮毛和温顺的性格深受人们喜爱。然而&#xff0c;当我们满心欢喜地将兔子迎进家门时&#xff0c;可曾想过它们能陪伴我们多久&#xff1f;兔子的寿命&#xff0c;是一个值得深入探讨的话题&#xff0c;它不仅关乎生命的时…

本地多卡(3090)部署通义千问Qwen-72B大模型提速实践:从龟速到够用

最近在做文本风格转化&#xff0c;涉及千万token级别的文本。想用大模型转写&#xff0c;在线的模型一来涉及数据隐私&#xff0c;二来又不想先垫钱再找报销。本地的7-9B小模型又感觉效果有限&#xff0c;正好实验室给俺配了4卡3090的机子&#xff0c;反正也就是做个推理&#…

鸿蒙开发——键值型数据库的基本使用与跨设备同步

1、简 述 ❓ 什么是键值型数据库 键值型数据库&#xff08;KV-Store&#xff09;是一种非关系型数据库&#xff0c;其数据以“键值”对的形式进行组织、索引和存储&#xff0c;其中“键”作为唯一标识符。 键值型数据库适合很少数据关系和业务关系的业务数据存储。 另外&#…

STM32一keil5更换芯片后报错问题的解决。

目录 一、STM32型号认识二、报错问题三、常用的启动配置文件四、问题解决 一、STM32型号认识 二、报错问题 当我们在原来工程下修改芯片时&#xff0c;原本可以编译通过的代码突然很多报错。如下所示&#xff0c;这是因为我们的启动文件配置错误。对于不同型号的芯片其flash容量…

CentOS安装Nginx并配置为系统服务

前言 Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器 [13]&#xff0c;同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点&#xff08;俄文&#xff1a;Рамблер&#xff09;开发的&#xff0c;公开版本1.19.6发布…

部署loki,grafana 以及springcloud用法举例

文章目录 场景docker 部署grafanadocker-compose部署loki维护配置文件 local-config.yaml维护docker-compose.yml配置启动 grafana 添加loki数据源springcloud用法举例查看loki的explore,查看日志 场景 小公司缺少运维岗位&#xff0c;需要研发自己部署日志系统&#xff0c;elk…

快速学习selenium基础操作

全篇大概19000字&#xff08;含代码&#xff09;&#xff0c;建议阅读时间1h 什么是Selenium&#xff1f; Selenium是一系列自动化工具集的统称&#xff0c;官方工具有 Selenium IDE、Selenium WebDriver、Selenium Grid&#xff0c; 主要用于桌面端Web应用程序的自动化。能够通…