Java 对象的内存布局

news2025/1/11 22:52:57

目录

一. 前言

二. 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               VALUE
  0   8                           (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                           (object header: class)    0x2000c043
 12   4                       int Goods.no                  123456
 16   8                    double Goods.price               1.5
 24   8                      long Goods.id                  111
 32   4                     float Goods.weight              0.065
 36   2                      char Goods.type                A
 38   2                     short Goods.age                 10
 40   1                      byte Goods.b                   1
 41   1                   boolean Goods.flag                true
 42   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               VALUE
  0   8                           (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8                           (object header: class)    0x00000000175647b8
 16   8                    double Goods.price               1.5
 24   8                      long Goods.id                  111
 32   4                       int Goods.no                  123456
 36   4                     float Goods.weight              0.065
 40   2                      char Goods.type                A
 42   2                     short Goods.age                 10
 44   1                      byte Goods.b                   1
 45   1                   boolean Goods.flag                true
 46   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               VALUE
  0   8                              (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   4                              (object header: class)    0x2000c18d
 12   4                              (array length)            3
 16  12   com.star95.study.jvm.Goods Goods;.<elements>         N/A
 28   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               VALUE
  0   8                              (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
  8   8                              (object header: class)    0x0000000017e04d70
 16   4                              (array length)            3
 20   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.coloradmin.cn/o/1373300.html

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

相关文章

Python-12-正则

当然内容不是很全&#xff0c;可以参考: 正则表达式学习资料 https://blog.csdn.net/weixin_40907382/article/details/79654372

解决录制的 mp4 视频文件在 windows 无法播放的问题

解决录制的 mp4 视频文件在 windows 无法播放的问题 kazam 默认录制保存下来的 mp4 视频文件在 windows 中是无法直接使用的&#xff0c;这是由于视频编码方式的问题。解决办法&#xff1a; 首先安装 ffmeg 编码工具&#xff1a; sudo apt-get install ffmpeg 然后改变视频的…

kivy,一个超级厉害的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超级厉害的 Python 库 - kivy。 Github地址&#xff1a;https://github.com/kivy/kivy Python是一种广泛使用的编程语言&#xff0c;而Kivy是一个用于创建跨平台移动应用和多点触控应用的开源…

【局域网window10系统搭建共享文件夹或与手机共享】

局域网window10系统搭建共享文件夹或与手机共享 1、Window 10之间搭建共享文件夹1.1 ping通两台window 10 电脑1.2 创建共享账号&#xff08;window 10专业版&#xff09;1.3 创建共享文件夹以及配置1.4访问共享文件夹 2、手机访问window10 共享文件夹&#xff08;结合步骤一&a…

kubeSphere DevOps自定义容器环境JDK11

kubeSphere DevOps自定义容器环境JDK11 &#x1f342;前言&#x1f342;增加JDK11容器环境&#x1f341;检查是否成功 &#x1f342;不生效的原因排查&#x1f341;按步骤执行如下命令 &#x1f342;前言 kubeSphere 版本v3.1.1 遇到问题:kubeSphere默认支持容器只有JDK8,目前…

【Axure高保真原型】树控制内联框架

今天和大家分享树控制内联框架的原型模板&#xff0c;点击树的箭头可以打开或者收起子节点&#xff0c;点击最后一级人物节点&#xff0c;可以切换右侧内联框到对应的页面&#xff0c;左侧的树是通过中继器制作的&#xff0c;使用简单&#xff0c;只需要按要求填写中继器表格即…

基于JavaWeb+BS架构+SpringBoot+Vue智能菜谱推荐系统的设计和实现

基于JavaWebBS架构SpringBootVue智能菜谱推荐系统的设计和实现 文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 文末获取源码 Lun文目录 目 录 目 录 III 第一章 概述 1 1.1 研究背景 1 1.2研究目的及意义 1 1.3…

Java:爬虫htmlunit实践

之前我们已经讲过使用htmlunit及基础&#xff0c;没有看过的可以参考Java&#xff1a;爬虫htmlunit-CSDN博客 我们今天就来实际操作一下&#xff0c;爬取指定网站的数据 1、首先我们要爬取一个网站数据的时候我们需要对其数据获取方式我们要进行分析&#xff0c;我们今天就拿双…

【数据库系统概论】期末复习2

系列文章 期末复习1 系列文章定义并理解下列术语&#xff0c;说明它们之间的联系与区别试述关系模型的完整性规则。在参照完整性中&#xff0c;什么情况下外码属性的值可以为空值&#xff1f;关系代数 定义并理解下列术语&#xff0c;说明它们之间的联系与区别 &#xff08;1…

基于微信小程序的学校图书管理系统开发与实现

学校图书馆里有大量的各种领域的图书&#xff0c;可供学校各个专业的师生来借阅与做科学和论文研究&#xff0c;但过去图书馆的借阅、归还及图书资料的管理完全依靠图书馆工作人员的手工记录与引导师生找寻借阅的书籍&#xff0c;其耗时费力且低效&#xff0c;开发的学校图书管…

《AI基本原理和python实现》栏目介绍

一、说明 栏目《AI基本原理和python实现》的设计目的是为了实现相关算法的python编程。因为用python实现AI需对相关的python库进行全方位了解&#xff0c;本栏目基本包含了【机器学习】相关的经典算法&#xff0c;除此之外还包括了数据分析、时间序列等一些概念和相关python代码…

09、Kafka ------ 通过修改保存时间来删除消息(retention.ms 配置)

目录 通过修改保存时间来删除消息★ 删除指定主题的消息演示1、修改kafka检查过期消息的时间间隔2、修改主题下消息的过期时间3、查看修改是否生效4、先查看下主题下有没有消息5、添加几条消息看效果6、查看消息是否被删除 ★ 恢复主题的retention.ms配置1、先查看没修改前的te…

String#intern

1.intern方法 intern()方法可以在运行期间向字符串中动态加入字符串实例的方式&#xff0c;它的功能很简单,总结起来就一句话 可以在运行时向字符串池中添加字符串常量 添加的原则是&#xff0c;如果常量池中存在当前字符串&#xff0c;则直接返回常量池中它的引用&#xff1b…

【博士每天一篇论文-实验分析】Toroidal topology of population activity in grid cells

阅读时间&#xff1a;2023-11-18 1 介绍 年份&#xff1a;2022 作者&#xff1a;Richard J. Gardner&#xff0c;挪威科技大学卡维利系统神经科学研究所和神经计算中心&#xff0c;挪威特隆赫姆 期刊&#xff1a; Nature 引用量&#xff1a;194 这篇论文通过揭示格状细胞网络…

Django的数据库模型的CharField字段的max_length参数与中文字符数的关系探索(参数max_length的单位是字符个数还是字节数?)

01-清理干净之前的数据库迁移信息 02-根据setting.py中的信息删除掉之前建立的数据库 03-删除之后重新创建数据库 04-models.py中创建数据库模型 from django.db import modelsclass User(models.Model):username models.CharField(max_length4)email models.EmailField(uni…

训练自己的GPT2

训练自己的GPT2 1.预训练与微调2.准备工作2.在自己的数据上进行微调 1.预训练与微调 所谓的预训练&#xff0c;就是在海量的通用数据上训练大模型。比如&#xff0c;我把全世界所有的网页上的文本内容都整理出来&#xff0c;把全人类所有的书籍、论文都整理出来&#xff0c;然…

C++标准学习--tuple

以下帖子介绍的比较详细&#xff1a; C的 tuple_c tuple-CSDN博客 tuple 是 C11 新标准里的类型&#xff0c;它是一个类似 pair 类型的模板。tuple 是一个固定大小的不同类型值的集合&#xff0c;是泛化的 std::pair。我们也可以把它当做一个通用的结构体来用&#xff0c;不需…

Golang,Gin框架使用ShouldBindJSON时出现EOF报错

前言 做毕设ing&#xff0c;使用的是Gin框架做的一个简单的管理系统。但偶尔也会碰到一些稀奇古怪的Bug&#xff0c;因此记录一下。 问题描述 api是这样写的 func UserRegisterHandler(c *gin.Context, user *usecase.UserOperate) {if err : c.ShouldBindJSON(&UserReg…

满足ITOM需求的网络监控工具

IT 运营管理&#xff08;ITOM&#xff09;可以定义为监督 IT 基础架构的各种物理和虚拟组件的过程;确保其性能、运行状况和可用性;并使它们能够与基础架构的其他组件无缝协作。IT 运营管理&#xff08;ITOM&#xff09;在大型 IT 管理模型中也发挥着积极作用&#xff0c;包括 I…

Arthas CPU 火焰图技术 简谈

1.基础命令 help 作用 查看命令帮助信息&#xff0c;可以查看当前 arthas 版本支持的指令&#xff0c;或者查看具体指令的使用说明。cat 作用 打印文件内容&#xff0c;和linux里的cat命令类似&#xff0c;如果没有写路径&#xff0c;则展示当前目录下的文件 使用参考 cat /t…