Java虚拟机:类的加载机制

news2024/9/23 17:20:04

大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 034 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自己的技术栈的同学。与此同时,本专栏的所有文章,也都会准备充足的代码示例和完善的知识点梳理,因此也十分适合零基础的小白和要准备工作面试的同学学习。当然,我也会在必要的时候进行相关技术深度的技术解读,相信即使是拥有多年 Java 开发经验的从业者和大佬们也会有所收获并找到乐趣。

类加载机制是 JVM 核心功能之一,也是理解 Java 应用程序运行过程的关键。类是如何从字节码被加载到内存中,并最终执行的?这个过程包含了哪些关键步骤?在本篇文章中,我们将详细解析 JVM 的类加载机制,包括类加载器的类型、双亲委派模型及其作用,帮助你深入理解 Java 程序从编译到执行的整个生命周期。


文章目录

      • 1、Java类的加载机制
      • 2、Java类的加载时机
        • 2.1、类的加载过程
        • 2.2、类的加载时机
        • 2.3、被动引用不会初始化
          • 2.3.1、代码示例一
          • 2.3.2、代码示例二
          • 2.3.2、代码示例三
        • 2.4、接口的加载过程
      • 3、Java类的加载过程
        • 3.1、加载
        • 3.2、验证
        • 3.3、准备
        • 3.4、解析
        • 3.5、初始化


1、Java类的加载机制

Java 虚拟机把描述类的数据从 Class 文件(‘.class’ 文件)中加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这个过程被称 Java 类的加载机制。

通俗的讲:当我们去 New 一个对象的时候,首先要保证,这个对象的 Class 文件,已经存在于内存之中。至于为什么我们在 New 的时候不需要去做相应加载内粗的动作?是因为 JVM 自带了类加载器的功能,我们在 New 一个对象的时候,JVM 会去判断内存中是否存在这个 Class 类,如果存在的话,就不用加载;如果不存在的话,就会进行自动加载,将 Class 文件读取至内存中。

在 Java 中,类型的加载、连接和初始化过程都是在程序运行期间完成的,这种策略让 Java 进行提前编译会面临额外的困难,也会让类加载时稍微增加一些性能开销, 但是却为 Java 应用提供了极高的扩展性和灵活性,Java 天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点实现的。

例如,编写一个面向接口的应用程序,可以等到运行时再指定其实际的实现类,从最基础的 Applet、JSP 到相对复杂的 OSGi 技术,都依赖着 Java 语言运行期类加载才 得以诞生。

加载 Class 文件的方式:

  • 从本地系统中直接加载;
  • 通过网络下载 Class 文件;
  • 从 zip、jar 等归档文件中加载 Class 文件;
  • 从专有数据库中提取 Class 文件;
  • 将 Java 源文件动态编译为 Class 文件;
  • 由其他文件生成。

2、Java类的加载时机

2.1、类的加载过程

