Java虚拟机揭秘-底层驱动力,性能保障!


Java虚拟机作为Java技术体系的核心组成部分,其重要性不言而喻。它不仅为Java提供了跨平台的能力,更是Java程序运行的基石。本文将为您深入解析Java虚拟机的工作原理、作用和应用场景,并通过生动的实例让您彻底理解这一关键技术。


一、Java虚拟机详细介绍


1、什么是Java虚拟机?

Java虚拟机(Java Virtual Machine,JVM)是一个虚拟的计算机,它有自己完善的硬件架构,能够运行Java字节码。JVM有着与硬件无关的指令集,屏蔽了与具体操作系统相关的信息,使得Java语言编译后的字节码可以在多种平台上运行。这正是Java"一次编写,处处运行"的根本所在。

JVM 并非只能运行Java语言,而是面向多语言的运行平台,只要遵守JVM的约定(只要编译成.class文件),就可以运行在JVM虚拟机上。

现在除了Java之外,比如scala、kotlin、groovy等语言都运行在JVM上。


在这里插入图片描述


2、Java虚拟机的作用

(1)、内存管理

自动内存管理是JVM最基本的作用之一。JVM通过自动分配和管理内存,防止了手动编码过程中可能出现的内存泄漏等问题。

(2)、跨平台能力

Java字节码可以在任何安装了JVM的设备上运行,这使得Java拥有了出色的跨平台能力,避免了操作系统、硬件的制约。

(3)、自动优化

现代JVM能够对热点代码进行运行时的动态优化,包括生成本地机器代码、内联编译等,大幅提升程序性能。

(4)、安全性

JVM对字节码进行了语法检查、类型检查和安全检查,并提供了沙箱防护机制,确保了Java程序的健壮与安全。


3、JVM、JDK、JRE 的区别与联系

JVM、JDK、JRE是Java开发和运行环境中的三个核心组件,它们之间有着紧密的关系:


(1)、JVM(Java Virtual Machine,Java虚拟机)

JVM是Java平台的核心组成部分,它是一个可以执行Java字节码的虚拟计算机。JVM提供了一个运行时环境,用于加载Java程序的类文件,执行其中的字节码,并管理程序的执行,如内存管理、垃圾回收等。JVM确保了Java的跨平台性,即所谓的“一次编写,到处运行”。


(2)、JRE(Java Runtime Environment,Java运行时环境)

JRE是运行Java应用程序所必需的环境。它包含了JVM以及Java核心类库和其它运行Java程序所需的资源。简单来说,JRE是JVM的宿主环境,它提供了JVM运行所需的所有资源。JRE不包含开发工具,因此它只适用于运行Java程序。


(3)、 JDK(Java Development Kit,Java开发工具包)

JDK是为Java开发者提供的一套工具包,它包含了JRE的所有内容,并且还包括了用于开发Java应用程序的工具和库。JDK中包含了javac编译器、调试器、jar工具、Java文档生成器(javadoc)等开发工具。此外,JDK还包含了一些用于Java性能测量和监控的工具,以及Java平台的源代码。


(4)、JDK、JRE和JVM的关系

  • JVM是JRE的一部分:JRE提供了JVM,使得Java程序可以在不同的平台上运行。

  • JDK包含了JRE:JDK中包含了一个完整的JRE,因此安装了JDK就不需要单独安装JRE。

  • JDK为开发者提供工具:JDK提供了编译器和调试器等开发工具,而JRE和JVM则提供了运行Java程序所需的环境。


(5)、应用场景

  • JRE适用于只运行Java程序的用户:如果只需要运行Java程序,不需要开发Java程序,那么安装JRE就足够了。
  • JDK适用于Java开发者:如果你是一名Java开发者,需要编写、编译和调试Java程序,那么需要安装JDK。

总之:

JVM作为核心,提供了字节码的执行环境;JRE作为JVM的宿主,提供了运行时所需的资源;JDK则为开发者提供了完整的开发和运行环境。


二、Java虚拟机的工作原理

Java虚拟机的工作过程可以概括为以下几个阶段:

在这里插入图片描述


1、编译阶段

首先 java经过javac编译成.class文件。

类的编译阶段主要是把源码文件编译成JVM可以解析的class文件,这个阶段会经过词法分析、语法语义分析、生成字节码,这个几个阶段后生成最后的class,用16进制的方式打开class文件后如图:

在这里插入图片描述


