架构师必知的绝活-JVM调优

前言

为什么要学JVM?

首先:面试需要

了解JVM能帮助回答面试中的复杂问题。面试中涉及到的JVM相关问题层出不穷,难道每次面试都靠背几百上千条面试八股?

其次:基础知识决定上层建筑

自己写的代码都不知道是怎么回事,怎么可能写出靠谱的系统?只有理解了JVM的工作机制,才能真正掌握Java这门语言,写出高效、稳定的代码。

然后:学习JVM也是进行JVM调优的基础

写的代码放到线上要如何运行?要配多少内存?4G够不够?线上环境出问题,服务崩溃了,怎么快速定位?怎么解决问题?这些都离不开对JVM的深入理解。

学不学JVM,是能自主解决问题的一流程序员与跟着别人做CRUD的二流程序员的分水岭!

二流程序员会觉得学JVM无关紧要,反正开发也用不上,做开发我只要学各种框架就行了。

而一流程序员都在尽自己能力把JVM每个底层逻辑整理成自己的知识体系,从而在实际工作中游刃有余,解决各种棘手的问题。

插播一条:真的免费,如果你近期准备面试跳槽,建议在cxykk.com在线刷题,涵盖 1万+ 道 Java 面试题,几乎覆盖了所有主流技术面试题、简历模板、算法刷题

一、JVM要学什么?

一个Java文件,整体的执行过程整理如下图:


这张图就是整个JVM(JDK1.8)要学的所有东西,其中细节非常多,也很容易让人学得枯燥,但是想要成为高手,就要忍受常人难以忍受的枯燥,不是吗?

下面小北主要带大家以实战的方式整理一下这些核心模块,让大家学的没那么枯燥。

二、Class文件规范

2.1 Class文件结构

首先,从上图可以看到,JVM运行的第一步,是把一个xxxx.java文件编译成为一个class文件,class文件本质上是一个二进制文件。

比如,对于一个ByCodeTest.class文件,使用UltraEdit工具打开,看到的内容部分是这样的:


看到这很多小伙伴内心一万头草泥马就奔腾而出了,这谁看得懂。

别慌,我们可以在IDEA中安装一个ByteCodeView的插件来更直观的查看一个ClassFile的内容,看到的大概内容是下面这样的:

插件安装及使用,这里就不多说了,相信对各位都是小菜一碟


可以看到,一个class文件的大致组成部分。

然后再结合官方的文档,或许能够让你开始对class文件有一个大致的感觉。

​ 例如,前面u4表示四个字节是magic魔数,而这个魔数就是不讲道理的 CAFEBABE 。

​ 而后面的两个u2,表示两个字节的版本号。例如我们用 JDK8 看我们之前的class文件,minor_version就是 00 00,major_version就是 00 34。换成二进制就是 52。52.0 这就是 JVM 给 JDK8 分配的版本号。这两个版本号就表示当前这个class文件是由JDK8编译出来的。后续就只能用8以前版本的JVM执行。这就是JDK版本向前兼容的基础。

例如,如果你尝试用JDK8去引用Spring 6或者SpringBoot 3以后的新版本,就会报错。就是因为Spring 6和SpringBoot 3发布出来的class文件,是用JDK17编译的,版本号是61。JDK8是无法执行的。

2.2 理解字节码指令

在上面的字节码指令中,我们重点需要关注的是方法,也就是我们自己写的代码的部分。

例如在ByteCodeTest中的typeTest()这个方法,在Class文件中是这样记录的:


图中 的每一行就是一个字节码指令。
上述字节码的含义,如果不考虑异常的话,那么JVM虚拟机执行的代码逻辑应该是下面这样的:

do{从程序计数器中读取 PC 寄存器的值 + 1;根据 PC 寄存器指示的位置,从字节码流中读取一个字节的操作码;if(字节码存在操作数) 从字节码流中读取对应字节的操作数;执行操作码所定义的操作;
}while(字节码流长度>0)