一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历:加载 (Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化 (Initialization)、使用(Using)和卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称 为连接(Linking)。这七个阶段的发生顺序如下图所示:

Ps:加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类型的加载过程必须按 照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始, 这是为了支持 Java 语言的运行时绑定特性(也称为动态绑定或晚期绑定)。

2.2、类的加载时机

关于在什么情况下需要开始类加载过程的第一个阶段 “加载”,《Java虚拟机规范》中并没有进行强制约束,这点可以交给虚拟机的具体实现来自由把握。但是对于初始化阶段,《Java虚拟机规范》则是严格规定了有且只有六种情况必须立即对类进行 “初始化”(而加载、验证、准备自然需要在此之前开始):

  1. 遇到 newgetstaticputstaticinvokestatic 这四条字节码指令时,如果类型没有进行过初始化,则需要先触发其初始化阶段。能够生成这四条指令的典型 Java 代码场景有:
    • 使用 New 关键字实例化对象的时候;
    • 取或设置一个 Class 的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外) 的时候;
    • 调用一个类型的静态方法的时候。
  2. 使用 java.lang.reflect 包的方法对 Class 进行反射调用的时候,如果类型没有进行过初始化,则需要先触发其初始化;
  3. 当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化;
  4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类;
  5. 当使用 Jdk7 新加入的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStaticREF_putStaticREF_invokeStaticREF_newInvokeSpecial 四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化;
  6. 当一个接口中定义了 Jdk8 新加入的默认方法(被 default 关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

这六种场景中的行为称为对一个类型进行主动引用。

2.3、被动引用不会初始化

除以上主动引用之外,所有引用类型的方式都不会触发初始化,称为被动引用。下面举三个例子来说明何为被动引用。

2.3.1、代码示例一
package com.lizhengi.classloading;

/**
 * 被动使用类字段演示一:通过子类引用父类的静态字段,不会导致子类初始化
 */
public class SuperClass {
    static {
        System.out.println("父类 init!");
    }
    public static int value = 123;
}

class SubClass extends SuperClass {
    static {
        System.out.println("子类 init!");
    }
}

/**
 * 非主动使用类字段演示
 */
class NotInitialization {
    public static void main(String[] args) {
        System.out.println(SubClass.value);
    }
}

上述代码运行之后,只会输出 “父类 init!”,而不会输出 “子类 init!”。

得出结论:访问静态属性的时候,不管是通过子类还是父类来访问这个静态属性,只有静态属性所呆的类会被初始化。至于是否要触发子类的加载和验证阶段,在《Java虚拟机规范》中并未明确规定。

2.3.2、代码示例二
package com.lizhengi.classloading;

/**
 * 被动使用类字段演示二:通过数组定义来引用类,不会触发此类的初始化
 */
public class SuperClass {
    static {
        System.out.println("父类 init!");
    }
    public static int value = 123;
}

/**
 * 非主动使用类字段演示
 */
class NotInitialization {
    public static void main(String[] args) {
        SuperClass[] sca = new SuperClass[10];
    }
}

上述代码运行之后,发现没有输出 “父类 init!”,说明并没有触发类 com.lizhengi.classloading.SuperClass 的初始化阶段。

但是这段代码里面触发了 另一个名为 com.lizhengi.classloading.SuperClass 的类的初始化阶段,对于用户代码来说,这并不是一个合法的类型名称,它是一个由虚拟机自动生成的、直接继承于 java.lang.Object 的子类,创建动作由字节码指令 newarray 触发。

这个类代表了一个元素类型为 com.lizhengi.classloading.SuperClass 的一维数组,数组中应有的属性和方法(用户可直接使用的只有被修饰为 publiclength 属性和 clone() 方法)都实现在这个类里。Java 语言中对数组的访问要比 C/C++ 相对安全,很大程度上就是因为这个类包装了数组元素的访问。

2.3.2、代码示例三
package com.lizhengi.classloading;

/**
 * 被动使用类字段演示三:常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
 */
public class ConstClass {
    static {
        System.out.println("ConstClass init!");
    }
    public static final String HELLOWORLD = "hello world";
}

/**
 * 非主动使用类字段演示
 */
class NotInitialization {
    public static void main(String[] args) {
        System.out.println(ConstClass.HELLOWORLD);
    }
}

上述代码运行之后,也没有输出 “ConstClass init!”,原因是常量在编译阶段存入了常量池,已经彻底和类脱离了关系,也就是常量和类的关系在编译成 Class 文件后就已不存在任何联系了。常量已经不再属于这个类。

2.4、接口的加载过程

Ps:接口的加载过程与类加载过程稍有不同,针对接口需要做一些特殊说明:

接口也有初始化过程, 这点与类是一致的,上面的代码都是用静态语句块 static{} 来输出初始化信息的,而接口中不能使用 static{} 语句块,但编译器仍然会为接口生成 <clinit>() 类构造器,用于初始化接口中所定义的成员变量。

接口与类真正有所区别的是前面讲述的六种 “有且仅有” 需要触发初始化场景中的第三种:当一个类在初始化时,要求其父类全部都已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化(被 default 关键字修饰的接口方法这种情况除外)。


3、Java类的加载过程

3.1、加载

在加载阶段,Java 虚拟机需要完成以下三件事情:

  1. 通过一个类的全限定名来获取定义此类的二进制字节流;
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
  3. 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。

“通过一个类的全限定名来获取定义此类的二进制字节流” 这条规则,它并没有指明二 进制字节流必须得从某个Class文件中获取,确切地说是根本没有指明要从哪里获取、如何获取。许多举足轻重的Java技术都建立在这 一基础之上,例如:

  • 从 ZIP 压缩包中读取,这很常见,最终成为日后 JAR、EAR、WAR 格式的基础;
  • 运行时计算生成,这种场景使用得最多的就是动态代理技术,在 java.lang.reflect.Proxy 中,就是用了 ProxyGenerator.generateProxyClass() 来为特定接口生成形式为 *$Proxy 的代理类的二进制字节流;
  • 由其他文件生成,典型场景是 JSP 应用,由 JSP 文件生成对应的 Class 文件;
  • 可以从加密文件中获取,这是典型的防 Class 文件被反编译的保护措施,通过加载时解密 Class 文件来保障程序运行逻辑不被窥探。

加载阶段既可以使用 Java 虚拟机里内置的引导类加 载器来完成,也可以由用户自定义的类加载器去完成,开发人员通过定义自己的类加载器去控制字节流的获取方式(重写一个类加载器的 findClass()loadClass() 方法),实现根据自己的想法来赋予应用程序获取运行代码的动态性。

对于数组类而言,情况就有所不同,数组类本身不通过类加载器创建,它是由 Java 虚拟机直接在内存中动态构造出来的。但数组类与类加载器仍然有很密切的关系,因为数组类的元素类型最终还是要靠类加载器来完成加载,一个数组类创建过程遵循以下规则:

  1. 如果数组的组件类型是引用类型,那就递归采用本节中定义的加载过程去加载这个组件类型,数组类将被标识在加载该组件类型的类加载器的类名称空间上。
  2. 如果数组的组件类型不是引用类型(例如 int[] 数组的组件类型为 int),Java 虚拟机将会把数组类标记为与引导类加载器关联。

加载阶段与连接阶段的部分动作(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始,但这些夹在加载阶段之中进行的动作,仍然属于连接阶段的一部分,这两个阶段的开始时间仍然保持着固定的先后顺序。

3.2、验证

验证是连接阶段的第一步,这一阶段的目的是确保 Class 文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运行后不会危害虚拟机自身的安全:

  • 编译器验证: Java 语言本身是相对安全的编程语言(起码对于 C/C++ 来说是相对安全的),将一个对象转型为它并未实现的类型、跳转到不存在的代码行之类的事情,如果尝试这样去做了,编译器会毫不留情地抛出异常、拒绝编译;
  • 字节码验证: 但前面也曾说过, Class 文件并不一定只能由 Java 源码编译而来,Java 代码无法做到的事情在字节码层面上都是可以实现的,至少语义上是可以表达出来的。Java 虚拟机如果不检查输入的字节流,对其完全信任的话,很可能会因为载入了有错误或有恶意企图的字节码流而导致整个系统受攻击甚至崩溃,所以验证字节码是 Java 虚拟机保护自身的一项必要措施;
  • 验证阶段的工作量在虚拟机的类加载过程中占了相当大的比重。验证阶段大致上会完成下面四个阶段的检验动作:文件格式验证、元数据验证、字节码验证和符号引用验证;
  • 文件格式验证:该验证阶段的主要目的是保证输入的字节流能正确地解析并存储于方法区之内,格式上符合描述一个 Java 类型信息的要求。这阶段的验证是基于二进制字节流进行的,只有通过了这个阶段的 验证之后,这段字节流才被允许进入 Java 虚拟机内存的方法区中进行存储,所以后面的三个验证阶段 全部是基于方法区的存储结构上进行的,不会再直接读取、操作字节流了。
  • 元数据验证:这个阶段可能包括的验证点如下(内容比较多,只列了以下几点):
    • 这个类是否有父类(除了 java.lang.Object 之外,所有的类都应当有父类)
    • 这个类的父类是否继承了不允许被继承的类(被 final 修饰的类)
    • 如果这个类不是抽象类,是否实现了其父类或接口之中要求实现的所有方法
  • 字节码验证:第三阶段是整个验证过程中最复杂的一个阶段,主要目的是通过数据流分析和控制流分析,确定 程序语义是合法的、符合逻辑的;
  • 符号引用验证:主要作用是验证该类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源。类、字段、方法的可访问性(privateprotectedpublic)是否可被当前类访问。

如果程序运行的全部代码(包括自己编写的、第三方包中的、从外部加载的、动态生成的等所有代码)都已经被反复使用和验证过,在生产环境的实施阶段就可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

3.3、准备

准备阶段是正式为类中定义的变量(即静态变量,被 static 修饰的变量)分配内存并设置类变量初始值的阶段,从概念上讲,这些变量所使用的内存都应当在方法区中进行分配,但必须注意到方法区本身是一个逻辑上的区域,在 Jdk7 及之前,HotSpot 使用永久代来实现方法区时,实现是完全符合这种逻辑概念的;而在 Jdk8 及之后,类变量则会随着 Class 对象一起存放在 Java 堆中。

关于准备阶段,还有两个容易产生混淆的概念笔者需要着重强调,首先是这时候进行内存分配的仅包括类变量,而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在 Java 堆中。其次是这里所说的初始值 “通常情况” 下是数据类型的零值,假设一个类变量的定义为:

public static int value = 123;

那变量 value 在准备阶段过后的初始值为 0 而不是 123,因为这时尚未开始执行任何 Java 方法,而把 value 赋值为 123putstatic 指令是程序被编译后,存放于类构造器方法之中,所以把 value 赋值为 123 的动作要到类的初始化阶段才会被执行。

下面列出了 Java 中所有基本数据类型的零值。

数据类型零值数据类型零值
int0booleanfalse
long0Lfloat0.0f
short0double0.0d
char\u0000referncenull
byte0
3.4、解析

解析阶段是 Java 虚拟机将常量池内的符号引用替换为直接引用的过程

  • 符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可;
  • 直接引用(Direct References):直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。

符号引用与虚拟机实现的内存布局无关,直接引用是和虚拟机实现的内存布局直接相关的,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。

如果有了直接引用,那引用的目标必定已经在虚拟机的内存中存在。

解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这 7 类符号引用进行。

/**
 * 符号引用
 */
 String str = "abc";
 System.out.print("str=" + str);
 
 /**
 * 直接引用
 */
 System.out.print("str=" + "abc");

符号引用要转换成直接引用才有效,这也说明直接引用的效率要比符号引用高。那为什么要用符号引用呢?这是因为类加载之前,javac 会将源代码编译成 .class 文件,这个时候 javac 是不知道被编译的类中所引用的类、方法或者变量他们的引用地址在哪里,所以只能用符号引用来表示。

3.5、初始化

类的初始化阶段是类加载过程的最后一个步骤,除了在加载阶段用户应用程序可以通过自定义类加载器的方式局部参与外,其余动作都完全由 Java 虚拟机来主导控制。直到初始化阶段,Java 虚拟机才真正开始执行类中编写的 Java 程序代码,将主导权移交给应用程序。

进行准备阶段时,变量已经赋过一次系统要求的初始零值,而在初始化阶段,则会根据程序员通过程序编码制定的主观计划去初始化类变量和其他资源。

初始化阶段就是执行类构造器 <clinit>() 方法的过程。<clinit>() 并不是程序员在 Java 代码中直接编写 的方法,它是 Javac 编译器的自动生成物。

<clinit>() 方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{} 块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序决定的,静态语句块中只能访问 到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问

<clinit>() 方法与类的构造函数(即在虚拟机视角中的实例构造器 <init>() 方法)不同,它不需要显式地调用父类构造器,Java 虚拟机会保证在子类的 <clinit>() 方法执行前,父类的 <clinit>() 方法已经执行 完毕。因此在 Java 虚拟机中第一个被执行的 <clinit>() 方法的类型肯定是 java.lang.Object

由于父类的 <clinit>() 方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值 操作,如下代码示例,输出2。

public class Test {
    static class Parent {
        public static int A = 1;

        static {
            A = 2;
        }
    }

    static class Sub extends Parent {
        public static int B = A;
    }

    public static void main(String[] args) {
        System.out.println(Sub.B);
    }
}

接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成 <clinit>() 方法。但接口与类不同的是,执行接口的 <clinit>() 方法不需要先执行父接口的 <clinit>() 方法, 因为只有当父接口中定义的变量被使用时,父接口才会被初始化。此外,接口的实现类在初始化时也 一样不会执行接口的 <clinit>() 方法。

Java 虚拟机必须保证一个类的 <clinit>() 方法在多线程环境中被正确地加锁同步,如果多个线程同 时去初始化一个类,那么只会有其中一个线程去执行这个类的 <clinit>() 方法,其他线程都需要阻塞等 待,直到活动线程执行完毕 <clinit>() 方法。如果在一个类的 <clinit>() 方法中有耗时很长的操作,那就 可能造成多个进程阻塞,在实际应用中这种阻塞往往是很隐蔽的。代码如下演示了这种场景。

public class Test {
    static class DeadLoopClass {
        static {
            // 如果不加上这个if语句,编译器将提示“Initializer does not complete normally” 并拒绝编译
            if (true) {
                System.out.println(Thread.currentThread() + "init DeadLoopClass");
                while (true) {
                }
            }
        }
    }

    public static void main(String[] args) {
        Runnable script = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread() + "start");
                DeadLoopClass dlc = new DeadLoopClass();
                System.out.println(Thread.currentThread() + " run over");
            }
        };
        Thread thread1 = new Thread(script);
        Thread thread2 = new Thread(script);
        thread1.start();
        thread2.start();
    }
}

