【JVM】class文件格式,JVM加载class文件流程,JVM运行时内存区域,对象分配内存流程

这篇文章本来只是想讲一下class文件格式,讲着讲着越讲越多。JVM这一块吧,知识比较散比较多,如果深研究下去如死扣《深入理解Java虚拟机》,这本书很深很细,全记住是不可能的,其实也没必要。趁这个机会直接把标题中的这些的主要知识点都总结一下,不会过深,也不会是太浮于表面的八股文,总结一下比较好记,也省的后面自己再忘了。

主要内容包括以下几部分:
class文件格式
JVM加载class文件流程
JVM运行时内存区域
对象分配内存流程

一,class文件格式

在idea中,一般我们直接点开target目录中的class文件,idea会帮助我们反编译后展示为java文件,那么如何观察到class文件的格式呢?

这里我们需要在idea中安装一个Jclasslib的插件,如下图所示:
在这里插入图片描述
鼠标光标停在类名上,点击view->show bytecode with jclasslib,即可查看到class文件。如下图所示:

在这里插入图片描述
class文件[注2]包含但不限于以下几个部分,

开头是著名的魔数0xCAFEBABE(如果没有特殊说明,这里及下面的进制都是16进制),CAFEBABE这个魔数是用于标识和校验这个文件是否是一个class文件的。

此外,class文件中还包括如Minor VersionMajor Version,用于标识Java版本号;

constant_pool(常量池),常量池用于存储字面量(Literal)符号引用(Symbolic References)。字面量比较接近Java语言层面的常量概念,如文本字符串、被声明为final的常量值等。而符号引用则属于编译原理方面的概念,主要包含下面几类常量[1]:
被模块导出或者开放的包(Package)
类和接口的全限定名(Fully Qualified Name)
字段的名称和描述符(Descriptor)
方法的名称和描述符()
方法句柄和方法类型(Method Handle、Method Type、Invoke Dynamic)
动态调用点和动态常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)
常量池中的这些常量和符号引用,在JVM运行时会存放到方法区中的常量池中[1]。而如Fields、Methods等元数据信息,存放在方法区中。

另外,什么是符号引用呢?这就涉及到了JVM的懒加载机制[2],就是说在编译成字节码class文件后,JVM不会立即加载class文件,而是用到的时候再进行加载[注1]。这就导致了class文件在被编译出来后,无法立即被分配到内存,也就没有直接指向内存的指针。那么,我们的字节码指令,又是如何调用类、方法、成员变量的呢?

在字节码class文件中,方法、成员变量、类以全限定名(即字符串常量)的方式被引用。如下图所示,Fieldref_info,即存储指向成员变量的指针(在下图中举例的是指向字符串String s),可以看到,描述了其类的全限定名、成员变量的名字和其类型的全限定名,点击跳转到字段,会跳到字段中,即完成了通过字符串常量作为指针,跳转到字段/方法/类的功能,这就是所谓的字符引用,说白了就是把全限定名的字符串作为引用的标识。

这些符号引用一部分会在类加载阶段或者第一次使用的时候就要被转化为直接引用,这种转换成为静态解析。另外一部分将在每一次运行期间都转化为直接引用,这部分就称为动态连接[1]。

java虚拟机支持5种方法调用字节码指令:
invokestatic:调用静态方法
invokespecial:用于调用实例构造器()方法、私有方法和父类中的方法
invokevirtual:调用所有的虚方法
invokeinterface:调用接口方法,会在运行时再确定一个实现该接口的对象
invokedynamic:先在运行时动态解析出调用带你限定符所引用的方法,然后再执行该方法。前面4条调用指令,分派逻辑都固化在Java虚拟机内部,而invokedynamic指令的分派逻辑是由用户设定的引导方法来决定的。

其中只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本,Java语言里符合这个条件的方法共有静态方法、私有方法、实例构造器、父类方法4种,再加上被final修饰的方法(尽管它使用invokevirtual指令调用),这5种方法调用会在类加载的时候就可以把符号引用解析为该方法的直接引用。这些方法统称为“非虚方法”(Non-Virtual-Method),与之相反,其他方法就被称为“虚方法”[1]。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

讲完了常量池,class文件中剩下的就比较好讲了。如描述字段的表fields,描述方法的表methods,描述接口的表等等。这些就不展开讲了,如果对其他部分感兴趣,可以自己去查资料或者自己下一个jclasslib去看一看。

