Java 对象的内存布局

目录

一. 前言

二. Java 对象的内存布局

三. Java 对象结构

3.1. 对象头

3.1.1. Mark Word

3.1.2. 类型指针(Class Metadata Pointer)

3.1.3. 数组长度(Length)

3.2. 实例数据

3.3. 对齐填充(Padding)

四. JVM 之 指针压缩

4.1. 压缩指针的由来

4.2. 如何压缩指针

4.3. 如何进一步扩大寻址空间

4.4. JVM 压缩指针参数

五. Java 对象大小计算

5.1. 对象大小计算公式

5.2. 对象分析

5.3. 非数组对象,开启指针压缩

5.4. 非数组对象,关闭指针压缩

5.5. 数组对象开启指针压缩

5.6. 数组对象关闭指针压缩

六. 总结


一. 前言

    作为一名 Java 程序员,我们在日常工作中使用这门面向对象的编程语言时,做的最频繁的操作大概就是去创建一个个的对象了。对象的创建方式有很多,可以通过 new、Spring 管理 Bean、反射、clone、反序列化等不同方式来创建,但最终使用时对象都要被放到内存中,那么你知道在内存中的 Java 对象是由哪些部分组成以及是怎么存储的吗?这篇文章可带你深入了解 Java 对象的内存布局。

二. Java 对象的内存布局

    Java 对象的内存布局分为两种,普通对象和数组对象。

通过图中可以看出,数组对象只是在对象头里多了数组长度这一项,普通对象(非数组对象)没有这一项,也不分配内存空间。

三. Java 对象结构

在JVM中,对象在内存中的布局分为三块区域:对象头实例数据对齐补全

3.1. 对象头

对象头由三部分组成:

  1. Mark Word:存储自身的运行时数据,例如 HashCode、GC 年龄、锁相关信息等内容。
  2. Class Metadata Pointer:类型指针指向它的类元数据的指针。
  3. Length:记录数组长度。只有对象是数组的情况下,才有这部分数据,若对象不是数组,则没有这部分,不分配空间。

3.1.1. Mark Word

    用于存储对象自身的运行时数据,如哈希码、GC 分代年龄、锁状态标志、线程持久的锁、偏向线程的 ID 等,通过存储的内容得知对象头是锁机制和 GC 的重要基础。

32位和64位的Mark Word

32位虚拟机的 Mark Word 的字节分配:

  1. 无锁 —— 对象的 hashcode:25bit;存放对象分代年龄:4bit; 存放是否偏向锁的标志位:1bit; 存放锁标志位为01:2bit。
  2. 偏向锁 —— 在偏向锁中划分更细。开辟 25bit 的空间,其中存放线程 ID:23bit;存放Epoch:2bit;存放对象分代年龄:4bit;存放是否偏向锁标志:1bit (0表示无锁,1表示偏向锁);锁的标志位为01:2bit。
  3. 轻量级锁 —— 在轻量级锁中直接开辟 30bit 的空间存放指向栈中锁记录的指针,2bit 存放锁的标志位,其标志位为00。
  4. 重量级锁 —— 在重量级锁中和轻量级锁一样,30bit 的空间用来存放指向重量级锁的指针,2bit 存放锁的标志位,其标志位为10。
  5. GC 标记 —— 开辟 30bit 的内存空间却没有占用,2bit 空间存放锁标志位为11。其中无锁和偏向锁的锁标志位都是01,只是在前面的 1bit 区分了这是无锁状态还是偏向锁状态。

64位虚拟机的 Mark Word 的字节分配:

  1. 无锁 —— unused:25bit;对象的 hashcode:31bit;Cmc_free:1bit;存放对象分代年龄:4bit; 存放是否偏向锁的标志位:1bit; 存放锁标志位为01:2bit。
  2. 偏向锁 —— 偏向线程 ID:54bit;存放Epoch:2bit;Cmc_free:1bit;存放对象分代年龄:4bit;存放是否偏向锁标识:1bit (0表示无锁,1表示偏向锁);锁的标志位为01:2bit。
  3. 轻量级锁 —— 在轻量级锁中直接开辟 62bit 的空间存放指向栈中锁记录的指针,2bit 存放锁的标志位,其标志位为00。
  4. 重量级锁 —— 在重量级锁中和轻量级锁一样,62bit 的空间用来存放指向重量级锁的指针,2bit 存放锁的标识位,为10。
  5. GC 标记 —— 开辟 62bit 的内存空间却没有占用,2bit 空间存放锁标志位为11。其中无锁和偏向锁的锁标志位都是01,只是在前面的 1bit 区分了这是无锁状态还是偏向锁状态。

