深入JVM:探索Java虚拟机

news2024/9/23 15:30:09

文章目录

      • 1. JVM简介
          • 1.1 定义与核心作用
          • 1.2 JVM的跨平台特性
      • 2. JVM内部结构深度探索
          • 2.1 类加载机制
            • 2.1.1 双亲委派模型
            • 2.1.2 OSGI框架
            • 2.1.3 类加载器分类
          • 2.2 JVM运行时数据区
            • 2.2.1 程序计数器
            • 2.2.2 本地方法栈
            • 2.2.3 Java虚拟机栈
          • 2.2.4 堆
          • 2.2.5 元数据区
      • 2.3 JVM内存区域的性能调优实践
          • 2.3.1 调优堆内存
          • 2.3.2 调优元数据区
          • 2.3.3 调优垃圾回收策略
      • 3. JVM垃圾回收机制
          • 3.1 常见GC收集器
            • 3.1.1 串行收集器
            • 3.1.2 CMS(并发标记清除)收集器
            • 3.1.3 并行收集器
            • 3.1.4 G1收集器
          • 3.2 垃圾回收算法
            • 3.2.1 复制算法
            • 3.2.2 标记-清除算法
            • 3.2.3 标记-整理算法
          • 3.3 JVM内存分区
            • 3.3.1 年轻代
            • 3.3.2 老年代
            • 3.3.3 元数据区
      • 4. JVM工具与性能调优
          • 4.1 jmap:Java内存映射工具
            • 4.1.1 生成堆转储
            • 4.1.2 查看堆配置信息
          • 4.2 jhat:Java堆分析工具
            • 4.2.1 启动jhat
            • 4.2.2 查询对象
          • 4.3 jstack:Java线程堆栈跟踪工具
            • 4.3.1 生成线程堆栈跟踪
          • 4.4 jinfo:Java配置信息工具
            • 4.4.1 查看JVM标志
            • 4.4.2 修改JVM标志
          • 4.5 jps:Java进程状态工具

博客思维导图

在这里插入图片描述

1. JVM简介

Java虚拟机(JVM)是Java技术的核心组件,它为Java的跨平台特性提供了基础。JVM不仅仅是一个虚拟机,它是一个完整的运行环境,负责加载、验证、编译和执行Java字节码。在本节中,我们将深入探讨JVM的定义、核心作用以及其跨平台的特性。

1.1 定义与核心作用

Java虚拟机是一个虚拟的计算机实例,它使得Java应用程序可以在任何设备或操作系统上运行,只要该设备或操作系统有一个JVM实现。JVM的主要任务是加载.class文件(Java字节码)并执行它们。这些字节码文件是由Java编译器从.java源文件编译而来的。

JVM的核心作用可以归纳为以下几点:

  • 加载字节码:JVM负责从文件系统或网络资源加载.class文件。

  • 字节码验证:确保加载的字节码是有效的、安全的,并且不会破坏JVM的内部数据结构。

  • 即时编译:JVM可以使用即时编译器(JIT)将字节码转换为本地机器代码,以提高执行速度。

  • 执行程序:JVM创建并管理所有的程序资源,如线程、内存空间和I/O操作,并执行字节码。

  • 提供内置库:JVM提供了Java API,这是一组预编译的类库,可以为Java应用程序提供核心功能。

1.2 JVM的跨平台特性

Java的口号是“一次编写,到处运行”。这得益于Java的跨平台特性,而这一特性的实现则依赖于JVM。

当Java程序被编译时,它被转换为与平台无关的字节码,而不是特定于某个操作系统的机器代码。这意味着,只要一个设备安装了JVM,它就可以运行任何Java应用程序,无论这个程序最初是在哪个平台上编写的。