二,JVM加载class文件流程

在javac编译完class文件后,显而易见,如果要使用这些类,需要去加载它们(这里说的加载,指的是广义上的加载,不是loading这一步)。JVM加载class文件,分为以下几步,

在这里插入图片描述

第一步,加载(loading),《深入理解Java虚拟机》中是这么说的:*通过一个类的全限定名来获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。在内存中生成一个代表这个类的java.lang.Class对象,作为方法去这个类的各种数据的访问入口。*听起来是有点复杂,其实就是把双亲委派机制的工作内容说出来了:通过类的全限定名,获取其字节流,在堆中申请class对象的内存空间,把类的各种元数据如方法表、属性表、常量池等放入方法区。双亲委派机制就是用来加载类的,具体的可以看我上一篇文章,讲的就是双亲委派机制是如何工作的[4]。

第二步,连接(linking),这一步中分3小步。其中第一小步,验证(verification),验证文件格式(如魔数是否为CAFEBABE、主次版本号等)、验证元数据、验证字节码、验证符号引用。总之就是要验证文件格式是否符合JVM规范,看看是否是一个合格的class文件格式;第二小步,准备(preparation),给静态变量赋默认值;第三小步,解析(resolution),将常量池内的符号引用替换为直接引用,即前面提到的静态解析。

第三步,初始化(initializing),这一阶段会调用类构造器< clinit>()。

需要注意的是,< clinit>()构造方法,并不是实例对象的构造方法< init>()。《深入理解Java虚拟机》中讲:< clinit>()构造方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{})中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序决定的。 我个人的理解就是,在loading阶段申请了这个类的class对象的内存,在initializing阶段,会通过编译器自动生成的类构造器方法来完成类class对象的初始化,即给静态变量和静态语句块中的变量赋初始值。毕竟静态变量是类的成员变量。

三,JVM运行时内存区域

前文讲了编译出的class文件结构,JVM加载class文件流程,剩下的我们聊一下JVM在运行时的内存区域的划分。

JVM内存可以划分为程序计数器(Program Counter, PC)Java虚拟机栈(Java Virtual Machine stack, JVM stack)本地方法栈(native method stacks)方法区(method area)堆(Heap)直接内存(Derict Memory)

其中程序计数器和Java虚拟机栈是线程私有的,本地方法栈、方法区、堆、直接内存都是线程公有的。

程序计数器,这一块内存区域较小,可以看作是当前线程所执行的字节码的行号指示器。

Java虚拟机栈,虚拟机栈描述的是Java方法执行的线程内存模型,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

对于执行引擎来说,在活动线程中,只有位于栈顶的方法才是运行的,只有位于栈顶的栈帧才是生效的,其被称为”当前栈帧“(Current Stack Frame),与这个栈帧所关联的方法被称为”当前方法“(Current Method)。执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作。

每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。

其中,局部变量表(Local Variables Table)是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。

操作数栈(Operand Stack)也常被称为操作栈,它是一个后入先出(Last In First Out, LIFO)栈。当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作。

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。实际上因为有的方法进行静态解析而不是动态连接(还记得吗,能被invokestatic和invokespecial指令调用的方法和被final修饰的方法都会进行静态解析),不是所有栈帧的这个方法引用都能进行动态连接。

这么描述可能有点抽象,我们结合jclasslib来看一下

很简单的一段代码

在这里插入图片描述

对应的字节码

在这里插入图片描述

下面的LocalVariable Table为局部变量表

在这里插入图片描述

看到字节码望而生畏,这么多字节码指令记不住怎么办?字节码不需要记,需要的话查就好了(一般也用不到debug到字节码的程度吧,除了一些面试用的题…),左键单击字节码指令,再点击Show JVM Spec,即可跳转到Java虚拟机规范中对应字节码的查询结果[5]。

在这里插入图片描述

在这里插入图片描述

m方法字节码流程如下:
iconst_0,《JVM虚拟机规范》[5]中规定如下,因此iconst_0意为将整数int 0压入操作数栈

在这里插入图片描述

istore_1,注意description中的描述,istore_< n>中的n,是当前栈帧的局部变量表的索引值,istore_1是将操作数栈顶的整数类型变量,即栈顶的数0弹出操作数栈,并且赋值给局部变量表索引为1的位置,即给局部变量i赋值为0。iconst_0和istore_1结合起来,就是代码int i = 0;