3.1.2. 类型指针(Class Metadata Pointer)

    类型指针指向类的元数据地址,JVM 通过这个指针确定对象是哪个类的实例。32位的 JVM 占32位,4个字节,64位的 JVM 占64位,8个字节,但是64位的 JVM 默认会开启指针压缩,压缩后也只占4字节。

    如果应用的对象过多,使用64位的指针将浪费大量内存,统计而言,64位的 JVM 将会比32位的JVM 多耗费50%的内存。所以会默认开启指针压缩(如不开启,类型指针将占用8字节),UseCompressedOops 是默认开启的,该参数表示开启指针压缩,会将原来64位的指针压缩为32位(即由原8字节压缩到4字节大小)。

-XX:+UseCompressedClassPointers //开启压缩类指针
-XX:-UseCompressedClassPointers //关闭压缩类指针// 这个JVM参数依赖UseCompressedOops这个参数,UseCompressedOops开启,UseCompressedClassPointers默认开启,可手工关闭,
// UseCompressedOops关闭,UseCompressedClassPointers不管开启还是关闭都不生效即不压缩。

3.1.3. 数组长度(Length)

    如果对象是普通对象(非数组对象),则没有这部分,不占用空间。如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度,这部分数据的长度也随着 JVM 架构的不同而不同:32位的JVM上,长度为32位(4字节);64位 JVM 则为64位(8字节)。64位 JVM 如果开启+UseCompressedOops 选项,该区域长度也将由64位压缩至32位。

3.2. 实例数据

普通对象:实例数据。

数组对象:数组中的实例数据。

实例数据存放的是非静态的属性,也包括父类的所有非静态属性(private 修饰的也在这里,不区分可见性修饰符),基本类型的属性存放的是具体的值,引用类型及数组类型存放的是引用指针。

实例数据不同的类型所占的空间不同:

数据类型占用空间
char2个字节
boolean1个字节
byte1个字节
short2个字节
int4个字节
long8个字节
float4个字节
double8个字节
对象引用

对象指针压缩默认是被开启的,占用4个字节,配置 JVM 参数

-XX:+UseCompressedOops 后,占用8个字节

64 位的 JVM 默认开启普通对象指针压缩 -XX:+UseCompressedOops (OOP 即 ordinary object pointer),由原8字节压缩到4字节大小。

3.3. 对齐填充(Padding)

    用于补齐对象内存长度的。因为 JVM 要求 Java 代码的对象必须是 8bit 的倍数。如果一个对象用不到 8N 个字节则需要对其填充,以此来补齐对象头和实例数据占用内存之后剩余的空间大小。如果对象头和实例数据已经占满了 JVM 所分配的内存空间,那么就不用再进行对齐填充了。所有的对象分配的字节总 SIZE 需要是8的倍数,如果前面的对象头和实例数据占用的总 SIZE 不满足要求,则通过对齐数据来填满。

四. JVM 之 指针压缩