这种方式的好处是显而易见的:

  • 可移植性:Java应用程序可以在任何安装了JVM的设备上运行,无需进行任何修改。

  • 安全性:由于Java应用程序在虚拟机上运行,它们与底层操作系统隔离,这为应用程序提供了一个安全的执行环境。

  • 性能:尽管Java应用程序在虚拟机上运行,但通过即时编译技术,它们的执行速度可以与本地应用程序相媲美。

  • 集成性:Java应用程序可以与其他语言编写的本地应用程序进行交互,这为复杂的应用程序集成提供了便利。

总之,JVM为Java技术提供了坚实的基础,使其成为当今最受欢迎的编程语言之一。通过深入了解JVM,我们可以更好地理解Java的工作原理,从而更有效地编写和优化Java应用程序。

2. JVM内部结构深度探索

Java虚拟机(JVM)是一个复杂的系统,负责执行Java字节码并提供Java应用程序的运行环境。为了更好地理解Java程序的运行机制,我们需要深入探讨JVM的内部结构和其工作原理。

image-20230915211004027


2.1 类加载机制

在Java中,类的加载、链接和初始化是JVM执行Java程序的基础。这些过程确保Java类正确、安全地加载到JVM中。

2.1.1 双亲委派模型

双亲委派模型是Java类加载的核心机制。它确保Java核心库的安全性,防止恶意代码篡改核心类库。

工作原理:
当一个类加载器尝试加载一个类时,它首先会请求其父类加载器来完成这个任务。这个过程会一直递归到启动类加载器;只有当父类加载器不能完成这个任务时,子类加载器才会尝试自己加载这个类。

// 示例代码:自定义类加载器
public class CustomClassLoader extends ClassLoader {
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        // 委派给父类加载器
        return super.loadClass(name);
    }
}

代码解释: 上述代码中,我们创建了一个自定义的类加载器。当调用loadClass方法时,它会首先委派给其父类加载器。

面试题(阿里云)

什么是双亲委派机制?介绍一些运作过程,双亲委派模型的好处;

如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都不愿意干活,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己想办法去完成,这不就是传说中的双亲委派模式。

动作过程image-20230915210629324

好处

沙箱安全机制:自己写的String.class类不会被加载,这样便可以防止核心API库被随意篡改避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次

2.1.2 OSGI框架

OSGI(Open Service Gateway Initiative)是一个Java模块化框架,它允许应用程序动态地安装、启动、停止和卸载模块。

工作原理:
OSGI框架使用自己的类加载器来加载模块。这允许模块之间有自己的类版本,避免了类版本冲突的问题。

// 示例代码:OSGI BundleActivator
public class MyActivator implements BundleActivator {
    public void start(BundleContext context) {
        System.out.println("Module started");
    }

    public void stop(BundleContext context) {
        System.out.println("Module stopped");
    }
}

代码解释: 上述代码是一个简单的OSGI激活器,它在模块启动和停止时打印消息。

2.1.3 类加载器分类

在JVM中,类加载器被分为三种:

  • 启动类加载器(Bootstrap ClassLoader): 负责加载JVM核心类库,如java.lang.*

  • 扩展类加载器(Extension ClassLoader): 负责加载Java的扩展库,如javax.*

  • 应用类加载器(Application ClassLoader): 负责加载应用程序的类路径、模块路径等。

// 示例代码:获取类加载器
ClassLoader loader = MyClass.class.getClassLoader();
System.out.println(loader);

代码解释: 上述代码获取MyClass的类加载器并打印它。


2.2 JVM运行时数据区

JVM在执行Java程序时,会使用多个内存区域来存储数据。了解这些区域及其用途对于优化性能和诊断问题至关重要。

2.2.1 程序计数器

程序计数器是一个小的内存区域,它存储了当前线程正在执行的字节码的地址。

工作原理:
当JVM执行一个方法时,程序计数器会指向这个方法的第一条字节码指令。随着字节码指令的执行,程序计数器的值会递增。

// 示例代码:模拟程序计数器
public class ProgramCounterSimulation {
    public static void main(String[] args) {
        int counter = 0; // 模拟程序计数器
        method1();
        counter += 3; // 假设method1有3条字节码指令
        method2();
        counter += 2; // 假设method2有2条字节码指令
    }