在这里插入图片描述

在这里插入图片描述

iload_1,将局部变量表中索引为1的位置的数据弹出,并压入操作数栈中。

在这里插入图片描述

iinc 1 by 1,将局部变量表中索引为1位置的数+1 。注意这个操作和操作数栈没有关系。完成了i++

在这里插入图片描述

istore_2,将操作数栈中的数0弹出,赋值给局部变量表中2号索引位置。即int j = 0;

最后,返回,没有任何返回值。如果当前栈帧(m方法)下面还有其他栈帧,当前栈帧return后弹出,会由调用m方法的栈帧继续执行,成为新的当前栈帧。当然,这里m方法没有任何调用,到此,字节码结束。

有一些面试题会考i = i++和i = ++i的区别,实质上就是字节码istore_1和iinc 1 by 1顺序调换导致的。当然任何人都不会在项目中这么写代码,考这个纯粹为了考而考了…

在这里插入图片描述

Java虚拟机栈说到这里。

本地方法栈(Native Method Stacks),与虚拟机栈所发挥的作用是非常相似的,其区别是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。《Java虚拟机规范》对本地方法栈中的方法使用的语言、使用方式和数据结构并没有任何强制规定,因此具体的虚拟机可以根据需要自由实现它[1]。

Java堆(Java Heap),对于Java应用来说,Java堆是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存。Java堆是垃圾收集器管理的内存区域,因此一些资料中它也被称作”GC堆“(Garbage Collected Heap)[1]。

从回收内存的角度看,由于现代垃圾收集器大部分都是基于分代收集理论设计的,所以Java堆中经常会出现”新生代“ ”老年代“ “永久代” “Eden空间” “From Survivor空间” "To Survivor空间"等名词,在这里笔者想先说明的是这些区域划分仅仅是一部分垃圾收集器的共同特性或者说设计风格而已,而非某个Java虚拟机具体实现的固有内存布局,更不是《Java虚拟机规范》里对Java堆的进一步细致划分[1]。

需要注意的是,尽管Java堆中可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer, TLAB),以提升对象分配时的效率。TLAB占用伊甸区eden,默认1%。用于给小对象在多线程的情况下不用竞争eden区分配就可以申请空间,提高效率。

方法区(Method Area),线程共享的内存区域,它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

其中,**运行时常量池(Runtime Constant Pool)**是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

说到方法区,不得不提一下“永久代”这个概念,尤其是在JDK8之前,许多程序员习惯在HotSpot虚拟机上开发、部署程序,很多人都更愿意把方法区称呼为“永久代”(Permanent Generation),或将两者混为一谈。本质上这两者不是等价的,因为仅仅是当时的HotSpot虚拟机设计团队选择把收集器的分代设计扩展至方法区,或者说使用分代来实现方法区而已,这样使得HotSpot的垃圾收集器能够像管理Java堆一样管理这些内存,省去专门为方法区编写内存管理代码的工作。但是对于其他虚拟机实现,譬如BEAJRockit、IBM J9等来说,是不存在永久代概念的。原则上如何实现方法区属于虚拟机的实现细节,不受《Java虚拟机规范》管束,并不要求统一。但是回过头来看,当年使用永久代来实现方法区的决定并不是一个好主意,这种设计使得Java应用更容易遇到内存溢出的问题。到了JDK7的HotSpot,已经把原来存放在永久代的字符串常量池、静态变量等移出,到了JDK8,终于完全废弃了永久代的概念,改用与JRockit、J9一样在本地内存中实现的元空间(Meta Space)来代替,把JDK7中永久代还剩余的内容(主要是类型信息)全部移到元空间中。

直接内存(Derict Memory),直接内存并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。但是这部分内存也被频繁的使用,而且也可能导致OutOfMemoryError异常出现,所以我们放到这里一起讲解。

在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