4.1. 压缩指针的由来

    计算机操作系统分32位和64位,这里的位在计算机里是用0和1来表示的,用32个(或64个)二进制0和1的组合来表示内存地址。

    以32位为例,在普通的内存中,对象的大小最小是以1字节来计算的,通过0和1的排列组合,能够表示寻址的内存空间最大就是2^{32}个,换算成内存空间就是2^{32} / 1024 / 1024 / 1024 = 4G,也就是说32位的操作系统最大能寻址的内存空间只有4G。

    同理,64位的操作系统(查阅资料显示其实没有用到64位,最多只用到了48位,这个可自行查阅资料,反正肯定比32位大的多)2^{48} / 1024 / 1024 / 1024 / 1024 = 256TB,这样内存就足够大了,但是目前还没有厂商能生产出这么大的内存。

    4G 对于现在的 Java 应用系统来说,内存已经算小的了,那我们就会想到使用64位的系统,这样内存就可以更大了,但是当我们准备将32位系统切换到64位系统,起初我们可能会期望系统性能会立马得到提升,但现实情况可能并不是这样的,为什么呢?

  1. 32位系统对象指针是4字节,64位系统对象指针是8字节(1位表示1bit,8个bit 表示1字节),这样64位系统中的对象引用占用的内存空间是32位系统中的两倍大小,因此间接的导致了在64位系统中更多的内存消耗以及更频繁的 GC 发生,GC 占用的 CPU 时间越多,那么我们的应用程序占用 CPU 的时间就越少,响应会变慢,吞吐量会降低。
  2. 对象的引用变大了,那么 CPU 可缓存的对象相对就少了,降低了 CPU 缓存命中率,增加了对内存的访问,CPU 对 CPU 缓存的访问速度可比对内存的访问速度快太多了,所以大量的对内存访问,会降低 CPU 的执行效率,增加了执行时间,从而影响性能。

既然32位系统内存不够,64位内存够但又影响性能,那有没有折中方案来解决这两个问题呢,于是聪明的 JVM 开发者想到了利用压缩指针,在64位的操作系统中利用32位的对象指针引用获得超过 4G 的内存寻址空间。

4.2. 如何压缩指针

    由于在 JVM 里,对象都是以8字节对齐的(即对象的大小都是8的倍数),所以不管用32位还是64位的二进制表示,末尾3位始终都是0。既然 JVM 已经知道了这些对象的内存地址后三位始终是0,那么这些无意义的0就没必要在堆中继续存储。相反,我们可以利用存储0的这3位 bit 存储一些有意义的信息,这样我们就多出3位 bit 的寻址空间,也就是说如果我们继续使用32位来存储指针,只不过后三位原本用来存储0的 bit 现在被我们用来存放有意义的地址空间信息,当寻址的时候,JVM 将这32位的对象引用左移3位即可(后三位补0)。我们原本32位的内存寻址空间一下变成了35位,可寻址的内存空间变为 2^{35} / 1024 / 1024 / 1024 = 32G,也就是说在64位系统 JVM 的内存可扩大到 32G 了,基本上可满足大部分应用的使用了。

    所以在64位系统下,通过压缩指针我们可以继续使用32位来处理(引用指针由8字节可降低到4字节),存储的时候右移3位,寻址的时候左移3位,如下图所示:

    这样一来,JVM 虽然额外的执行了一些位运算但是极大的提高了寻址空间,并且将对象引用占用内存大小降低了一半,节省了大量空间,况且这些位运算对于 CPU 来说是非常容易且轻量的操作,可谓是一举两得。

4.3. 如何进一步扩大寻址空间

    前边提到我们在 Java 虚拟机堆中对象起始地址均需要对齐至8的倍数,不过这个数值我们可以通过 JVM 参数 -XX:ObjectAlignmentInBytes 来改变(默认值为8)。当然这个数值的必须是2的次幂,数值范围需要在 8 - 256 之间。

    正是因为对象地址对齐至8的倍数,才会多出3位 bit 让我们存储额外的地址信息,进而将 4G 的寻址空间提升至 32G。

    同样的道理,如果我们将 ObjectAlignmentInBytes 的数值设置为16呢?

    对象地址均对齐至16的倍数,那么就会多出4位 bit 让我们存储额外的地址信息。寻址空间变为 2^{36} / 1024 / 1024 / 1024 = 64G。

    通过以上规律,我们就能知道,在64位系统中开启压缩指针的情况,寻址范围的计算公式:4G * ObjectAlignmentInBytes = 寻址范围。

    但是并不建议大家贸然这样做,因为增大了 ObjectAlignmentInBytes 虽然能扩大寻址范围,但是这同时也可能增加了对象之间的字节填充,导致压缩指针没有达到原本节省空间的效果。