    public static void method1() {
        // ...
    }

    public static void method2() {
        // ...
    }
}

代码解释: 上述代码模拟了程序计数器的工作原理。当调用一个方法时,程序计数器的值会递增,反映了字节码指令的执行。

2.2.2 本地方法栈

本地方法栈是一个内存区域,它存储了Java方法的本地变量、返回地址和其他数据。

工作原理:
当JVM调用一个方法时,它会为这个方法创建一个栈帧并压入本地方法栈。当这个方法返回时,它的栈帧会被弹出。

// 示例代码:模拟本地方法栈
public class LocalMethodStackSimulation {
    public static void main(String[] args) {
        method1();
        method2();
    }

    public static void method1() {
        int localVariable1 = 10; // 存储在本地方法栈中
        // ...
    }

    public static void method2() {
        String localVariable2 = "Hello"; // 存储在本地方法栈中
        // ...
    }
}

代码解释: 上述代码模拟了本地方法栈的工作原理。每个方法的本地变量都存储在本地方法栈中。

2.2.3 Java虚拟机栈

Java虚拟机栈是一个内存区域,它存储了Java方法的操作数栈、局部变量表和其他数据。

工作原理:
与本地方法栈类似,当JVM调用一个方法时,它会为这个方法创建一个栈帧并压

入Java虚拟机栈。但与本地方法栈不同的是,Java虚拟机栈还存储了操作数栈,这是一个用于存储计算过程中的中间结果的栈。

// 示例代码:模拟Java虚拟机栈
public class JVMStackSimulation {
    public static void main(String[] args) {
        int result = add(10, 20); // 操作数栈存储10和20,然后存储30(结果)
        System.out.println(result);
    }

    public static int add(int a, int b) {
        int sum = a + b; // 操作数栈存储a和b的值,然后存储它们的和
        return sum;
    }
}

代码解释: 上述代码模拟了Java虚拟机栈的工作原理。add方法的操作数栈首先存储ab的值,然后存储它们的和。

2.2.4 堆

堆是JVM中的一个重要的内存区域,用于存储对象实例。它被划分为年轻代和老年代,以优化垃圾回收性能。

工作原理:
新创建的对象首先被分配在年轻代。随着时间的推移,存活的对象会从年轻代移动到老年代。垃圾回收主要发生在年轻代,因为大多数对象很快就会变得不可达。

// 示例代码:创建对象
public class HeapSimulation {
    public static void main(String[] args) {
        Person person = new Person("Alice", 25); // 对象被分配在堆上
    }
}

class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

代码解释: 上述代码创建了一个Person对象,这个对象被分配在堆上。

2.2.5 元数据区

元数据区用于存储JVM加载的类的元数据,如类的名称、字段和方法。这个区域不是堆的一部分,它有自己的垃圾回收策略。

工作原理:
当JVM加载一个类时,它会将这个类的元数据存储在元数据区。这个区域是固定大小的,如果它被填满,JVM会触发垃圾回收来回收不再使用的类的元数据。

// 示例代码:加载类
public class MetadataAreaSimulation {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("com.example.MyClass"); // 类的元数据被存储在元数据区
    }
}

代码解释: 上述代码加载了一个类,并将其元数据信息存储在元数据区。

2.3 JVM内存区域的性能调优实践

理解JVM的内存区域对于Java应用程序的性能调优至关重要。通过调整这些区域的大小和参数,我们可以优化应用程序的性能,减少垃圾回收的暂停时间,并提高系统的吞吐量。

2.3.1 调优堆内存

实践案例:
假设一个Web应用程序在高并发情况下经常出现OutOfMemoryError。通过分析,我们发现这是因为堆内存设置得太小。

解决方案:
增加堆的最大大小。例如,将最大堆大小设置为2GB:

java -Xmx2g -jar my-web-app.jar