class 文件是Java编译器将Java源代码(.java 文件)编译后生成的字节码文件。这个文件是JVM执行引擎执行的基础。.class 文件包含了多种类型的数据,用于确保JVM能够正确地加载、链接和执行代码。以下是.class 文件中包含的主要内容:

  • 魔数(Magic Number)
    每个.class 文件的开始都是魔数,用于标识这个文件是否是一个有效的类文件。魔数是四个字节,值为 0xCAFEBABE

  • 版本信息
    包括主版本号和次版本号,用于确定这个.class 文件是为哪个版本的Java虚拟机编译的。

  • 常量池(Constant Pool)
    常量池用于存储编译期间生成的各种字面量和符号引用。这部分可能包括类和接口的名称、字段名、方法名、字符串常量等。

  • 访问标志(Access Flags)
    定义了这个类或接口的访问权限,如是否是公共的(public)、抽象的(abstract)等。

  • 类索引、父类索引
    类索引用于确定这个类的全名,父类索引指向常量池中的一个引用,表示这个类的直接父类。

  • 接口索引表(Interfaces)
    如果这个类实现了一个或多个接口,接口索引表将列出这些接口。

  • 字段表集合(Fields)
    字段表包含了类的字段信息,每个字段都有其名称、描述符、访问修饰符等。

  • 方法表集合(Methods)
    方法表包含了类的方法信息,包括方法的名称、返回类型、参数列表、局部变量表、字节码指令等。

  • 属性表集合(Attributes)
    属性表可以包含多种不同的属性,如代码(Code)属性包含了实际的字节码指令、行号表、异常处理表等;SourceFile属性表示源代码文件的名称;还有其他如Deprecated、Synthetic等属性。

  • 字节码(Bytecode)
    方法的字节码是JVM执行的指令集,定义了方法的行为。

  • 栈映射表(Stack Map Table)
    在较新的JVM版本中,栈映射表用于支持字节码的验证和JIT编译器的优化。

  • 异常表(Exception Table)
    如果方法中有try-catch块,异常表将列出可能抛出的异常类型和异常处理器。

这些组成部分共同定义了Java类的结构和行为,使得JVM能够加载和执行Java程序。开发者通常不会直接编辑.class 文件,而是通过Java源代码进行编程,并利用编译器生成这些字节码文件。


2、类加载阶段

Java 类加载过程是 Java 运行时环境中的一个关键特性,它涉及到将 Java 类(通常是 .class 文件)加载到 JVM(Java 虚拟机)中,并为类的实例化和执行做准备。类加载过程大致可以分为以下几个阶段:


(1)、加载(Loading)

加载是类加载过程的第一个阶段,在这个阶段,JVM 完成以下任务:

  • 通过类的全名查找 .class 文件。

  • 加载 .class 文件到内存中,通常使用二进制形式。

  • 通过解析常量池中的符号引用(如类名、方法名、字段名等),将它们转换为直接引用。


(2)、验证(Verification)

验证阶段确保加载的类信息是合法的、符合JVM规范的,不会危害虚拟机安全。验证包括以下几个方面:

  • 文件格式验证:确保 .class 文件符合Java类文件的规范。

  • 元数据验证:检查类的结构信息,如访问修饰符、类和接口的继承关系等。

  • 字节码验证:确保字节码的指令不会做出危害虚拟机安全的行为。

  • 符号引用验证:确保符号引用能正确解析。


(3)、准备(Preparation)

准备阶段是为类的静态变量分配内存,并设置初始值。这里的初始值指的是数据类型的零值,如:

  • 整数类型 int 的初始值为 0
  • 浮点类型 float 的初始值为 0.0
  • 引用类型(如类、接口、数组)的初始值为 null
  • boolean 类型的初始值为 false
  • char 类型的初始值为 \u0000(Unicode编码中的空字符)。

(4)、解析(Resolution)

解析阶段是将常量池中的符号引用转换为直接引用的过程。直接引用是指向内存中的直接地址。解析包括:

  • 类或接口的解析。

  • 字段的解析。

  • 方法的解析。

  • 接口方法的解析。

  • 字段和方法的访问权限的解析。


(5)、初始化(Initialization)

初始化是类加载的最后一个阶段,它包括:

  • 执行静态初始化块(使用 static 关键字声明的代码块)。
  • 为静态变量赋予正确的初始值。
  • 如果类具有超类(除了 Object 类之外),则先初始化其超类。