运行结果:

img

需要注意,其他线程虽然会被阻塞,但如果执行 <clinit>() 方法的那条线程退出

<clinit>() 方法后,其他线程唤醒后则不会再次进入 <clinit>()方法。同一个类加载器下,一个类型只会被初始化一 次。

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

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

相关文章

【BUU】[Dest0g3 520迎新赛]Really Easy SQL

2024/8/14 [Dest0g3 520迎新赛]Really Easy SQL 题目标题说明是SQL注入 题目首页 页面title显示是钓鱼站点。 钓鱼站点主要为将我们的输入信息保存在数据库。后台应该是插入语句。 这里无论输入什么都显示密码错误, 只能尝试盲注&#xff0c;基于时间的盲注, 这里经过测试p…

OPC DAY-上海场提前预告:Softing带您探索“智能工厂中的OPC应用”

&#xff08;图片来源于&#xff1a;OPC基金会官网&#xff09; 时间&#xff1a;2024年9月25日 14:00-14:30 | OPC DAY-上海站 地点&#xff1a;上海国家会展中心-5.1H M5-03会议室 2024年9月24-28日&#xff0c;第二十四届中国国际工业博览会将于国家会展中心&#xff08;上…

Linux 中的同步机制

代码基于&#xff1a;Kernel 6.6 临界资源&#xff1a;指哪些在同一时刻只允许被一个线程访问的软件或硬件资源。这种资源的特点是&#xff0c;如果有线程正在使用&#xff0c;其他进程必须等待直到该线程释放资源。 临界区&#xff1a;指在每个线程中访问临界资源的那段代码。…