建议:

  • 使用监控工具(如JVisualVM或JMC)定期检查堆的使用情况。
  • 如果应用程序有大量的短暂对象,考虑增加年轻代的大小。
  • 如果应用程序有大量的长时间存活的对象,考虑增加老年代的大小。
2.3.2 调优元数据区

实践案例:
一个应用程序在运行时动态生成并加载了大量的类。随着时间的推移,应用程序抛出了OutOfMemoryError: Metaspace错误。

解决方案:
增加元数据区的大小。例如,将元数据区的最大大小设置为256MB:

java -XX:MaxMetaspaceSize=256m -jar my-dynamic-app.jar

建议:

  • 如果应用程序使用了大量的动态代理或CGLIB,考虑增加元数据区的大小。
  • 使用-XX:MetaspaceSize参数设置元数据区的初始大小,以避免频繁的扩展。
2.3.3 调优垃圾回收策略

实践案例:
一个在线交易应用程序在高并发情况下经常出现长时间的垃圾回收暂停,导致用户体验下降。

解决方案:
切换到低延迟的垃圾回收器,如G1或ZGC:

java -XX:+UseG1GC -jar my-trading-app.jar

建议:

  • 根据应用程序的需求选择合适的垃圾回收器。例如,对于低延迟应用程序,G1或ZGC可能是一个好选择;对于高吞吐量应用程序,Parallel GC可能更合适。
  • 使用-XX:GCTimeRatio-XX:MaxGCPauseMillis参数来调整垃圾回收的行为。

3. JVM垃圾回收机制

Java虚拟机(JVM)的垃圾回收(GC)机制是Java内存管理的核心组成部分。它自动回收不再使用的对象,从而释放内存。在本节中,我们将深入探讨JVM的垃圾回收机制,包括常见的GC收集器、垃圾回收算法以及JVM的内存分区。

3.1 常见GC收集器

Java提供了多种GC收集器,每种收集器都有其特定的应用场景和优势。选择合适的收集器可以显著提高应用程序的性能。

3.1.1 串行收集器

串行收集器是最简单的GC收集器,它在单线程环境中工作,并在进行垃圾回收时暂停所有的应用线程。

优势:

  • 适用于单线程应用程序。
  • 由于没有线程切换的开销,它在单线程环境中通常比其他收集器更快。

缺点:

  • 不适用于多线程应用程序,因为它会导致长时间的暂停。
// 启用串行收集器
// JVM参数: -XX:+UseSerialGC
3.1.2 CMS(并发标记清除)收集器

CMS收集器是一种并发收集器,它在标记和清除阶段与应用线程并发执行,从而减少暂停时间。

优势:

  • 适用于响应时间要求严格的应用程序。
  • 并发执行,减少暂停时间。

缺点:

  • 可能导致较高的CPU使用率。
  • 由于它不进行压缩,可能导致内存碎片。
// 启用CMS收集器
// JVM参数: -XX:+UseConcMarkSweepGC
3.1.3 并行收集器

并行收集器在多线程环境中工作,它在垃圾回收时使用多个线程。

优势:

  • 适用于多线程应用程序。
  • 可以充分利用多核CPU。

缺点:

  • 在垃圾回收时会暂停所有的应用线程。
// 启用并行收集器
// JVM参数: -XX:+UseParallelGC
3.1.4 G1收集器

G1收集器是一种面向区域的收集器,它将堆分为多个区域,并优先回收垃圾最多的区域。

优势:

  • 可以预测暂停时间,从而满足响应时间的要求。
  • 高效地利用多核CPU和大量内存。

缺点:

  • 可能需要更多的CPU资源。
// 启用G1收集器
// JVM参数: -XX:+UseG1GC

面试题:
GC分哪两种,Minor GC 和Full GC有什么区别?什么时候会触发Full GC?分别采用什么算法?

image-20230915210931693

对象从新生代区域消失的过程,我们称之为 “minor GC

对象从老年代区域消失的过程,我们称之为 “major GC

Minor GC