执行顺序和触发条件:

  • 类的加载过程是按照上面列出的顺序进行的,但并不是所有的类都会经过所有阶段。例如,如果一个类没有进行任何静态变量的赋值操作,那么它可能不会进入初始化阶段。
  • 类的加载是由某个特定的动作触发的,如 new 关键字实例化对象、访问静态字段、调用静态方法等。

注意事项:

  • 类加载器不仅仅加载 .class 文件,它还负责加载包和其他资源文件。
  • 类加载过程是线程安全的。
  • 类加载器的父子委派模型确保了Java核心库的安全性,防止核心库被随意替换。

通过以上各个阶段,JVM 确保了类的安全性、一致性和可用性,为类的执行提供了必要的准备和初始化工作。


3、解释和执行阶段

加载完成后,执行引擎负责解释和执行字节码指令。执行引擎主要由解释器和即时编译器(JIT)两部分组成。解释器逐条解释字节码,JIT则能够动态编译热点代码为优化后的本地机器码以提高执行效率。

执行引擎通常包括以下部分:

  • 字节码解释器:负责逐条解释执行字节码。

  • 即时编译器:负责将热点字节码编译为机器码。

  • 代码优化器:负责对编译后的机器码进行进一步的优化。

  • 代码缓存:存储编译后的机器码,供后续执行使用。


(1)、解释执行(Interpretation)

执行引擎的一种基本工作模式是解释执行。在这种模式下,执行引擎逐条读取字节码,并根据字节码执行相应的操作。这种方式类似于我们在学习编程时使用的解释器。

过程:

  • 第一步,字节码扫描:执行引擎从PC寄存器(程序计数器)指向的字节码指令开始执行。

  • 第二步,解释执行:对每条字节码进行解释,并执行其对应的操作。

  • 第三步,状态更新:更新操作数栈、局部变量表和程序计数器等状态。

  • 第四步,循环执行:继续扫描下一条字节码,直到方法执行完毕或遇到返回指令。


(2)、即时编译(Just-In-Time Compilation, JIT)

为了提高程序的执行效率,现代JVM通常采用即时编译技术。即时编译器将热点代码(频繁执行的代码)编译为本地机器码,以提高执行速度。

过程:

  • 第一步,代码选择:JIT编译器选择需要编译的热点代码,这些代码可能是被多次调用的方法或循环体。

  • 第二步,编译优化:JIT编译器将字节码编译为机器码,并进行优化,如分支预测、循环展开等。

  • 第三步,本地代码缓存:编译后的机器码被存储在执行引擎的代码缓存中,以便重复使用。

  • 第四步,执行本地代码:执行引擎直接执行编译后的机器码,而不是逐条解释字节码。


(3)、热点代码探测(HotSpot Code Detection)

为了确定哪些代码需要即时编译,JVM需要能够探测热点代码。这通常通过以下方式实现:

  • 计数器:为每个方法入口和循环回边设置计数器,统计执行次数。
  • 阈值设定:当计数器达到一定阈值时,认为该代码是热点代码,适合进行即时编译。

(4)、多版本优化(Multi-Versioning)

为了进一步优化性能,JVM可以为同一段代码生成多个版本,以适应不同的执行环境或条件。例如,基于处理器的CPU架构特点,生成优化的机器码。

JVM的执行引擎是实现Java程序高效运行的关键。通过解释执行和即时编译,执行引擎能够在保持跨平台特性的同时,提供接近本地编译语言的性能。随着JVM技术的发展,执行引擎的优化策略也在不断进步,以满足日益增长的性能需求。


4、内存管理阶段

Java虚拟机负责自动分配和管理堆、栈、方法区等运行时数据区域。其中垃圾收集器(GC)通过跟踪可达性分析,自动回收无用对象的内存空间。

JVM内存管理是确保Java程序高效运行的关键环节。JVM内存管理主要包括以下几个阶段:

(1)、类加载过程中的内存分配

当JVM开始加载一个类时,它会为这个类分配内存以存储类的元数据。这包括类的信息、字段、方法等。这个阶段的内存分配是在方法区(Method Area)进行的。


(2)、对象创建时的内存分配

当使用new关键字创建一个对象时,JVM会在堆(Heap)中为这个新对象分配内存。堆是JVM中用于存储对象实例和数组的部分。