vue3结合海康WEB开发包,开发web在线预览视频

我们这里选择V3.3版本 文档地址&#xff1a;https://open.hikvision.com/download/5cda567cf47ae80dd41a54b3?type20&id4c945d18fa5f49638ce517ec32e24e24 解压过后&#xff0c;会有三个文件夹 在docs中&#xff0c;点开Demo使用说明&#xff0c;按照流程先测试下&…

赋能基层,融合创新:EasyCVR视频汇聚平台构建平安城市视频共享系统

一、雪亮工程建设的意义 雪亮工程的核心在于通过高清视频监控、环境监测和智能预警等先进技术手段&#xff0c;构建一个高效、智能、安全、便捷的社会安全防控体系。这一工程的建设不仅代表了现代化科技手段在城市治安管理中的应用&#xff0c;更是提升社会安全保障能力、推动…

树形结构查找(B树、B+树)

平衡树结构的树高为 O(logn) &#xff0c;平衡树结构包括两种平衡二叉树结构&#xff08;分别为 AVL 树和 RBT&#xff09;以及一种树结构&#xff08;B-Tree&#xff0c;又称 B 树&#xff0c;它的度大于 2 &#xff09;。AVL 树和 RBT 适合内部存储的应用&#xff0c;而 B 树…

CompreFace Study