清理整个YouGen的过程,eden的清理,S0\S1的清理都会由于MinorGC Allocation

Failure(YoungGen区内存不足),而触发minorGC

Major GC

OldGen区内存不足,触发Major GC

Full GC

Full GC 是清理整个堆空间—包括年轻代和永久代

Full GC 触发的场景

1)System.gc

2)promotion failed (年代晋升失败,比如eden区的存活对象晋升到S区放不下,又尝试直接晋升到Old区又放不下,那么Promotion Failed,会触发FullGC)

3)CMS的Concurrent-Mode-Failure

由于CMS回收过程中主要分为四步: 1.CMS initial mark 2.CMS Concurrent mark 3.CMS remark 4.CMS Concurrent sweep。在2中gc线程与用户线程同时执行,那么用户线程依旧可能同时产生垃圾, 如果这个垃圾较多无法放入预留的空间就会产生CMS-Mode-Failure, 切换为SerialOld单线程做mark-sweep-compact。

4)新生代晋升的平均大小大于老年代的剩余空间 (为了避免新生代晋升到老年代失败)当使用G1,CMS 时,FullGC发生的时候是Serial+SerialOld。当使用ParalOld时,FullGC发生的时候是 ParallNew +ParallOld.

3.2 垃圾回收算法

垃圾回收算法决定了如何识别和回收不再使用的对象。选择合适的算法可以提高垃圾回收的效率。

3.2.1 复制算法

复制算法将堆分为两个相等的区域,每次只使用其中一个区域。当这个区域被填满时,它会将仍然存活的对象复制到另一个区域,并清空当前区域。

优势:

  • 没有内存碎片。
  • 只需要处理存活的对象。

缺点:

  • 堆的有效容量减半。
// 示例代码:复制算法的简化表示
public void copy() {
    for (Object obj : fromSpace) {
        if (isAlive(obj)) {
            toSpace.add(obj);
        }
    }
    fromSpace.clear();
    swap(fromSpace, toSpace);
}

代码解释: 上述代码模拟了复制算法的基本工作原理。它首先遍历fromSpace,将存活的对象复制到toSpace,然后清空fromSpace并交换两个空间。

3.2.2 标记-清除算法

标记-清除算法分为两个阶段:标记阶段和清除阶段。在标记阶段,它会标记所有存活的对象;在清除阶段,它会清除所有未被标记的对象。

img

优势:

  • 不需要移动对象。
  • 可以回收任何不再使用的对象。

缺点:

  • 可能导致内存碎片。
  • 清除阶段可能导致较长的暂停时间。
// 示例代码:标记-清除算法的简化表示
public void markAndSweep() {
    markAllAliveObjects();
    sweepUnmarkedObjects();
}

代码解释: 上述代码模拟了标记-清除算法的基本工作原理。它首先标记所有存活的对象,然后清除所有未被标记的对象。

3.2.3 标记-整理算法

标记-整理算法是标记-清除算法的一个变种,它在标记和清除阶段之间添加了一个整理阶段。在整理阶段,它会移动所有存活的对象,从而消除内存碎片。

img

优势:

  • 没有内存碎片。
  • 可以回收任何不再使用的对象。

缺点:

  • 需要移动对象,可能导致较长

的暂停时间。

// 示例代码:标记-整理算法的简化表示
public void markCompact() {
    markAllAliveObjects();
    compactAliveObjects();
    sweepUnmarkedObjects();
}

代码解释: 上述代码模拟了标记-整理算法的基本工作原理。它首先标记所有存活的对象,然后整理存活的对象,最后清除所有未被标记的对象。

3.3 JVM内存分区

JVM将内存分为几个区域,每个区域都有其特定的用途和垃圾回收策略。

3.3.1 年轻代

年轻代是堆的一部分,它包括Eden区和两个Survivor区。大多数新创建的对象首先被分配到Eden区。当Eden区被填满时,存活的对象会被移动到一个Survivor区,而非存活的对象会被回收。

// 示例代码:创建一个新对象
Object obj = new Object();