这些字节码指令你看不懂?没关系,至少现在,你可以知道你写的代码在 Class 文件当中是怎么记录的了😀

另外,如果你还想更仔细一点的分辨你的每一样代码都对应哪些指令,那么在这个工具中还提供了一个LineNumberTable,会告诉你这些指令与代码的对应关系。

起始 PC 就是这些指令的字节码指令的行数,行号则对应 Java 代码中的行数。
实际上,Java 程序在遇到异常时给出的堆栈信息,就是通过这些数据来反馈报错行数的。

2.3 字节码指令解读

在ByteCodeTest中,我们写了一个typeTest方法:

初学者小白看到这里肯定不禁困惑:这些莫名其妙的true和false是怎么蹦出来的?

如果你之前恰巧刷到过这样的面试题,或许你会记得这是因为JAVA的基础类型装箱机制引起的小误会。

但是你知道产生这个问题的底层原因是什么吗?

首先,我们可以从LineNumberTable 中获取到这几行代码对应的字节码指令:


以前面三行为例,三行代码对应的 PC 指令就是从 0 到 10 号这几条指令。把指令摘抄下来是这样的:

 0 bipush 102 invokestatic #2 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>5 astore_16 bipush 108 invokestatic #2 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
11 astore_2
12 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>

可以看到,在执行astore指令往局部变量表中设置值之前,都调用了一次Integer.valueOf方法。

而在这个方法中,对于[-128,127]范围内常用的数字,实际上是构建了缓存的。每次都从缓存中获取一个相同的值,他们的内存地址当然就是相等的了。

这些初学者的梦魇,是不是在这个过程中找到了终极答案?

2.4 字节码指令是如何工作的

要了解字节码指令是如何工作的,就不得不先了解一下JVM中两个重要的数据结构:局部变量表和操作数栈。

在 JVM 虚拟机中,会为每个线程构建一个线程私有的内存区域。

其中包含的最重要的数据就是程序计数器和虚拟机栈。

其中程序计数器主要是记录各个指令的执行进度,用于在 CPU 进行切换时可以还原计算结果。

虚拟机栈中则包含了这个线程运行所需要的重要数据。

虚拟机栈是一个先进后出的栈结构,其中会为线程中每一个方法构建一个栈帧。而栈帧先进后出的特性也就对应了我们程序中每个方法的执行顺序。每个栈帧中包含四个部分:

  • 局部变量表
  • 操作数栈
  • 动态链接库
  • 返回地址。

操作数栈是一个先进后出的栈结构,主要负责存储计算过程中的中间变量。

操作数栈中的每一个元素都可以是包括long型和double在内的任意 Java 数据类型。

局部变量表可以认为是一个数组结构,主要负责存储计算结果。存放方法参数和方法内部定义的局部变量。以 Slot 为最小单位。

动态链接库主要存储一些指向运行时常量池的方法引用。每个栈帧中都会包含一个指向运行时常量池中该栈帧所属方法的应用,持有这个引用是为了支持方法动态调用过程中的动态链接。

返回地址存放调用当前方法的指令地址。一个方法有两种退出方式,一种是正常退出,一种是抛异常退出。如果方法正常退出,这个返回地址就记录下一条指令的地址。如果是抛出异常退出,返回地址就会通过异常表来确定。

附加信息主要存放一些 HotSpot 虚拟机实现时需要填入的一些补充信息。这部分信息不在 JVM 规范要求之内,由各种虚拟机实现自行决定。

其中最为重要的就是操作数栈和局部变量表了

例如,对于初学者最头疼的++操作,下面的 mathTest 方法

public int mathTest(){int i = 1 ;i = i++;return i;}

i 的返回结果是多少?

我们都知道 i 的返回结果是 1 ,但是++自增操作到底有没有执行呢?就可以按照指令这样进行解释:

0 iconst_1   //往操作数栈中压入一个常量1
1 istore_1  // 将 int 类型值从操作数栈中移出到局部变量表1 位置
2 iload_1  // 从局部变量表1 位置装载int 类型的值到操作数栈中
3 iinc 1 by 1  // 将局部变量表 1 位置的数字增加 1
6 istore_1  // 将int类型值从操作数栈中移出到局部变量表1 位置
7 iload_1  // 从局部变量表1 位置装载int 类型的值到操作数栈中
8 ireturn  // 从操作数栈顶,返回 int 类型的值

这个过程中,k++是在局部变量表中对数字进行了自增,此时栈中还是 1。接下来执行=操作,就对应一个istore指令,从栈中将数字装载到局部变量表中。局部变量表中的k的值(对应索引 1 位置),就还是还原成了 1。

那么接下来,你是不是可以自行理解一下 k=++k,是怎么执行的呢?

2.5 补充知识点:大厂面试题

如何确定一个方法需要多大的操作数栈和局部变量?

实际上,每个方法在执行前都需要申请对应的资源,主要是内存。如果内存空间不够,就要在执行前直接抛出异常,而不能等到执行过程中才发现要用的内存空间申请不下来。

有些面试时,是会给你一个具体的方法,让你自己一下计算过程中需要几个操作数栈和几个局部变量。

这是对算法的基础要求。但是在工作中,其实class文件当中就记录了所需要的操作数栈深度和局部变量表的槽位数。

例如对于 mathTest方法,所需的资源在工具中的纪录是这样的:

以后被领导问的时候可以直接通过这种方式告诉他就行了

这里会有一个小问题,如果你自己推演过刚才的计算过程,可以看到,局部变量表中,明明只用到了索引为 1 的一个位置而已,为什么局部变量表的最大槽数是 2 呢?

这是因为对于非静态方法,JVM 默认都会在局部变量表的 0 号索引位置放入this变量,指向对象自身。所以我们可以在代码中用this访问自己的属性。

一个槽可以存放 Java 虚拟机的基本数据类型,对象引用类型和returnAddress类型

插播一条:真的免费,如果你近期准备面试跳槽,建议在cxykk.com在线刷题,涵盖 1万+ 道 Java 面试题,几乎覆盖了所有主流技术面试题、简历模板、算法刷题

三、类加载

Class文件中已经定义好了一个Java程序执行的全部过程,接下来就是要扔到JVM中执行。

既然要执行,就少不了类加载的模块。而有趣的是,类加载模块是少数几个可以在Java代码中扩展的JVM底层功能。

类加载模块在JDK8之后,发生了非常重大的变化,本文主要以JDK8为例讲解

3.1 JDK8的类加载体系

有了 Class 文件之后,接下来就需要通过类加载模块将这些 Class 文件加载到 JVM 内存当中,这样才能执行。而关于类加载模块,最为重要的内容有以下三点:

  • 每个类加载器对加载过的类保持一个缓存。
  • 双亲委派机制,即向上委托查找,向下委托加载。
  • 沙箱保护机制。

3.2 双亲委派机制

JDK8中的类加载器都继承于一个统一的抽象类ClassLoader,类加载的核心也在这个父类中。

其中,加载类的核心流程如下:


核心代码如下:

//类加载器的核心方法
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// 每个类加载起对他加载过的类都有一个缓存,先去缓存中查看有没有加载过Class<?> c = findLoadedClass(name);if (c == null) {//没有加载过,就走双亲委派,找父类加载器进行加载。long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {}if (c == null) {long t1 = System.nanoTime();// 父类加载起没有加载过,就自行解析class文件加载。c = findClass(name);sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}//这一段就是加载过程中的链接Linking部分,分为验证、准备,解析三个部分。// 运行时加载类,默认是无法进行链接步骤的。if (resolve) {resolveClass(c);}return c;}}

这个方法里,就是最为核心的双亲委派机制。

双亲委派机制说简单点就是,先找父亲加载,不行再由儿子自己加载