过程:

  • 确定对象大小:JVM首先确定对象所需的内存大小。

  • 内存分配:在堆中找到足够大的连续空间分配给对象。

  • 对象头信息设置:设置对象头(Mark Word),包含对象的哈希码、GC信息、锁状态等。

  • 初始化:执行<init>方法,对对象进行初始化。


(3)、 运行时的内存管理

在程序运行期间,JVM需要不断管理内存的使用,包括局部变量的创建和销毁,以及对对象的访问。

过程:

  • 局部变量表:方法执行时,其局部变量会在栈(Stack)的局部变量表中分配空间。

  • 操作数栈:方法执行过程中,临时数据会在操作数栈中存取。

  • 引用访问:通过引用访问堆中的对象,进行读写操作。


(4)、垃圾收集(Garbage Collection, GC)

JVM周期性地执行垃圾收集,回收不再被引用的对象所占用的内存。

过程:

  • 可达性分析:从一系列的GC Roots开始,标记所有可达的对象。
  • 清理:清除未被标记的对象,回收内存。
  • 内存整理:可选地整理内存,减少碎片。

(5)、 内存分配和回收策略

JVM使用不同的算法和策略来优化内存的分配和回收。例如:

  • 复制算法:用于新生代,将内存分为一个eden区和两个survivor区,对象在这些区域之间复制。
  • 标记-清除算法:用于老年代,首先标记所有存活的对象,然后清除未被标记的对象。
  • 标记-清除-整理:在标记-清除的基础上,对内存进行整理,消除碎片。

(6)、内存分配的并发处理

为了避免在内存分配和回收过程中的Stop-The-World现象,JVM采用了并发处理机制:

  • 并发标记:在应用程序运行的同时进行对象的标记。
  • 并发清理:在应用程序运行的同时进行内存的清理。
  • 增量收集:将垃圾收集过程分步骤执行,减少GC暂停时间。

(7)、 内存溢出处理

当JVM的内存不足时,会抛出内存溢出异常。例如,堆内存不足时会抛出OutOfMemoryError

JVM内存管理是一个复杂的过程,涉及到内存的分配、垃圾收集、内存整理和并发处理等多个方面。通过有效的内存管理策略,JVM确保了Java程序的高效运行,同时提供了自动内存管理和垃圾回收功能,简化了内存管理的复杂性。了解JVM内存管理的工作原理对于Java开发者来说是非常重要的,它有助于我们编写更高效的程序,并在遇到性能问题时能够快速定位和解决。


// GC示例
public static void main(String[] args) {
   byte[] array = new byte[1024 * 1024 * 50]; //50MB数组
   array = null; // 数组不可访问
   System.gc(); // 触发GC
}

5、执行子系统阶段

JVM执行子系统不仅仅是执行字节码的组件,它还提供了与操作系统之间通信的能力,支持线程同步、文件I/O等操作。以下是执行子系统在这些方面的作用和实现方式:


(1)、线程管理

JVM执行子系统负责管理Java程序中的线程。每个线程在操作系统中都对应一个内核线程,JVM需要管理这些线程的创建、同步、调度和终止。

  • 线程创建

    • Java程序通过Thread类或Runnable接口启动新线程。JVM负责将这些线程映射到操作系统的线程。
  • 线程同步

    • JVM提供了同步机制,包括synchronized关键字和java.util.concurrent包中的并发工具,以确保线程安全。
  • 线程调度

    • JVM执行子系统与操作系统的调度器协作,管理线程的执行顺序和时间分配。


(2)、系统调用

JVM提供了对本地系统调用的接口,使得Java程序能够执行操作系统级别的操作。

JVM通过本地库接口(如Java Native Interface, JNI)允许Java代码调用本地方法,实现与操作系统的交互。


(3)、文件I/O 及操作

JVM执行子系统支持文件输入输出操作,使得Java程序能够读写文件。

I/O操作:

Java程序使用java.iojava.nio包中的类进行文件I/O操作。JVM负责将这些操作映射到对应的操作系统I/O调用。


(4)、网络通信

JVM支持网络通信,使得Java程序能够进行网络连接和数据传输。

网络I/O:

Java程序使用java.net包中的类进行网络编程。JVM负责网络连接的建立、数据的发送和接收。


(5)、 安全性

JVM执行子系统还负责执行安全策略,确保Java程序不会执行有害的系统操作。


(6)、安全管理器

JVM提供了安全管理器(java.lang.SecurityManager),可以监控和限制程序对系统资源的访问。


(7)、 性能监控与优化