代码解释: 上述代码创建了一个新对象,这个对象首先被分配到Eden区。

3.3.2 老年代

老年代是堆的另一部分,它用于存储长时间存活的对象。当一个对象在Survivor区存活了足够长的时间,它会被移动到老年代。

// 示例代码:模拟对象的长时间存活
for (int i = 0; i < 10000; i++) {
    Object obj = new Object();
    // 使用obj...
}

代码解释: 上述代码创建了大量的对象,并使用它们。这些对象可能会被移动到老年代,因为它们存活了足够长的时间。

3.3.3 元数据区

元数据区用于存储JVM加载的类的元数据信息,如类的名称、字段和方法。这个区域不是堆的一部分,它有自己的垃圾回收策略。

// 示例代码:加载一个类
Class<?> clazz = Class.forName("com.example.MyClass");

代码解释: 上述代码加载了一个类,并将其元数据信息存储在元数据区。

4. JVM工具与性能调优

Java虚拟机(JVM)提供了一系列的工具,帮助开发者监控、诊断和优化应用程序的性能。这些工具为我们提供了深入的洞察,使我们能够更好地理解应用程序在运行时的行为。在本节中,我们将详细探讨这些工具的使用方法和它们在性能调优中的应用。


4.1 jmap:Java内存映射工具

jmap是一个用于生成堆转储和内存映射的工具。它可以帮助我们诊断内存泄漏和其他内存相关的问题。

4.1.1 生成堆转储

堆转储是JVM内存的快照,它包含了所有的对象及其引用。通过分析堆转储,我们可以识别内存泄漏和优化内存使用。

# 生成堆转储
jmap -dump:format=b,file=heapdump.hprof <pid>

代码解释: 上述命令会为指定的进程ID生成一个名为heapdump.hprof的堆转储文件。

4.1.2 查看堆配置信息

jmap还可以显示JVM的堆配置信息,这对于调优堆大小和其他相关参数非常有用。

# 查看堆配置信息
jmap -heap <pid>

代码解释: 上述命令会显示指定进程的堆配置信息,包括堆的大小、使用情况和垃圾回收策略。

4.2 jhat:Java堆分析工具

jhat是一个用于分析堆转储的工具。它可以解析hprof文件,并提供一个Web界面来查询数据。

4.2.1 启动jhat
# 使用jhat分析堆转储
jhat heapdump.hprof

代码解释: 上述命令会启动一个Web服务器,默认端口为7000,您可以在浏览器中访问http://localhost:7000来查看分析结果。

4.2.2 查询对象

jhat提供了一个简单的OOQL(Object Oriented Query Language)来查询对象。例如,您可以查询所有的String对象,或者查找特定的对象引用。

4.3 jstack:Java线程堆栈跟踪工具

jstack是一个用于生成线程堆栈跟踪的工具。它可以帮助我们诊断线程死锁、线程饥饿和其他并发问题。

4.3.1 生成线程堆栈跟踪
# 生成线程堆栈跟踪
jstack <pid>

代码解释: 上述命令会为指定的进程ID生成线程堆栈跟踪。这些跟踪信息可以帮助我们识别线程的状态和它们正在执行的任务。

4.4 jinfo:Java配置信息工具

jinfo可以显示和调整运行时的JVM配置。这对于调优JVM参数非常有用。

4.4.1 查看JVM标志
# 查看JVM标志
jinfo -flags <pid>

代码解释: 上述命令会显示指定进程的JVM标志,包括堆大小、垃圾回收策略等。

4.4.2 修改JVM标志
# 修改JVM标志
jinfo -flag +PrintGCDetails <pid>

代码解释: 上述命令会为指定进程启用PrintGCDetails标志,这会导致JVM打印详细的垃圾回收日志。

4.5 jps:Java进程状态工具

jps是一个显示Java进程信息的工具。它可以列出本地机器上运行的所有Java进程。

# 列出所有Java进程
jps -l