4.4. JVM 压缩指针参数

可以通过以下命令查看 Java 命令默认的启动参数:

java -XX:+PrintCommandLineFlags -version

通过下面这个命令,可以看到所有JVM参数的默认值:

java -XX:+PrintFlagsFinal -version

关于压缩指针的两个参数:

  • UseCompressedClassPointers:压缩类指针(开启时类指针占4字节,关闭时类指针占8字节);
  • UseCompressedOops:压缩普通对象指针(开启时引用对象指针占4字节,关闭时引用对象指针占8字节)。

Oops 是 Ordinary object pointers 的缩写,这两个参数默认是开启的,即                                        -XX:+UseCompressedClassPointers,-XX:+UseCompressedOops,也可手动设置,如下所示:

-XX:+UseCompressedClassPointers //开启压缩类指针
-XX:-UseCompressedClassPointers //关闭压缩类指针
-XX:+UseCompressedOops  //开启压缩普通对象指针
-XX:-UseCompressedOops  //关闭压缩普通对象指针

注:32位 HotSpot VM 是不支持 UseCompressedOops 参数的,只有64位 HotSpot VM 才支持。Oracle JDK 从6 update 23开始在64位系统上会默认开启压缩指针。

五. Java 对象大小计算

5.1. 对象大小计算公式

以下表格展示了对象中各部分所占空间大小,单位:字节。

类型所属部分占用空间大小(压缩开启)占用空间大小(压缩关闭)
Markwork对象头88
类型指针对象头48
数组长度对象头44
byte对象体11
boolean对象体11
short对象体22
char对象体22
int对象体44
float对象体44
long对象体88
double对象体88
对象引用指针对象体48
对齐填充对齐填充对象头+对象体是8的倍数?0 :8 -(对象头+对象体)% 8对象头+对象体是8的倍数?0 :8 -(对象头+对象体)% 8

计算公式:对象大小 = 对象头 + 对象体(对象是数组时,对象体的大小=引用指针占用空间大小 *对象个数) + 对齐填充。

64位操作系统 32G 内存以下,默认开启对象指针压缩,对象头是12字节,关闭指针压缩,对象头是16字节。内存超过 32G 时,则自动关闭指针压缩,对象头占16字节。

5.2. 对象分析

使用 JOL 工具分析 Java 对象大小:

<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.17</version>
</dependency>

常用类及方法:
查看对象内部信息: ClassLayout.parseInstance(obj).toPrintable();
查看对象外部信息:GraphLayout.parseInstance(obj).toPrintable();
查看对象占用空间总大小:GraphLayout.parseInstance(obj).totalSize();
查看类内部信息:ClassLayout.parseClass(Object.class).toPrintable()。

使用到的测试类:

@Setter
class Goods {private byte b;private char type;private short age;private int no;private float weight;private double price;private long id;private boolean flag;private String goodsName;private LocalDateTime produceTime;private String[] tags;public static String str;public static int temp;
}

5.3. 非数组对象,开启指针压缩

64位 JVM,堆内存小于 32G 的情况下,默认是开启指针压缩的。

public static void main(String[] args) {Goods goods = new Goods();goods.setAge((short) 10);goods.setNo(123456);goods.setId(111L);goods.setGoodsName("方便面");goods.setFlag(true);goods.setB((byte)1);goods.setPrice(1.5d);goods.setProduceTime(LocalDateTime.now());goods.setType('A');goods.setWeight(0.065f);goods.setTags(new String[] {"food", "convenience", "cheap"});Goods.str = "test";Goods.temp = 222;System.out.println(ClassLayout.parseInstance(goods).toPrintable());
}

