网站设计策划书/世界足球排名前100
这篇文章本来只是想讲一下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 Version
、Major 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:优化-栈上分配
相关文章:

【JVM】class文件格式,JVM加载class文件流程,JVM运行时内存区域,对象分配内存流程
这篇文章本来只是想讲一下class文件格式,讲着讲着越讲越多。JVM这一块吧,知识比较散比较多,如果深研究下去如死扣《深入理解Java虚拟机》,这本书很深很细,全记住是不可能的,其实也没必要。趁这个机会直接把…...

GPU系列(六)-NVIDIA GPU 驱动安装
1. 安装驱动 1.1 查看系统是否识别显卡 lspci | grep -i vga03:00.0 VGA compatible controller: NVIDIA Corporation GP102 [TITAN X] (rev a1) 0a:00.0 VGA compatible controller: Matrox Electronics Systems Ltd. G200eR2 (rev 01) 识别出显卡为 NVIDIA 的 TITAN X。 …...

第十五届蓝桥杯总结
因为本人不是计院的,以后可能也不会打算法类的竞赛了,故作此总结,纪念我四个月的算法学习经历,还算是对算法有了一定的基础,碰运气拿下了湖北b组省二,个人感觉比赛题目没有第十四届难,感觉就是纯…...

Linux驱动开发——(八)Linux异步通知
目录 一、异步通知简介 二、信号处理 2.1 驱动程序中的处理 2.1.1 fasync_struct结构体 2.1.2 fasync操作函数 2.1.3 kill_fasync函数 2.2 应用程序中的处理 三、驱动代码 一、异步通知简介 异步通知的核心就是信号。信号类似于硬件上使用的中断,只不过信号…...

Docker知识点汇总表格总结
Docker容器给我的一个很直观的感受就是将项目以及中间件安装变得比较简单直接,运行维护起来也更方便。之前做的一些微服务项目也是用docker来部署,现在很多开源的项目也流行使用docker来部署,简化了很多手动安装和配置的步骤,将项…...