并且,这个方法是protected声明的,意味着,是可以被子类覆盖的,所以,双亲委派机制也是可以被打破的。
而关于类加载机制的所有有趣的玩法,也都在这个核心方法里。比如class文件加密加载,热加载等。

补充知识点(面试题:Tomcat为什么要打破双亲委派呢?)

3.3 沙箱保护机制

双亲委派机制有一个最大的作用就是要保护JDK内部的核心类不会被应用覆盖。

而为了保护JDK内部的核心类,JAVA在双亲委派的基础上,还加了一层保险。就是ClassLoader中的下面这个方法。

private ProtectionDomain preDefineClass(String name,ProtectionDomain pd){if (!checkName(name))throw new NoClassDefFoundError("IllegalName: " + name);// 不允许加载核心类if ((name != null) && name.startsWith("java.")) {throw new SecurityException("Prohibited package name: " +name.substring(0, name.lastIndexOf('.')));}if (pd == null) {pd = defaultDomain;}if (name != null) checkCerts(name, pd.getCodeSource());return pd;}

这个方法会用在JAVA在内部定义一个类之前。

这种简单粗暴的处理方式,当然是有很多时代的因素。也因此在JDK中,你可以看到很多javax开头的包。这个奇怪的包名也是跟这个沙箱保护机制有关系的。

四、执行引擎

之前已经看到过,在 Class 文件当中,已经明确的定义清楚了程序的完整执行逻辑。

而执行引擎就是将这些字节指令转为机器指令去执行了。

这一块更多的是跟操作系统打交道,对开发工作其实帮助就不是很大了。

所以,如果不是专门研究语言,执行引擎这一块就没有必要研究太深了。我们也直接跳过。

五、GC垃圾回收

执行引擎会将class文件扔到JVM的内存当中运行,在运行过程中,需要不断的在内存当中创建并销毁对象。

在传统C/C++语言中,这些销毁的对象需要手动进行内存回收,防止内存泄漏。而在Java当中,实现了影响深远的GC垃圾回收机制。

GC 垃圾自动回收,这个可以说是 JVM 最为标志性的功能。

不管是做性能调优,还是工作面试,GC 都是 JVM 部分的重中之重。

而对于 JVM 本身,GC 也是不断进行设计以及优化的核心。

几乎 Java 提出的每个版本都对 GC 有或大或小的改动。

这里,我就用目前还是用得做多的 JDK8,带大家快速梳理一下 GC 部分的主线。

插播一条:真的免费,如果你近期准备面试跳槽,建议在cxykk.com在线刷题,涵盖 1万+ 道 Java 面试题,几乎覆盖了所有主流技术面试题、简历模板、算法刷题

5.1 垃圾回收器是干什么的?

在了解 JVM之前,给大家推荐一个工具,阿里开源的 Arthas

官网地址:https://arthas.aliyun.com/
这个工具功能非常强大,是对 Java进程进行性能调优的一个非常重要的工具,对于了解 JVM 底层帮助也非常大。

public class GCTest {public static void main(String[] args) throws InterruptedException {List l = new ArrayList<>();for(int i = 0 ; i < 100_0000 ; i ++){l.add(new String("dddddddddddd"));Thread.sleep(100);}}
}

运行后,使用Arthas 的dashboard指令,可以查看到这个 Java 程序的运行情况。

重点关注中间的 Memory 部分,这一部分就是记录的 JVM 的内存使用情况

  • ps_eden_space:伊甸园区
  • ps_survivor_space:幸存区
  • ps_old_gen:老年代
  • nonheap:非堆内存
  • code_cache:热点指令缓存
  • metaspace:元空间
  • compressed_class_space:压缩类空间

而后面的 GC 部分就是垃圾回收的执行情况。我

们就从这些能看到的部分作为入口,来理解一下一个 Java 进程是怎么管理他的内存的。

从 Memory 部分可以看到,一个 Java 进程会将他管理的内存分为heap堆区和nonheap非堆区两个部分。其中非堆区的几个核心部分像code_cache(热点指令缓存),metaspace(元空间),compressed_class_space(压缩类空间)。这一部分就相当于 Java 进程中的地下室,属于不太活跃的部分。

而中间heap堆区就相当于客厅了,属于Java 中最为核心的部分。

而这其中,又大体分为了eden_spacesurvivor_spaceold_gen三个大的部分,这就是 JVM 内存的主体

堆区是JVM用来存放对象的核心内存区域。

它的大小可以通过两个参数来控制:-Xms(初始堆内存大小)和 -Xmx(最大堆内存大小)。

通过这两个参数可以看出,堆内存是可以扩展的。如果初始内存不够用,JVM会自动扩大堆内存。

但是,如果堆内存扩展到了最大值还不够用,就没法继续扩展了,这时候就会抛出OOM(Out of Memory)异常。

在生产环境中,建议把 -Xms 和 -Xmx 设置成一样的大小,这样可以减少内存扩展时的性能消耗。

GC垃圾回收器的任务就是及时回收这些内存空间,让内存可以重复利用,从而提升系统的性能和稳定性。

5.2 分代收集模型

不同GC,对内存的管理和回收的方式都是不同的。但是这其中面试最喜欢问的,就是关于垃圾分代收集模型。

在Memor部分还可以看到多次出现了 ps_ 这样的字样。这其实就代表JDK8默认的垃圾回收器Parallel Scavenge。

其整体的工作机制如下图:

你知道吗?Java做过统计,80%的对象都是“朝生夕死”,换句话说,这些对象创建快,消亡也快。

这些短命的对象被放在一个比较小的内存区域,这块地方叫“年轻代”。

在年轻代,垃圾回收特别频繁,叫做YoungGC。年轻代又被进一步分成三个区域:一个叫eden区,两个叫survivor区。

默认情况下,这三个区域的大小比例是8:1:1。

那剩下的20%长寿对象去哪了?

它们被放到另一块内存区域,这地方叫“老年代”。

老年代的对象竞争没那么激烈,所以垃圾回收的频率也低,只有空间不够用时才进行,叫OldGC。

年轻代和老年代的默认大小比例是1:2。

在常见的分代收集模型中,对象会首先在eden区创建,经过一次YoungGC后,如果对象没有被回收,就会被移动到一个survivor区。

下一次YoungGC时,这些幸存的对象又会被移动到另一个survivor区。每次移动,都会记录一个分代年龄。一直到分代年龄达到阈值(默认是16),这些对象就会被移到老年代。到了老年代后,就不再记录分代年龄了,安安静静地待着,直到“退休”。

这就是JDK最有代表性的分代收集机制。通过这个机制,JVM可以对不同的对象采取不同的回收策略,从而大大提高垃圾回收的效率。

5.3 JVM中有哪些垃圾回收器?

java 从诞生到现在最新的 JDK21 版本,总共就产生了以下十个垃圾回收器


其中,左边的都是分代算法。也就是将内存划分为年轻代和老年代进行管理。

而有虚线的部分表示可以协同进行工作。

JDK8默认就是使用的Parallel Scavenge和Parallel Old的组合。也就是在arthas的dashboard中看到的ps。

右侧的是不分代算法。也就是不再将内存严格划分位年轻代和老年代。

JDK9 开始默认使用 G1。而 ZGC是目前最先进的垃圾回收器。

shennandoah则是OpenJDK 中引入的新一代垃圾回收器,与 ZGC 是竞品关系。Epsilon是一个测试用的垃圾回收器,根本不干活。

六、GC 情况分析实例

GC可以说是决定JAVA程序运行效率的关键。因此我们一定要学会定制GC参数,以及分析GC日志,从而达到调优的目的

6.1 如何定制GC运行参数

现在,不同的GC垃圾回收器适用于不同的场景,所以我们得根据业务场景来定制合理的GC运行参数。

在Java程序运行过程中,会遇到各种问题:

有时CPU飙高
有时FullGC频繁
有时OOM异常等等

这些问题大多需要凭经验深入分析,才能对症下药。

那我们该怎么定制JVM运行参数呢?

首先得知道有哪些参数可以选择。

JVM的参数主要有三类

  • 标准参数:以-开头,所有HotSpot都支持。例如java -version。可以用java -help或java -?查看所有标准参数。

  • 非标准参数:以-X开头,是特定HotSpot版本支持的指令。例如java -Xms200M -Xmx200M。可以用java -X查看所有非标准参数。

  • 不稳定参数:以-XX开头,这些参数与特定HotSpot版本对应,可能换个版本就没有了,文档资料也特别少。以下是JDK8中的几个有用指令:

java -XX:+PrintFlagsFinal:打印所有最终生效的不稳定指令。
java -XX:+PrintFlagsInitial:打印默认的不稳定指令。
java -XX:+PrintCommandLineFlags:打印当前命令的不稳定指令,可以看到使用了哪种GC。JDK1.8默认用的是ParallelGC。

例如下面一个简单的示例代码:

public static void main(String[] args) {ArrayList<byte[]> list = new ArrayList<>();for (int i = 0; i < 500; i++) {byte[] arr = new byte[1024 * 100];//100KBlist.add(arr);try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}

然后在执行这个方法时,添加以下 JVM 参数:

-Xms60m -Xmx60m -XX:SurvivorRatio=8 -XX:+PrintGCDetails


执行后,可以看到下面输出内容

这里面就记录了两次 MinorGC 和两次 FullGC 的执行效果。

当然,目前这些日志信息只是打印在控制台,你只能凭经验自己强行去看。

接下来,就可以添加-Xloggc参数,将日志打印到文件里。然后拿日志文件进行整体分析。

6.3 GC日志分析

这些GC日志隐藏了项目运行非常多隐蔽的问题

要如何发现其中的这些潜在的问题呢?

肉眼去看肯定是不现实的,这时候就要使用工具了。

这里推荐一个开源网站 https://www.gceasy.io/ 这是国外一个开源的GC 日志分析网站。

你可以把 GC 日志文件直接上传到这个网站上,他就会分析出日志文件中的详细情况。

这是个收费网站,但是有免费使用的额度

例如,在我们之前的示例中,添加一个参数 -Xloggc:./gc.log ,就可以将GC日志打印到文件当中。

接下来就可以将日志文件直接上传到这个网站上。网站就会帮我们对GC情况进行分析。示例文件得到的报告是这样的:

通过这份报告,你能迅速识别项目运行中潜藏的问题。报告不仅提供了具体的修改建议,还包含详尽的指标分析。如果你觉得这些建议还不够详细,可以利用这些指标做进一步的分析,找到更多改进的方向。

如果是你们自己开发的项目,那接下来就可以根据这些建议和数据,深入分析,调整参数,优化配置。到了这一步,恭喜你,已经成功入门架构师的核心技能——JVM调优了。

总结

聊到这里,你对JVM是不是有点感觉了?

在这个过程中,你是不是还有很多细节上的疑问?

保持这些疑问,它们会成为你后续深入学习那些晦涩枯燥的底层理论的动力。这不会是一个容易的过程,但正是因为有挑战,才更有价值,不是吗?

踏上这条学习JVM的路,你会发现其中的复杂与精彩。而那些疑问和挑战,正是推动你不断前行的力量。保持好奇心和耐心,你才能一步步掌握这门技术,成为真正的Java架构师!

最后说一句(求关注,求赞,别白嫖我)

最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。
这是大佬写的, 7701页的BAT大佬写的刷题笔记,让我offer拿到手软

本文,已收录于,我的技术网站 cxykk.com:程序员编程资料站,有大厂完整面经,工作技术,架构师成长之路,等经验分享

求一键三连:点赞、分享、收藏

点赞对我真的非常重要!在线求赞,加个关注我会非常感激!

真的免费,如果你近期准备面试跳槽,建议在cxykk.com在线刷题,涵盖 1万+ 道 Java 面试题,几乎覆盖了所有主流技术面试题、简历模板、算法刷题

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图形处理单元(GPU)在现代计算中的应用与挑战(研究论文框架)

摘要:随着高性能计算需求的日益增长,图形处理单元(GPU)已从专业的图形渲染处理器转变为具有高性能并行处理能力的多功能计算平台。本文将探讨GPU的核心优势、编程模型、在不同领域的应用以及面临的挑战和限制。此外,还将讨论GPU技术的未来发展趋势和潜在的研究机会。 关键…

[计算机网络] 虚拟局域网

虚拟局域网 VLAN&#xff08;Virtual Local Area Network&#xff0c;虚拟局域网&#xff09;是将一个物理的局域网在逻辑上划分成多个广播域的技术。 通过在交换机上配置VLAN&#xff0c;可以实现在同一个VLAN 内的用户可以进行二层互访&#xff0c;而不同VLAN 间的用户被二…

hive零基础入门

1、hive简介 hive&#xff1a;由facebook开源用于解决海量结构化数据的统计工具。 hive是基于Hadoop的数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供sql查询功能。 2、hive本质 hive的本质是HQL&#xff08;HiveSQL&#xff09;转化成MapR…

快速应用开发(RAD):加速软件开发的关键方法

目录 前言1. 快速应用开发的概念1.1 什么是快速应用开发&#xff1f;1.2 RAD与传统开发方法的对比 2. 快速应用开发的实施步骤2.1 需求分析与规划2.2 快速原型开发2.3 用户评估与反馈2.4 迭代开发与改进2.5 最终交付与维护 3. 快速应用开发的优点与应用场景3.1 优点3.2 应用场景…

UNIAPP编译到微信小程序时,会多一层以组件命名的标签

UNIAPP编译到微信小程序时&#xff0c;会多一层以组件命名的标签 解决方案 可以配置virtualHost来配置 export default {options: {virtualHost: true} }

Navicat安装与连接教程

navicat 的安装 官网&#xff1a;https://www.navicat.com.cn/ 进入官网之后点击左上角的产品&#xff0c;然后往下滑动就可以看见许多类型&#xff0c;我们使用的是MongoDB数据库&#xff0c;所以就下载Navicat 17 for MongoDB 进入到这里之后&#xff0c;选择自己的系统版本…

面试-J.U.C包的梳理

1.J.U.C包的梳理 Java.Util.Concurrent包简称JUC (1)JUC整体架构图 (2)分析 Executor&#xff1a;线程执行器&#xff0c;任务执行和调度的框架。Tools下存在executor相关的executors类&#xff0c;用于创建executorservice&#xff0c;scheduleexecutorservice&#xff0c;…

京东云备案流程图_云主机快速ICP备案_京东云服务器备案问题解答

京东云ICP备案流程&#xff0c;备案包括网站和APP备案&#xff0c;以及备案问题解答FAQ&#xff0c;阿腾云以京东云网站域名备案流程为例&#xff0c;先填写主办单位信息&#xff0c;选择网站备案或APP备案&#xff0c;申请授权码并验证&#xff0c;填写并上传主办单位详细信息…

学生宿舍管理系统

摘 要 随着高校规模的不断扩大和学生人数的增加&#xff0c;学生宿舍管理成为高校日常管理工作中的重要组成部分。传统的学生宿舍管理方式往往依赖于纸质记录和人工管理&#xff0c;这种方式不仅效率低下&#xff0c;而且容易出错&#xff0c;无法满足现代高校管理的需求。因此…

gemini 1.5 flash (node项目)

https://www.npmjs.com/package/google/generative-ai https://ai.google.dev/pricing?hlzh-cn https://aistudio.google.com/app/apikey https://ai.google.dev/gemini-api/docs/models/gemini?hlzh-cn#gemini-1.5-flash https://ai.google.dev/gemini-api/docs/get-started…

数据可视化在智慧园区中的重要作用

在现代智慧园区的建设和管理中&#xff0c;数据的作用越来越重要。智慧园区利用物联网、云计算、大数据等技术&#xff0c;实现了园区各类信息的实时采集和处理。数据可视化作为数据处理和展示的重要工具&#xff0c;为智慧园区的各个方面提供了强有力的支持。 首先&#xff0c…

家电品牌如何利用3D数字化技术,突破转型瓶颈?

家电行业正经历着从增量市场向存量市场的转变&#xff0c;用户的消费观念也日趋成熟&#xff0c;更加注重产品的体验和服务质量。无论是线上购物平台还是线下实体门店&#xff0c;提供个性化和增强体验感的产品与服务已成为家电市场未来发展的核心驱动力。 51建模网依托“3D数字…

视频共享融合赋能平台LntonCVS统一视频接入平台数字化升级医疗体系

医疗健康事关国计民生&#xff0c;然而&#xff0c;当前我国医疗水平的地区发展不平衡、医疗资源分布不均和医疗信息系统老化等问题&#xff0c;制约了整体服务能力和水平的提升。视频融合云平台作为推动数字医疗的关键工具&#xff0c;在医疗领域的广泛应用和普及&#xff0c;…

YOLOv5改进 | 注意力机制 | 迈向高质量像素级回归的极化自注意力【全网独家】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录&#xff1a; 《YOLOv5入门 …

Linux高并发服务器开发(二)系统调用函数

文章目录 1 系统调用2 errno3 虚拟内存空间4 文件描述符5 常用文件IO函数6 阻塞和非阻塞7 lseek 偏移函数8 文件操作函数之stat函数9 文件描述符复制 dup10 fcnlt函数 修改文件属性11 目录相关操作12 时间相关函数 1 系统调用 根据系统调用&#xff0c;获取驱动信息、CPU的信息…

数据平台发展史-从数据仓库数据湖到数据湖仓

做数据的同学经常听到一些数据相关的术语&#xff0c;常见的包括数据仓库&#xff0c;逻辑数据仓库&#xff0c;数据湖&#xff0c;数据湖仓/湖仓一体&#xff0c;数据网格 data mesh,数据编织 data fabric等. 笔者在这里回顾了下数据平台的发展史&#xff0c;也介绍和对比了下…

Nvidia显卡GeForce Experience录屏操作流程

安装软件 首先我们从英伟达官网下载GeForce Experience程序&#xff0c;安装在电脑中GeForce Experience&#xff08;简称 GFE&#xff09;自动更新驱动并优化游戏设置 | NVIDIA 登录软件 安装完成后登录 开启录屏功能 登录后点击右上角的设置&#xff08;小齿轮图标&#x…

(单机版)神魔大陆|v0.51.0|冰火荣耀

前言 今天给大家带来一款单机游戏的架设&#xff1a;神魔大陆v0.51.0:冰火荣耀。 如今市面上的资源参差不齐&#xff0c;大部分的都不能运行&#xff0c;本人亲自测试&#xff0c;运行视频如下&#xff1a; (单机版)神魔大陆 下面我将详细的教程交给大家&#xff0c;请耐心阅…

C语言力扣刷题1——最长回文字串[双指针]

力扣算题1——最长回文字串[双指针] 一、博客声明二、题目描述三、解题思路1、思路说明2、知识补充a、malloc动态内存分配b、free释放内存c、strlen求字符数组长度d、strncpy函数 四、解题代码&#xff08;附注释&#xff09; 一、博客声明 找工作逃不过刷题&#xff0c;为了更…

JS在线加密简述

JS在线加密&#xff0c;是指&#xff1a;在线进行JS代码混淆加密。通过混淆、压缩、加密等手段&#xff0c;使得JS源代码难以阅读和理解。从而可以有效防止代码被盗用或抄袭&#xff0c;保护开发者的知识产权和劳动成果。常用的JS在线加密网站有&#xff1a;JShaman、JS-Obfusc…