JVM执行子系统还包括性能监控和优化工具,帮助开发者了解程序的运行情况,并进行性能调优。


JVM执行子系统是Java程序与操作系统之间的桥梁。它不仅负责执行字节码,还提供了线程管理、内存管理、系统调用、文件I/O、网络通信等能力,确保Java程序能够高效、安全地运行。通过JVM执行子系统,Java程序能够充分利用操作系统提供的资源和功能。


以上就是Java虚拟机的基本工作流程,体现了JVM为Java提供了一个安全、高效的运行环境。


三、Java虚拟机的应用场景


Java虚拟机为我们提供了无数的便利,可以在以下场景大显身手:
  • 跨平台应用开发

    JVM的存在使得Java可以运行于任何安装了虚拟机的平台,从而实现了跨平台的能力,如Android应用、嵌入式设备、云服务器等。

  • 大数据分析

    Hadoop、Spark等大数据处理框架都是建立在JVM之上,利用其高效的内存管理和多线程支持,极大提高了分布式计算能力。

  • 中间件服务

    Java的各种应用服务器中间件如WebLogic、WebSphere、Tomcat等,都是基于JVM构建的,发挥了JVM提供的跨平台、高效、安全等特性。


总之,Java虚拟机作为Java技术体系的根基,贯穿于Java应用的方方面面。了解JVM的工作原理和特性,将使我们能够编写出更加高效、可靠的程序。期待在下期与您分享更多关于JVM调优和故障排查的干货知识,敬请期待!


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

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

相关文章

温故而知新-MySQL篇【面试复习】

温故而知新-数据库篇【面试复习】 前言版权推荐温故而知新-Mysql篇Mysql常见面试题Mysql事务Mysql索引Mysql锁Mysql日志Mysql中的Buffer 数据库的三范式是什么MySQL对于LRU的优化InnoDB三大特性自适应哈希索引&#xff08;Adaptive Hash Index&#xff09;插入缓存&#xff08;…

驱动与系统学习网址

DRM&#xff08;Direct Rendering Manager&#xff09;学习简介-CSDN博客 Android Qcom Display学习(零)-CSDN博客 https://blog.csdn.net/hexiaolong2009/category_9705063.htmlhttps://blog.csdn.net/hexiaolong2009/category_9705063.htmlRender Hell —— 史上最通俗易懂…

【ECharts】数据可视化

目录 ECharts介绍ECharts 特点Vue2使用EChats步骤安装 ECharts引入 ECharts创建图表容器初始化图表更新图表 示例基本柱状图后台代码vue2代码配置 组件代码运行效果 基本折线图示例代码组件 基础饼图示例代码后台前端配置组件运行效果 其他 ECharts介绍 ECharts 是一个由百度开…

DataGear 制作服务端分页的数据可视化看板

DataGear 2.3.0 版本新增了附件图表数据集特性&#xff08;在新建图表时将关联的数据集设置为 附件 &#xff0c;具体参考官网文档定义图表章节&#xff09;&#xff0c;在制作看板时&#xff0c;可以基于此特性&#xff0c;结合dg-chart-listener&#xff0c;利用服务端数据扩…

使用VCPKG编译并使用Qt5

一、背景 Qt就不介绍了。VCPKG可以看这里VCPKG资料记录_vcpkg boost 多久-CSDN博客 为什么搞Qt5而不是Qt6&#xff1f;因为Qt5比较稳定吧。而且我公司也是用的Qt5。 为什么要自己编译而不是去下载Qt5&#xff1f; 第一&#xff0c;因为Qt5在Qt在线安装版本只提供到Qt5.15.2&…

C语言内存函数超详细讲解

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 C语言内存函数超详细讲解 收录于专栏【C语言学习】 本专栏旨在分享学习C语言学习的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. m…

linux文件权限常用知识点,基于Linux(openEuler、CentOS8)

目录 知识点常用实例 知识点 真实环境文件显示 解读 常用实例 文件所有者 chown -R nginx:nginx /home/source目录权限(R选填必须大写<遍历子文件夹及文件>) chmod -R 755 /home/sourcechmod -R 777 /home/source

【小程序八股文】系列之篇章二 | 小程序的核心机制

【小程序八股文】系列之篇章二 | 小程序的核心机制 前言三、微信小程序原理与运行机制简述一下微信小程序的原理微信小程序的双线程的理解为什么不采用浏览器多线程模式&#xff1f;为什么是双线程&#xff1f;&#xff08;出发点&#xff1a;安全&#xff0c;快速&#xff0c;…