代码解释: 上述命令会列出本地机器上运行的所有Java进程及其主类名。

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

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

相关文章

企业架构LNMP学习笔记48

数据结构类型操作&#xff1a; 数据结构&#xff1a;存储数据的方式 数据类型 算法&#xff1a;取数据的方式&#xff0c;代码就把数据进行组合&#xff0c;计算、存储、取出。 排序算法&#xff1a;冒泡排序、堆排序 二分。 key&#xff1a; key的命名规则不同于一般语言…

DataX 概述、部署、数据同步运用示例

文章目录 什么是 DataX&#xff1f;DataX 设计框架DataX 核心架构DataX 部署DataX 数据同步&#xff08;MySQL —> HDFS&#xff09; 什么是 DataX&#xff1f; DataX 是阿里巴巴集团开源的、通用的数据抽取工具&#xff0c;广泛使用的离线数据同步工具/平台。它设计用于支…

数据结构——查找(二叉排序树)

文章目录 前言一、二叉排序树构造二叉排序树步骤构造二叉排序树步骤图二叉排序树的查找二叉排序树查找递归算法二叉排序树查找非递归算法 二叉排序树的插入二叉排序树插入结点——递归算法二叉排序树插入结点——非递归算法 二叉排序树的删除 总结 前言 二叉排序树查找定义 二…

Qt的ui文件不能简单复制

在使用vsQt开发时&#xff0c;直接复制另外一个widget类的ui文件&#xff0c;简单改名成当前类对应的ui文件&#xff0c;会导致编译出错。尽可能使用添加的Qt class自带的ui文件&#xff0c;因为ui文件的配置文件中有许多与当前类相关的字符串&#xff0c;简单复制容易报错。

一年一度的中秋节马上又要到了,给你的浏览器也来点氛围感吧

说在前面 一年一度的中秋节马上又要到了&#xff0c;给你的浏览器也来点氛围感吧 &#x1f315;&#x1f315;&#x1f315; 插件设计 效果 首先我们应该要先确定一下我们想要实现的效果是怎样的&#xff0c;如上图&#xff0c;我们希望在页面上鼠标点击的时候会在点击区域随…

【送书活动】用“价值”的视角来看安全:《构建新型网络形态下的网络空间安全体系》

文章目录 每日一句正能量前言本书概况赠书活动目录 每日一句正能量 成功与失败&#xff0c;幸福与不幸&#xff0c;在各自心里的定义都不会相同。 前言 过去&#xff0c;安全从未如此复杂&#xff1b; 现在&#xff0c;安全从未如此重要&#xff1b; 未来&#xff0c;安全更需如…

Kasisto AI:金融对话人工智能

【产品介绍】​ 名称 Kasisto 成立时间​ Kasisto创立于2013年​。 具体描述 Kasisto 数字体验平台 KAI 为全渠道虚拟助理和聊天机器人提供支持&#xff0c;他们在移动应用程序、 网站、消息传递平台和支持语音的设备上精通银行业…

交换瓶子问题(暴力求解 + 图论解法)

交换瓶子问题 文章目录 交换瓶子问题前言题目描述暴力解法【能过】图论解法知识预备【交换环】 代码暴力做法和图论做法的对比总结 前言 知道题目用暴力算法是可以过的&#xff0c;注意数据范围是1~10000&#xff0c;卡在一个微妙的地方&#xff0c;不免让人想用暴力算法&…

PyTorch深度学习(一)【线性模型、梯度下降、随机梯度下降】

这个系列是实战&#xff08;刘二大人讲的pytorch&#xff09; 建议把代码copy下来放在编译器查看&#xff08;因为很多备注在注释里面&#xff09; 线性模型(Linear Model)&#xff1a; import numpy as npimport matplotlib.pyplot as plt #绘图的包​x_data [1.0, 2.0, …

Cesium 地球网格构造