显然,本机直接内存的分配不会收到Java堆大小的限制,但是,既然是内存,则肯定还是会受到本机总内存(包括物理内存、SWAP分区或者分页文件)大小以及处理器寻址空间的限制,一般服务器管理员配置虚拟机参数时,会根据实际内存去设置-Xmx等参数信息,但经常忽略掉直接内存,使得各个内存区域综合大于物理内存限制(包括物理的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。

四,对象分配内存的流程

我们通常堆(Heap)是用于存储对象实例的,那么一个对象是直接就被分配到堆中吗?并不是的。但是在了解对象的分配流程之前,我们需要先了解一些内容。

栈上分配,满足栈上分配的要求如下:
线程私有小对象
无逃逸
支持标量替换

需要说明的是,栈上分配需要预先开启标量替换和逃逸分析才能使用[6],但是一般情况下,无需对栈上分配这两项设置进行更改和调优。

其中,逃逸指的是这个对象被多个线程共享,无逃逸即这个对象就在这一段代码中使用,没有return出去[6];而标量替换是说把一个对象中的字段直接分配到栈上,用于代替整个对象。

分配缓冲区(Thread Local Allocation Buffer, TLAB)(前文讲过,这里再复述一遍),Java堆中可以划分出多个线程私有的分配缓冲区,以提升对象分配时的效率。TLAB占用伊甸区eden,默认1%。用于给小对象在多线程的情况下不用竞争eden区分配就可以申请空间,提高效率。满足TLAB的条件如下:
小对象

一个对象分配内存的流程如下
如果能够栈上分配,就栈上分配;

剩下的对象,看看是否足够大,如果足够大,直接分配到老年代。

剩下的对象,看看是否满足分配到TLAB,如果可以,分配到Eden区中的TLAB。

剩下的对象,分配到Eden区。

Eden区(包括TLAB)中的对象,等到年轻代空间满了,会触发YGC对年轻代进行GC。经历了YGC后,还存活的对象,会迁移到S1区。S1区的对象经历YGC,会进入S2区;S2区的对象经历YGC,会进入S1区。当S1区和S2区的对象在经历了YGC后达到了进入老年代的要求后,就可以进入老年代。

老年代一旦触发FGC,就会开始对整个堆进行GC,存活的对象仍然留在老年代中,直到被GC回收。

在这里插入图片描述

这里解释一下关于JVM分代的一些术语,如前文所述,部分JVM实现是按照分代设计的,按GC后存活次数分为老年代,年轻代。其中年轻代分为Eden区(伊甸区),S1区(Survivor1区)、S2区(Survivor2区)(也有的叫做S0区和S1区,或者from区和to区,都是一个意思),而老年代翻译为old区或者tenured区。笼统的讲,新生的对象会进入Eden区,经历了YGC会进入S1区,随后在S1和S2区中间来回倒腾,直到进入老年代为止。

YGC,即Young GC或者Minor GC,是年轻代触发GC后对年轻代的垃圾回收,包括Eden和S1 S2。

FGC,即Full GC或者Major GC,是老年代触发GC后对整个堆的垃圾回收。

前文说,”对象在S1和S2中间来回移动直到满足进入老年代的条件为止“。进入老年代的条件有二,满足其一即可进入老年代:

1,超过-XX:MaxTenuringThreshold指定次数(YGC)
对于常见的垃圾回收器,这个值是不同的,如

Parallel Scavenge   15
CMS    6
G1   15

2,满足动态年龄要求,即
在Survivor空间中低于或等于某年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到-XX:MaxTenuringThreshold中要求的年龄。

注1[3]:
严格来讲,JVM没有规定何时加载,但是规定了什么时候必须初始化。
new getstatic putstatic invokestatic指令,访问final变量除外
java.lang.reflect对类进行反射调用
初始化子类的时候,父类首先初始化
虚拟机启动时,被执行的主类必须初始化
动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic REF_putstatic REF_invokestatic 的方法句柄时,该类必须初始化

注2:
一般来说,我们说Java文件编译成为class文件字节码,再由JVM的解释器(Bytecode Interpreter)逐行解释为机器码,这是Java或者说JVM能够跨平台的关键。其实Java中有一种即时编译机制JIT(Just In-Time compiler),这种机制可以将代码编译为机器码,进而不需要每次执行热点代码都需要解释执行——编译为机器码后直接执行机器码即可。
Java默认为混合模式,即混合使用解释器+热点代码编译,起始阶段采用解释执行,通过热点代码探测,检测到热点代码后,对其进行JIT即时编译,并对其进行编译执行。
-Xmixed 默认混合模式,开始时为解释执行,启动速度较快,对热点代码进行解释和编译
-Xint 使用解释模式,启动很快,执行稍慢
-Xcomp 使用纯编译模式,执行很快,启动很慢
我们通过java -version,能够看到最后显示mixed mode,即此Java为混合模式。可以通过上述VM参数改变为解释模式或者纯编译模式。

在这里插入图片描述

参考文章:
[1],深入理解Java虚拟机,周志明,第三版
[2],谈谈对Java中符号引用和引用的理解
[3],03_class_loading_linking_initializing.pdf
[4],【JVM】简述类加载器及双亲委派机制
[5],Chapter 6. The Java Virtual Machine Instruction Set
[6],jvm:优化-栈上分配

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

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

相关文章

如何快速找出文件夹里的全部带有中文纯中文的文件

首先&#xff0c;需要用到的这个工具YTool&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 步骤 1、打开工具&#xff0c;切换到批量复制文件 2、鼠标移到右侧&#xff0c;点击搜索添加 3、设定查找范围、指定为文件、勾选 包含全部子文件夹&…

FP16与BF16区别

二者都是占用16bit空间。 FP16由1个符号位、5个指数位和10个尾数位组成。FP16在表达小数时具有较高的精度&#xff0c;但表示的最大范围相对BF16比较小。相比BF16&#xff0c;在表达较大的数时更容易出现上溢的情况。BF16由1个符号位、8个指数位和7个尾数位组成。相比于FP16&a…

RFC 791 (1)-导论

目录 浅论 IP是啥 IP可以管啥 操作 范例查看 提示&#xff1a;本系列将会开始RFC文档阅读&#xff0c;这里会给出我的一些笔记 浅论 我们这篇RFC文档描述的是IP和ICMP协议&#xff0c;我们都知道&#xff0c;在传统的OSI七层或者是现在被简化的五层&#xff1a;应用层&…

2024年Q1季度果酒行业线上市场数据分析:女性消费力量强劲!

随着短视频推广和健康饮酒理念的盛行&#xff0c;果酒凭借酒精度数低、口味丰富、富含多种营养成分等优势逐渐受到了消费者的青睐。 Q1季度&#xff0c;消费者对果酒需求依旧旺盛。根据鲸参谋数据显示&#xff0c;今年Q1季度&#xff0c;线上电商平台&#xff08;某猫&#xf…

6S管理,真的有必要吗?

工厂里的物料不知道是什么时间堆放的&#xff0c;不知道这个是谁的&#xff0c;不知道还有没有用&#xff0c;不知道该不该处理掉&#xff0c;越积越多&#xff0c;想要的东西总是找不着&#xff0c;不要的东西总是“碍手碍脚”……可怕的是大家对这一些现象习以为常。 说起6S…

搬运5款小众,无广告,实用性拉满的软件

​ 你是否喜欢一些小众且无广告的软件&#xff1f;如果是的话&#xff0c;我这边有一些给你推荐的。 1.屏幕录制——OBS Studio ​ OBS Studio是一款广泛使用的实时流媒体和屏幕录制软件&#xff0c;适用于Windows、MacOS、Linux平台。它采用C、C和Qt编写&#xff0c;提供高质…

Keepalived实现LVS高可用

6.1 KeepalivedLVS集群介绍 Keepalived和LVS共同构建了一个高效的负载均衡和高可用性解决方案&#xff1a;LVS作为负载均衡器&#xff0c;负责在集群中的多个服务器间分配流量&#xff0c;以其高性能和可扩展性确保应用程序能够处理大量的并发请求&#xff1b;而Keepalived则作…

如何使用DEEPL免费翻译PDF

如何使用DEEPL免费翻译PDF 安装DEEPL取消PDF限制 安装DEEPL 安装教程比较多&#xff0c;这里不重复。 把英文pdf拖进去&#xff0c;点翻译&#xff0c;在下面的框中有已经翻译完毕的文档。 但是存在两个问题 问题1&#xff1a;这些文档是加密的。 问题2&#xff1a;带有DeepL标…

C#知识|上位机UI设计-详情窗体设计思路及流程(实例)

哈喽,你好啊,我是雷工! 上两节练习记录了登录窗体和主窗体的实现过程,本节继续练习内容窗体的实现,以下为练习笔记。 01 详情窗体效果展示: 02 添加窗体并设置属性 在之前练习项目的基础上添加一个Windows窗体,设置名称为:FrmIPManage.cs 设置窗体的边框和标题栏的外…

flink sql 优化

文章目录 一、参数方面二、资源方面三、总结 提示&#xff1a;实时flink sql 参考很多网上方法与自己实践方法汇总(版本:flink1.13) 一、参数方面 flink sql参数配置 //关闭详细算子链(默认为true),true后job性能会略微有提升。false则可以展示更详细的DAG图方便地位性能结点…

go mod

常用命令 初始化模块 go mod init 模块名下载 go.mod 文件中指明的所有依赖 go mod download github.com/gin-gonic/ginv1.9.(依赖路径)依赖对其&#xff08;使引用的都是所依赖的&#xff09; go mod tidy编辑go.mod go mod edit go mod edit -require"github.com/g…

jvm 马士兵 01 JVM简介,class文件结构

01.JVM是什么 JVM是一个跨平台的标准 JVM只识别class文件&#xff0c;符合JVM规范的class文件都可以被识别 u1 是一个字节 u2是两个字节

5款采用AMD Instinct MI300芯片的超酷AI和HPC服务器

我们收集了戴尔科技、联想、超微和技嘉的五款超酷人工智能和高性能计算服务器&#xff0c;这些服务器使用 AMD 的 Instinct MI300 芯片&#xff0c;该芯片于几个月前推出&#xff0c;旨在挑战 Nvidia 在人工智能计算领域的主导地位。 AMD 正在凭借其 Instinct MI300 加速器芯片…

新手必看!场外个股期权的权利金估算公式

场外个股期权的权利金估算公式 场外个股期权的权利金估算公式通常涉及多个因素&#xff0c;这些因素共同决定了权利金的具体数额。虽然具体的估算公式可能因不同的交易平台、交易规则和标的资产而有所差异&#xff0c;但一般来说&#xff0c;权利金的计算会考虑以下几个关键要…

毕业单纯的钻研嵌入式知识有前景吗?

毕业之后单纯地去钻研嵌入式知识到底有没有前景呢&#xff1f;不可否认的是&#xff0c;嵌入式领域有着较高的薪资待遇&#xff0c;并且还存在着巨大的上升空间。然而&#xff0c;要学习嵌入式开发并非易事&#xff0c;其中存在着诸多挑战。其中一个挑战就是需要深入理解计算机…

前端奇怪面试题总结

面试题总结 不修改下面的代码进行正常结构 这道题考的是迭代器和生成器的概念 let [a,b] {a:1,b:2}答案 对象缺少迭代器&#xff0c;需要手动加上 Object.prototype[Symbol.iterator] function* (){// return Object.values(this)[Symbol.iterator]()return yeild* Object.v…

SpringBootWeb创建

创建spring项目 创建SpringBoot工程定义请求处理类运行常见问题java: 无效的源发行版: XXjava: 无法访问org.springframework.web.bind.annotation.RequestMapping类文件具有错误的版本 61.0, 应为 52.0 创建SpringBoot工程 定义请求处理类 RestController public class HelloC…

毕业设计uniapp+vue有机农产品商城系统 销售统计图 微信小程序

本人在网上找了一下这方面的数据发现农村中的信心普及率很是低农民们都不是怎么会用手机顶多就是打打电话发发短信&#xff0c;平时不太会上网更不会想到通过网络手段去卖出自己的劳作成果—农产品&#xff0c;这无疑大大浪费了农民的劳动成果和国家资源也大大打击了人们的生产…

Centos7配置NFS

环境描述 服务器IP为192.168.200.132客户机IP为192.168.200.143 服务器配置 首先安装软件包 [rootlocalhost ~]# yum install nfs-utils rpcbind //我这里已经安装完毕了建立共享目录 [rootlocalhost ~]# mkdir -p /data/share更改文件夹权限 chmod 777 /data/share编辑配置…

vsftp虚拟用户和ssl加密配置 —— 筑梦之路

为什么要用虚拟用户&#xff1f; 1.增强安全性&#xff1a;使用虚拟用户&#xff0c;可以避免直接使用系统账户进行 FTP 访问,通过使用虚拟用户&#xff0c;可以限制 FTP 用户的访问范围和权限&#xff0c;减少潜在的安全风险。 2.隔离用户和文件&#xff1a;虚拟用户可以被隔…