系列文章目录 第一章 CompreFace Installation 第二章 Face verification POC 文章目录 系列文章目录前言一、What is the ComreFace&#xff1f;二、How to install the CompreFace? 1.On Linux for CompreFace 1.2.02.Troubleshooting总结 前言 此文旨在记录学习CompreF…

萤石取流播放失败自助排障及常见错误码解决方案

一、在使用播放地址播放时遇到播放失败的情况&#xff0c;可使用排障工具排查具体原因&#xff0c;以下具体介绍排障工具的使用方法 1、在浏览器里打开排障工具&#xff0c;地址&#xff1a;萤石开放平台-提供持续稳定的以音视频为主的全场景、多功能综合性服务 2、在第一行输入…

安全无忧!Windows7全补丁旗舰版:集成所有补丁!

今日&#xff0c;系统之家小编给大家分享集成所有补丁的Windows7旗舰版系统&#xff0c;集成至2023.12所有官方补丁&#xff0c;修复了系统高危漏洞&#xff0c;让大家时刻都能舒心地展开操作。系统基于微软 Windows 7 2009 SP1 旗舰版进行离线制作&#xff0c;全新升级的优化方…

本地环境VMware使用代理解决 Docker 镜像拉取问题

引言 本文将分享我在 Windows 10 环境下&#xff0c;通过 VMware 运行的 CentOS 7.8 虚拟机中配置 Docker 代理&#xff0c;成功解决了镜像拉取问题的经验。 问题描述 在尝试启动一个依赖 Docker 的 GitHub 项目时&#xff0c;拉取 Docker 镜像的失败。尝试配置了几个国内源…