Cesium 地球网格构造 Cesium原理篇&#xff1a;3最长的一帧之地形(2&#xff1a;高度图) HeightmapTessellator 用于从高程图像创建网格。提供了一个函数 computeVertices&#xff0c;可以根据高程图像创建顶点数组。 该函数的参数包括高程图像、高度数据的结构、网格宽高、…

Gradle的简介、下载、安装、配置及使用流程

Gradle的简介、下载、安装、配置及使用流程 1.Gradle的简介 Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置&#xff0c;也增加了基于Kotlin语言的kotlin-based DSL&#xff0c;抛弃了基于X…

AI项目六:基于YOLOV5的CPU版本部署openvino

若该文为原创文章&#xff0c;转载请注明原文出处。 一、CPU版本DEMO测试 1、创建一个新的虚拟环境 conda create -n course_torch_openvino python3.8 2、激活环境 conda activate course_torch_openvino 3、安装pytorch cpu版本 pip install torch torchvision torchau…

vcruntime140_1.dll修复方法分享,教你安全靠谱的修复手段

在使用Windows操作系统的过程中&#xff0c;我们有时会遇到vcruntime140_1.dll文件丢失或损坏的情况。本文将详细介绍vcruntime140_1.dll的作用&#xff0c;以及多种解决方法和修复该文件时需要注意的问题&#xff0c;希望能帮助读者更好地处理这一问题。 一.vcruntime140_1.dl…

数据结构——【堆】

一、堆的相关概念 1.1、堆的概念 1、堆在逻辑上是一颗完全二叉树&#xff08;类似于一颗满二叉树只缺了右下角&#xff09;。 2、堆的实现利用的是数组&#xff0c;我们通常会利用动态数组来存放元素&#xff0c;这样可以快速拓容也不会很浪费空间&#xff0c;我们是将这颗完…

【Java】SpringData JPA快速上手,关联查询,JPQL语句书写

JPA框架 文章目录 JPA框架认识SpringData JPA使用JPA快速上手方法名称拼接自定义SQL关联查询JPQL自定义SQL语句 ​ 在我们之前编写的项目中&#xff0c;我们不难发现&#xff0c;实际上大部分的数据库交互操作&#xff0c;到最后都只会做一个事情&#xff0c;那就是把数据库中的…

电容 stm32

看到stm32电源部分都会和电容配套使用&#xff0c;所以对电容的作用产生了疑惑 电源 负电荷才能在导体内部自由移动&#xff0c;电池内部的化学能驱使着电源正电附近的电子移动向电源负极区域。 电容 将电容接上电池&#xff0c;电容的两端一段被抽走电子&#xff0c;一端蓄积…

【STL容器】vector

文章目录 前言vector1.1 vector的定义1.2 vector的迭代器1.3 vector的元素操作1.3.1 Member function1.3.2 capacity1.3.3 modify 1.4 vector的优缺点 前言 vector是STL的容器&#xff0c;它提供了动态数组的功能。 注&#xff1a;文章出现的代码并非STL库里的源码&#xff0c…

C++ PrimerPlus 复习 第三章 处理数据

第一章 命令编译链接文件 make文件 第二章 进入c 第三章 处理数据 文章目录 C变量的命名规则&#xff1b;C内置的整型——unsigned long、long、unsigned int、int、unsigned short、short、char、unsigned char、signed char和bool&#xff1b;如何知道自己计算机类型宽度获…

Jenkins Maven pom jar打包未拉取最新包解决办法,亲测可行

Jenkins Maven pom jar打包未拉取最新包解决办法&#xff0c;亲测可行 1. 发布新版的snapshots版本的jar包&#xff0c;默认Jenkins打包不拉取snapshots包2. 设置了snapshot拉取后&#xff0c;部分包还未更新&#xff0c;需要把包版本以snapshot结尾3. IDEA无法更新snapshots包…

超炫的开关效果

超炫的开关动画 代码如下 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>Switch</title><style>/* 谁家好人 一个按钮写三百多行样式代码 &#x1f622;&#x1f622;*/*,*:after,*:before {box-sizing: bor…