Golang中实现调用Windows API向指定目标发送ARP请求
简介 Go库中很多实现的arp都是支持osx/linux/bsd之类的, 但几乎没有支持windows的, 也试了一些方式, 目前还是选用调用windows的API, 记录一下这一次windows的API的调用经验。 实现 代码 package main/* #cgo CFLAGS: -I. #cgo …...

这是一个简单的照明材料网站,后续还会更新
1、首页效果图 代码 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>爱德照明网站首页</title><style>/*外部样式*/charset "utf-8";*{margin: 0;padding: 0;box-sizing: border-box;}a{text-dec…...

【设计模式】之模板方法模式
系列文章目录 【设计模式】之策略模式 【设计模式】之责任链模式 文章目录 系列文章目录 前言 一、什么是模板方法模式 定义 角色 二、为什么要使用模板方法模式 优点 缺点 三、案例 普通案例 模拟Servlet过程案例 总结 前言 今天给大家介绍23种设计模式中的模板方法模式&a…...

【系统架构师】-选择题(十一)
1、紧耦合多机系统一般通过(共享内存)实现多机间的通信。对称多处理器结构(SMP)属于( 紧耦合)系统。 松耦合多机系统又称间接耦合系统,—般是通过通道或通信线路实现计算机间的互连。 2、采用微内核的OS结构…...

前端开发攻略---介绍HTML中的<dialog>标签,浏览器的原生弹框。
1、演示 2、介绍 <dialog> 标签用于定义对话框,即一个独立的窗口,通常用来显示对话框、提示框、确认框等弹出式内容。在对话框中,可以包含文本、表单元素、按钮等内容,用户可以和这些内容进行交互。 3、兼容性 4、示例代码 …...

让外贸业绩翻倍的销售话术分享
业绩翻三倍的话术,今后无论你遇到挑剔、犹豫、理智的顾客,都能轻松搞定。点赞存起来慢慢看,以免找不到。 与客户有效沟通技巧5的20句金句 业绩翻 3 倍,今后无论你遇到挑剔、犹豫、理智的顾客,都能轻松搞定。点赞存起来…...

观测与预测差值自动变化系统噪声Q的自适应UKF(AUKF_Q)MATLAB编写
简述 基于三维模型的UKF,设计一段时间的输入状态误差较大,此时通过对比预测的状态值与观测值的残差,在相应的情况下自适应扩大系统方差Q,构成自适应无迹卡尔曼滤波(AUKF),与传统的UKF相比&…...

虚拟数据中心
创建数据中心和连接宿主机 DRS:收集群集内所有主机和虚拟机的资源使用情况信息,并根据特定的运行状况给出建议或迁移虚拟机HA:如果一台主机出现故障,则该主机上运行的所有虚拟机都将立即在同一群集的其他主机上重新启动EVC:增强型vMotionVirtual SAN:集中…...

解决Blender导出FBX文件到Unity坐标轴错误的问题
发现Blender的模型导入到Unity里面有问题,简单研究了下发现是坐标系不同,Unity使用的是左手坐标系,Blender使用的是右手坐标系 。 下面直接将如何解决 首先忽略Blender的右手坐标系以及Z轴朝上的事,依照unity坐标系情况修改模型物体的旋转,以Blender猴…...

基于微信小程序的校园二手闲置物品交易平台的设计与实现
基于微信小程序的校园二手闲置物品交易平台的设计与实现 “Design and Implementation of a Campus Second-Hand Marketplace Platform based on WeChat Mini Program” 完整下载链接:基于微信小程序的校园二手闲置物品交易平台的设计与实现 文章目录 基于微信小程序的校园二…...

java中多线程的3种实现方法
1.继承Thread类 优点:代码简单,可以直接使用Thread类里面的方法。 缺点:扩张性较差,应为在java中,一个类只能继承一个父类。 2.实现Runnable接口 3.实现Callable接口 2和3的优缺点是一样的 优点:扩展性强&…...

【Docker】docker compose服务编排
docker compose 简介 Dockerfile模板文件可以定义一个单独的应用容器,如果需要定义多个容器就需要服务编排。 docker swarm(管理跨节点) Dockerfile可以让用户管理一个单独的应用容器;而Compose则允许用户在一个模板(…...

elementui的el-select+el-tree+el-input实现可搜索的下拉树组件
部分实现代码如下 <template> <div><el-selectv-model"item.TableName"placeholder"请选择":disabled"!item.disabled"visible-change"handleVisible"ref"TableName"><el-input placeholder"请输…...

微信公众号排名 SEO的5个策略
随着微信公众号在社交媒体领域的持续发展和普及,如何提升公众号的搜索排名,成为许多运营者关注的焦点。公众号排名SEO,即针对微信公众号进行搜索引擎优化,旨在提高公众号在搜索结果中的曝光率和点击率。下面,我们将深入…...

python烟花代码
在Python中,可以使用多种方式来模拟烟花效果,其中一种常见的方法是使用turtle图形库来绘制。以下是一个简单的示例,展示了如何使用turtle来创建一个烟花效果的动画: import turtle import random# 设置屏幕和背景 screen turtle…...

Python高级编程
描述 集合,列表生成式,生成器,迭代器,切片 Python 中的集合类型是一种无序、不重复的数据容器,用于存储可哈希(hashable)的元素。Python 提供了两种内置的集合类型:set 和 frozens…...

leetCode75. 颜色分类
leetCode75. 颜色分类 题目思路 代码 class Solution { public:void sortColors(vector<int>& nums) {for(int i 0, j 0, k nums.size() - 1; i < k;){if(nums[i] 0) swap(nums[i],nums[j]);else if(nums[i] 2) swap(nums[i],nums[k--]);else if(nums[i] …...

选择器、pxcook软件、盒子模型
结构伪类选择器 定义:根据结构的元素关系来查找元素。 <title>Document</title><style>li:first-child{color:aqua ;}li:last-child{color: aqua;}li:nth-child(3){color: aqua;}</style> </head> <body><ul><li>…...

商城系统秒杀功能设计思想
业务特点 1、瞬时并发量大,秒杀时会有大量用户在同一时间进行抢购,瞬时并发访问量突增几倍、甚至几十倍以上 2、库存量少,一般秒杀活动商品量很少,这就导致了只有极少量用户能成功购买到。 3、业务和流程较为常见,一般…...

#初始化列表
1.再谈构造函数 1.1构造函数的组成 构造函数包括函数体赋值与初始化列表初始化。 1.2函数体赋值 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;} private:int _year;int _month;int _day; };虽然上述构造函数调用之后&…...

Vue-组件中的data
一个组件的data选项必须是一个函数。保证每个组件实例,维护独立的一份数据对象。如下图: 组件一旦封装好了,可以使用多次,比如数字框组件使用了三次: 每次创建新的组件实例,都会重新执行一次data函数&#…...

抖音小店达人佣金应该怎么结算呢?给达人设置多少佣金合适?
大家好,我是电商小V 咱们在运营抖音小店的时候一定会遇到被达人催促结算佣金的情况,咱们给达人合作的时候都会遇到过新手达人,就是给咱们直播带货之后催促咱们赶紧结算商品的佣金, 但是咱们都需要知道一点,那就是小店的…...

水稻病害检测(YOLO数据集,多分类,稻瘟病、纹枯病、褐斑病、枯心病、霜霉病、水稻细菌性条纹斑病、稻苞虫)
是自己利用LabelImg工具进行手工标注,数据集制作不易,请尊重版权(稻瘟病、纹枯病、褐斑病、枯心病、霜霉病、水稻细菌性条纹斑病、稻苞虫) 如果需要yolv8检测模型和数据集放在一起的压缩包,可以关注:最新最…...

MYSQL数据库专业术语及创建数据表详细讲解{sql语句创建数据库语句及条件子句解析,编码格式解析,创建数据表解析,表定义字段解析,主键约束解析}
MYSQL数据库中的专业术语 数据库(Database):存储数据的集合,是数据的逻辑容器。 表(Table):数据库中存储数据的结构,由行(记录)和列(字段&#x…...

Kubernetes的13个常用命令,你都熟悉吗
Kubernetes(K8s)是一个容器编排和管理工具,用于部署、管理和扩展容器化应用程序。下面是一些常用的 Kubernetes 命令及其含义: kubectl create: 创建一个资源对象,如 Pod、Deployment、Service 等。kubectl apply: 应…...