(附源码)基于springboot的智慧社区管理系统-计算机毕设 06797

基于springboot的智慧社区管理系统 摘 要 SpringBoot智慧社区管理系统是一款基于SpringBoot框架开发的智能化社区管理软件&#xff0c;致力于提升社区管理效率和服务质量。该系统涵盖了社区入住管理、物业费管理、公共设施预约等功能&#xff0c;支持在线报修、信息发布、社区活…

Java语言程序设计——篇十三(2)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

【运维】报错Resource averaged_perceptron_tagger_eng not found.

文章目录 报错信息解决报错信息 able of handling various complex tasks. Please report the progress of this project to the team members.> ========================<

回溯算法探索篇Ⅲ

Leetcode93——复原IP地址 题目描述&#xff1a; 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" 是…

PyTorch — 初学者教程

一、说明 在本文中,我将编译 PyTorch 的初学者教程。本教程大量使用了官方 PyTorch 教程中的材料。

Java SE--IO流

一.File类型 如果我们想在程序中操作或者描述一个文件夹或文件&#xff0c;可以使用File类型 File类型在java.io包下 File可以新建&#xff0c;删除&#xff0c;移动&#xff0c;修改&#xff0c;重命名文件夹&#xff0c;也可以对文件或者文件夹的属性进行访问&#xff1b;…

嵌入式软件--模电基础 DAY 2

强电和弱电&#xff0c;简单一点是以电死人为标准的&#xff0c;交流电36伏特以下&#xff0c;直流电24V以下&#xff0c;为安全电压&#xff0c;是为弱电&#xff0c;反则强电。 市电进入家庭&#xff0c;连接你的电脑&#xff0c;220V的电压为什么没有让你感到危险&#xff…

【TiDB】09-修改tidb客户端访问密码

目录 1、修改配置文件 2、停止tidb-server 3、以root方式启动脚本 4、修改密码 5、停止脚本重启服务 1、修改配置文件 进入tidb-server默认部署位置 #切换tidb账号 su tidb# 进入tidb-server部署路径 cd /tidb-deploy/tidb-4000# 修改配置 vim ./conf/tidb.toml添加内容…

Nginx之我不会的

安装 windows下安装 这里我们选择windows下的稳定版本 之后就是解压&#xff0c;安装到英文目录下 启动&#xff08;关闭&#xff09;命令 windows命令 启动 nginx.exe 或者 start nginx 关闭 nginx -s quit&#xff08;会有一定程序退出&#xff09; 或 nginx -s stop &…

企业低代码平台那个好用?企业低代码推荐

近些年&#xff0c;很多大型的互联网公司开始了自研低代码开发平台的尝试&#xff0c;包括阿里、腾讯、华为等等国内头部大厂。由此我们可以了解到即使是开发能力富余的互联网大厂&#xff0c;也同样需要低代码的快速响应需求能力来应对数字化时代多变的功能需求。然而&#xf…