先不看输出结果,按上面的公式计算一下对象的大小:

  • 对象头:8字节(Mark Word)+4字节(类指针)=12字节;
  • 对象体:1字节(属性 b)+ 2字节(属性 type)+ 2字节(属性 age)+ 4字节(属性 no)+ 4字节(属性 weight)+ 8字节(属性 price)+ 8字节(属性 id)+ 1字节(属性 flag) + 4字节(属性 goodsName 指针) + 4字节(属性 produceTime 指针) + 4字节(属性 tags 指针)= 42字节(注意:静态属性不参与对象大小计算);
  • 对齐填充:8 -(对象头+对象体)% 8 = 8 - (12 + 42) % 8 = 2字节;
  • 对象大小 = 对象头 + 对象体 + 对齐填充 = 12字节 + 42字节 + 2字节 = 56字节。

执行看运行结果:

com.star95.study.jvm.Goods object internals:
OFF  SZ                      TYPE DESCRIPTION               VALUE0   8                           (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4                           (object header: class)    0x2000c04312   4                       int Goods.no                  12345616   8                    double Goods.price               1.524   8                      long Goods.id                  11132   4                     float Goods.weight              0.06536   2                      char Goods.type                A38   2                     short Goods.age                 1040   1                      byte Goods.b                   141   1                   boolean Goods.flag                true42   2                           (alignment/padding gap)   44   4          java.lang.String Goods.goodsName           (object)48   4   java.time.LocalDateTime Goods.produceTime         (object)52   4        java.lang.String[] Goods.tags                [(object), (object), (object)]
Instance size: 56 bytes
Space losses: 2 bytes internal + 0 bytes external = 2 bytes total

这里有一个特殊的地方,打印输出的属性顺序跟代码里的顺序不一致,这是因为 JVM 进行优化,也就是指令重排序,会根据属性类型的大小、执行的先后顺序对结果是否有影响、最小填充大小等因素计算出对象最小应占用的空间。

5.4. 非数组对象,关闭指针压缩

关闭压缩指针,类指针和引用对象指针都占8字节,推算一下对象大小:

  • 对象头:8字节(Mark Word)+ 8字节(类指针)= 16字节;
  • 对象体:1字节(属性 b)+ 2字节(属性 type)+ 2字节(属性 age)+ 4字节(属性 no)+ 4字节(属性 weight)+ 8字节(属性 price)+ 8字节(属性 id)+ 1字节(属性 flag) + 8字节(属性 goodsName 指针) + 8字节(属性 produceTime 指针) + 8字节(属性 tags 指针)= 54字节(注意:静态属性不参与对象大小计算);
  • 对齐填充:8 -(对象头+对象体)% 8 = 8 - (16 + 54) % 8 = 2字节;
  • 对象大小 = 对象头 + 对象体 + 对齐填充 = 16字节 + 54字节 + 2字节 = 72字节。

运行时增加JVM参数如下:

-XX:-UseCompressedClassPointers -XX:-UseCompressedOops

public class ObjectLayOut1 {public static void main(String[] args) {Goods goods = new Goods();goods.setAge((short) 10);goods.setNo(123456);goods.setId(111L);goods.setGoodsName("方便面");goods.setFlag(true);goods.setB((byte)1);goods.setPrice(1.5d);goods.setProduceTime(LocalDateTime.now());goods.setType('A');goods.setWeight(0.065f);goods.setTags(new String[] {"food", "convenience", "cheap"});Goods.str = "test";Goods.temp = 222;System.out.println(ClassLayout.parseInstance(goods).toPrintable());}
}

执行看运行结果:

com.star95.study.jvm.Goods object internals:
OFF  SZ                      TYPE DESCRIPTION               VALUE0   8                           (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   8                           (object header: class)    0x00000000175647b816   8                    double Goods.price               1.524   8                      long Goods.id                  11132   4                       int Goods.no                  12345636   4                     float Goods.weight              0.06540   2                      char Goods.type                A42   2                     short Goods.age                 1044   1                      byte Goods.b                   145   1                   boolean Goods.flag                true46   2                           (alignment/padding gap)   48   8          java.lang.String Goods.goodsName           (object)56   8   java.time.LocalDateTime Goods.produceTime         (object)64   8        java.lang.String[] Goods.tags                [(object), (object), (object)]
Instance size: 72 bytes
Space losses: 2 bytes internal + 0 bytes external = 2 bytes total

5.5. 数组对象开启指针压缩

默认是开启压缩指针的,类指针和引用对象指针都占4字节,推算一下对象大小:

  • 对象头:8字节(Mark Word)+ 4字节(类指针) + 4字节(数组长度)= 16字节;
  • 对象体:4字节 * 3 = 12字节;
  • 对齐填充:8 -(对象头+对象体)% 8 = 8 - (16字节 + 12字节)% 8 = 4字节;
  • 对象大小 = 对象头 + 对象体 + 对齐填充 = 16字节 + 12字节 + 4字节 = 32字节。
public class ObjectLayOut1 {public static void main(String[] args) {Goods goods = new Goods();goods.setAge((short) 10);goods.setNo(123456);goods.setId(111L);goods.setGoodsName("方便面");goods.setFlag(true);goods.setB((byte)1);goods.setPrice(1.5d);goods.setProduceTime(LocalDateTime.now());goods.setType('A');goods.setWeight(0.065f);goods.setTags(new String[] {"food", "convenience", "cheap"});Goods.str = "test";Goods.temp = 222;Goods[] goodsArr = new Goods[3];goodsArr[0] = goods;System.out.println(ClassLayout.parseInstance(goodsArr).toPrintable());}
}

 执行看运行结果:

[Lcom.star95.study.jvm.Goods; object internals:
OFF  SZ                         TYPE DESCRIPTION               VALUE0   8                              (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   4                              (object header: class)    0x2000c18d12   4                              (array length)            316  12   com.star95.study.jvm.Goods Goods;.<elements>         N/A28   4                              (object alignment gap)    
Instance size: 32 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

5.6. 数组对象关闭指针压缩

关闭压缩指针,类指针和引用对象指针都占8字节,推算一下对象大小:

  • 对象头:8字节(Markword)+8字节(类指针) + 4字节(数组长度)= 20字节;
  • 对象体:8字节 * 3 = 24字节;
  • 对齐填充:8 -(对象头+对象体)% 8 = 8 - (20+ 24) % 8 = 4字节;
  • 对象大小 = 对象头 + 对象体 + 对齐填充 = 20字节 + 24字节 + 4字节 = 48字节。

运行时增加 JVM 参数如下:

-XX:-UseCompressedClassPointers -XX:-UseCompressedOops
public class ObjectLayOut1 {public static void main(String[] args) {Goods goods = new Goods();goods.setAge((short) 10);goods.setNo(123456);goods.setId(111L);goods.setGoodsName("方便面");goods.setFlag(true);goods.setB((byte)1);goods.setPrice(1.5d);goods.setProduceTime(LocalDateTime.now());goods.setType('A');goods.setWeight(0.065f);goods.setTags(new String[] {"food", "convenience", "cheap"});Goods.str = "test";Goods.temp = 222;Goods[] goodsArr = new Goods[3];goodsArr[0] = goods;System.out.println(ClassLayout.parseInstance(goodsArr).toPrintable());}
}

执行看运行结果:

[Lcom.star95.study.jvm.Goods; object internals:
OFF  SZ                         TYPE DESCRIPTION               VALUE0   8                              (object header: mark)     0x0000000000000001 (non-biasable; age: 0)8   8                              (object header: class)    0x0000000017e04d7016   4                              (array length)            320   4                              (alignment/padding gap)   24  24   com.star95.study.jvm.Goods Goods;.<elements>         N/A
Instance size: 48 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

通过以上对象分析,我们看到在开启压缩指针的情况下,对象的大小会小很多,节省了内存空间。

六. 总结

    通过以上的分析,基本已经把 Java 对象的结构讲清楚了,另外对象占用内存空间大小也计算出来了,有助于进行 JVM 调优分析,64位的虚拟机内存在 32G 以下时默认是开启压缩指针的,超过32G 自动关闭压缩指针,主要目的都是为了提高寻址效率。

    另外,本文是通过 JOL 工具计算对象占用空间的大小,不包括引用对象实际占用的内存大小,因为计算时是按引用对象的指针占用空间大小计算的,可能跟其他工具计算的结果不一样,具体跟工具的计算逻辑有关,比如跟 JDK 自带的 jvisualvm 工具通过堆 dump 出来看到的对象大小不一样,感兴趣的可自行验证。

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

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

相关文章

【Github-Action】GithubAction 环境下,如何将临时生成的文件推送至指定分支。

通过这篇文章你可以掌握如何将github action 环境下临时生成的文件推送至指定分支&#xff0c;并且可以打开利用github开放的api做各种强大或有趣的事情的视野和思路。 如果你对github-action感兴趣&#xff0c;还可以看这篇文章&#xff0c; 这篇文章教会你如何开发Github Act…

牛刀小试---二分查找(C语言)

题目&#xff1a;在给定的升序数组中查找指定的数字n&#xff0c;并输出其下标 代码举例&#xff1a; #include <stdio.h> int main() {int arr[] { 1,2,3,4,5,6,7,8,9,10 };//给定的升序数组int left 0;//定义左下标int right sizeof(arr) / sizeof(arr[0]) - 1;//…

Hive基础知识(七):Hive 数据类型全解

1. 基本数据类型 对于 Hive 的 String 类型相当于数据库的 varchar 类型&#xff0c;该类型是一个可变的字符串&#xff0c;不过它不能声明其中最多能存储多少个字符&#xff0c;理论上它可以存储2GB 的字符数。 2. 集合数据类型 Hive 有三种复杂数据类型 ARRAY、MAP 和 STRUCT…

08、Kafka ------ 消息存储相关的配置-->消息过期时间设置、查看主题下的消息存活时间等配置

目录 消息存储相关的配置★ 消息的存储介绍★ 消息过期时间及处理方式演示&#xff1a;log.cleanup.policy 属性配置 ★ 修改指定主题的消息保存时间演示&#xff1a;将 test2 主题下的消息的保存时间设为10个小时1、先查看test2主题下的配置2、然后设置消息的保存时间3、然后再…

Element Plus 离线手册 下载

Element Plus (Vue3) 离线手册&#xff0c;解压就能用&#xff0c;双击运行&#xff0c;浏览器访问 http://localhost:7011 获取方式&#xff1a;原文关注微信公众号&#xff0c;回复消息&#xff1a;7011ELP Element Plus 离线手册 下载Vue3 Element Plus 离线手册 离线文档 …

vue3 响应式api中特殊的api

系列文章目录 TypeScript 从入门到进阶专栏 文章目录 系列文章目录一、shallowRef()二、triggerRef()三、customRef()四、shallowReactive()五、shallowReadonly()六、toRaw()七、markRaw()八、effectScope()九、getCurrentScope() 一、shallowRef() shallowRef()是一个新的响…

景联文科技:以高质量数据赋能文生图大模型

1月5日&#xff0c;在智求共赢・中国AIGC产业应用峰会暨无界AI生态合作伙伴大会上&#xff0c;中国AIGC产业联盟联合无界AI发布了《中国AIGC文生图产业白皮书2023》&#xff0c;从AIGC文生图发展历程、主流工具、产业实践以及规模预测等多个维度&#xff0c;全面揭示了中国AIGC…

【elastic search】JAVA操作elastic search

目录 1.环境准备 2.ES JAVA API 3.Spring Boot操作ES 1.环境准备 本文是作者ES系列的第三篇文章&#xff0c;关于ES的核心概念移步&#xff1a; https://bugman.blog.csdn.net/article/details/135342256?spm1001.2014.3001.5502 关于ES的下载安装教程以及基本使用&…

节省时间:AI 模型靠谱下载方案汇总

这篇文章&#xff0c;想分享下我日常是如何下载模型的&#xff0c;包括下载来源和工具使用细节&#xff0c;希望对折腾模型的你也有帮助。 也希望开源和 AI 领域的研究者、从业者在做技术调研和落地实践的时候&#xff0c;都能节约一些时间。 写在前面 之前写了很多模型相关…

微信小程序:发送小程序订阅消息

文档&#xff1a;小程序订阅消息&#xff08;用户通过弹窗订阅&#xff09;开发指南 目录 步骤一&#xff1a;获取模板 ID步骤二&#xff1a;小程序端获取参数2.1、获取消息下发权限2.2、获取登录凭证&#xff08;code&#xff09; 步骤三&#xff1a;后端调用接口下发订阅消息…

【Web】CTFSHOW PHP命令执行刷题记录(全)

目录 web29 web30 web31 web32 web33 web34 web35 web36 web37-39 web40 web41 &#xff08;y4✌脚本&#xff09; web42 -44 web45 web46 -49 web50 web51 web52 web53 web54 web55-56 web57 web58 web59 web60 web61 web62 web63-65 web66-67 w…

QT 信号与槽

.h文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);~Widget();public slots:void buttonLable();priv…

rke2 Online Deploy Rancher v2.8.0 latest (helm 在线部署 rancher v2.8.0)

文章目录 1. 简介2. 预备条件3. 安装 helm4. 安装 cert-manager4.1 yaml 安装4.2 helm 安装 5. 安装 rancher6. 验证7. 界面预览 1. 简介 Rancher 是一个 Kubernetes 管理工具&#xff0c;让你能在任何地方和任何提供商上部署和运行集群。 Rancher 可以创建来自 Kubernetes 托…

文档扫描与矫正-仿射变换

图像变换是计算机视觉和图像处理中的关键技术之一&#xff0c;它允许我们对图像进行各种形式的变形、调整和校正。其中&#xff0c;仿射变换是一种常见的变换方式。 在文档扫描过程中&#xff0c;由于拍摄角度和畸变等原因&#xff0c;文档图像可能存在一定程度的形变。仿射变…

【LeetCode】组合两个表(mysql)

题目 编写解决方案&#xff0c;报告 Person 表中每个人的姓、名、城市和州。如果 personId 的地址不在 Address 表中&#xff0c;则报告为 null 。 以 任意顺序 返回结果表。 结果格式如下所示。 答 select firstName ,lastName,city,state from Person left join Address …

软路由之爱快基于L2TP 实现

申明&#xff1a;本文仅针对国内SSTP&#xff0c;适用于国内的游戏加速&#xff0c;禁止一切利用该技术的翻墙行为。 相信很多接触过爱快的人都需要连接L2TP或PPTP&#xff0c;本文主要介绍通过爱快iKuai的L2TP 来实现异地组网。 一、准备工作 1、爱快 v3.x 2个&#xff08;免…

SpringMVC源码解析——HTTP请求处理

在SpringMVC源码解析——DispatcherServlet的逻辑处理中&#xff0c;最后介绍到了org.springframework.web.servlet.DispatcherServlet的doDispatch方法中关于处理Web HTTP请求的核心代码是调用AbstractHandlerMethodAdapter类的handle方法&#xff0c;源码如下&#xff1a; /*…

作业:通过两台linux主机配置ssh实现互相免密登陆

做题步骤&#xff1a; 一.开启两个Linux主机&#xff0c;并且用ssh连接&#xff0c;要能够ping通 我这里是server&#xff1a;192.168.81.129 client&#xff1a;192.168.81.130 举例 步骤&#xff1a; 1.安装服务软件 2.运行软件程序 3.根据自定配置提供对应的服务/etc/chr…

期末查分系统(c,链表实现)

主要功能&#xff1a; 分为三个身份: 学生:可以通过学号查询个人分数 老师&#xff1a;可以看所有学生成绩&#xff0c;单科排名&#xff08;正序&#xff0c;倒序&#xff09;&#xff0c;统计绩点&#xff0c;查看绩点排名前百分之n的学生 管理员端&#xff1a;可以创建链…

用React给XXL-JOB开发一个新皮肤(二):目录规划和路由初始化

目录 一. 简述二. 目录规划三. Vite 配置 3.1. 配置路径别名3.2. 配置 less 四. 页面 4.1. 入口文件4.2. 骨架文件4.3. 普通页面 五. 路由配置六. 预览启动 一. 简述 上一篇文章我们介绍了项目初始化&#xff0c;此篇文章我们会先介绍下当前项目的目录规划&#xff0c;接着对…