2.Redis之Redis的背景知识

Redis 是一个在内存中存储数据的中间件 用于作为数据库,用于作为数据缓存. 在分布式系统中能够大展拳脚~ 1.Redis的特性介绍(优点) 1.1 在内存中存储数据 MySQL 主要是通过"表"的方式来存储组织数据的,"关系型数据库" Redis 主要是通过“键值对" 的…

safetensors介绍

1 介绍 safetensors 是 Huggingface 推出的一种可靠、易移植的机器学习模型存储格式&#xff0c;用于安全地存储 Tensor&#xff0c;而且速度很快&#xff08;零拷贝&#xff09;。 safetensors 格式结构&#xff1a; 8 bytes&#xff1a;一个无符号的整数&#xff0c;表示…

1106 2019数列

solution 维护长度为4的数组&#xff0c;对于第四位之后的数字&#xff0c;为所维护数组的所有元素之和 的个位数 #include<iostream> using namespace std; int main(){int n, a[4] {2, 0, 1, 9}, cnt 0, d;scanf("%d", &n);for(int i 0; i < n; …

WSL2添加桌面显示

编译 autoscan && aclocal && autoconf && automake --add-missing 自动 #!/bin/shrun () {echo "running: $*"eval $*if test $? ! 0 ; thenecho "error: while running $*"exit 1fi }run aclocal run autoheader run automak…

k8s命令式对象管理和配置

kubectl补全: # dnf install -y bash-completion # echo "source <(kubectl completion bash)" >> ~/.bashrc # kubectl completion bash > /etc/bash_completion.d/kubectl 命令式对象管理 kubectl命令 # 查看所有pod kubectl get pod # 查看某个po…

01-02.Vue的常用指令(二)

01-02.Vue的常用指令&#xff08;二&#xff09; 前言v-model&#xff1a;双向数据绑定v-model举例&#xff1a;实现简易计算器Vue中通过属性绑定为元素设置class 类样式引入方式一&#xff1a;数组写法二&#xff1a;在数组中使用三元表达式写法三&#xff1a;在数组中使用 对…

Python3 笔记:IDLE的几个基本设置

1、设置字体&#xff1a; Options > Configure IDLE > Fonts 2、设置文字颜色&#xff08;设置高亮&#xff09;&#xff1a; Options > Configure IDLE > Highlights 3、设置背景颜色&#xff1a; Options > Configure IDLE > Highlights 4、设置窗口&a…

存储+调优:存储-IP-SAN-EXTENSION

存储调优&#xff1a;存储-IP-SAN-EXTENSION 文件系统的锁标记 GFS&#xff08;锁表空间&#xff09; ----------- ------------ ------------- 节点 | ndoe1 | | node2 | | node3 | ---------- ------…

树莓派学习笔记——树莓派的三种GPIO编码方式

1、板载编码&#xff08;Board pin numbering&#xff09;: 板载编码是树莓派上的一种GPIO引脚编号方式&#xff0c;它指的是按照引脚在树莓派主板上的物理位置来编号。这种方式对于初学者来说可能比较直观&#xff0c;因为它允许你直接根据引脚在板上的位置来编程。 2、BCM编…

钉钉算是在线办公系统的设计标杆,尽管它依然很难用

不吹不黑&#xff0c;钉钉界面谁的的确简洁&#xff0c;无奈它面向的是场景复杂的办公领域&#xff0c;导致其越来越臃肿难用&#xff0c;反正我是该研究研究&#xff0c;但绝对不会用的。 举报 评论 1

如何下载Axure pr 9 免费使用 汉化版本

下载包得链接 链接&#xff1a;https://pan.baidu.com/s/17FSx5XfL1TAoU6Sz67yifw?pwdff00 提取码&#xff1a;ff00 然后一直下一步就行 打开软件 授权码&#xff1a; 被授权人&#xff08;License&#xff09;:AX9001 授权码&#xff08;Key&#xff09;&#xff1a;iy9u…

韭菜的自我总结

韭菜的自我总结 股市技术面量价关系左侧右侧右侧技术左侧技术洗盘 韭菜的自我修养虚拟货币的启示韭菜的买入时机韭菜的心理压力成为优秀玩家的关键 股市技术面 技术面分析可以作为买卖时机判定的工具&#xff0c;但是投资还是需要基本面的分析作为支撑。也就是基本面选股&…