JVM学习-内存结构(二)

一、堆

1.定义

2.堆内存溢出问题

1.演示

-Xmx设置堆大小

3.堆内存的诊断

3.1介绍

1,2都是命令行工具(可直接在ideal运行时,在底下打开终端,输入命令)

1可以拿到Java进程的进程ID,2 jmap只能查询某一个时刻的堆内存

3.2 堆内存jmap演示

public class Demo1_4 {
​public static void main(String[] args) throws InterruptedException {System.out.println("1...");Thread.sleep(30000);byte[] array = new byte[1024 * 1024 * 10]; // 10 MbSystem.out.println("2...");Thread.sleep(20000);array = null;System.gc();System.out.println("3...");Thread.sleep(1000000L);}
}

第一个:

第二个(Eden多了10M):

第三个:垃圾回收后

3.3jconsole演示

运行程序 在终端输入jconsole,弹出界面

选择对应的程序,点击不安全连接

3.4.案例二

现象垃圾回收后,内存仍然占用很高

工具:jvisualvm

代码

/*** 演示查看对象个数 堆转储 dump*/
public class Demo1_13 {
​public static void main(String[] args) throws InterruptedException {List<Student> students = new ArrayList<>();for (int i = 0; i < 200; i++) {students.add(new Student());
//            Student student = new Student();}Thread.sleep(1000000000L);}
}
class Student {private byte[] big = new byte[1024*1024];
}

点击堆Dump,进行查找

二、方法区

1.定义

        Java 虚拟机有一个在所有 Java 虚拟机线程之间共享的方法区域。方法区域类似于用于传统语言的编译代码的存储区域,或者类似于操作系统进程中的“文本”段。它存储每个类的结构,例如运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括特殊方法,用于类和实例初始化以及接口初始化方法区域是在虚拟机启动时创建的。尽管方法区域在逻辑上是堆的一部分,但简单的实现可能不会选择垃圾收集或压缩它。此规范不强制指定方法区的位置或用于管理已编译代码的策略。方法区域可以具有固定的大小,或者可以根据计算的需要进行扩展,并且如果不需要更大的方法区域,则可以收缩。方法区域的内存不需要是连续的!

        存的是跟类相关的信息,包括方法,构造器,成员方法等。方法区在虚拟机启动时被创建是在概念上定义的方法区,逻辑上属于堆的组成部分(厂家实现不同)方法区也会导致内存溢出的错误,抛出OutOfMemoryEror

2.组成

以Hotspot 虚拟机为例,jdk1.6 1.7 1.8 内存结构图,1.8使用的是本地内存不在占用堆内存 。

3.方法区内存溢出

  • 1.8 之前会导致永久代内存溢出

    使用 -XX:MaxPermSize=8m 指定永久代内存大小
  • 1.8 之后会导致元空间内存溢出

    • 使用 -XX:MaxMetaspaceSize=8m 指定元空间大小

  • 设置元空间大小为8m

    public class Demo1_8 extends ClassLoader { // 类加载器 可以用来加载类的二进制字节码public static void main(String[] args) {int j = 0;try {Demo1_8 test = new Demo1_8();for (int i = 0; i < 10000; i++, j++) {// ClassWriter 作用是生成类的二进制字节码ClassWriter cw = new ClassWriter(0);// 版本号, public, 类名, 包名, 父类, 接口cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);// 返回 byte[]byte[] code = cw.toByteArray();// 执行了类的加载test.defineClass("Class" + i, code, 0, code.length); // Class 对象}} finally {System.out.println(j);}}
    }

4.运行时常量池

4.1.二进制字节码文件的构成

主要分为(类的基本信息,常量池,类方法定义,包含的虚拟机指令)

可将程序运行产生的.class文件通过 javap 命令反编译

源程序

package jvm;
​
​
public class HelloWorld {public static void main(String[] args) {System.out.println("hello world");}
}

切换到out输出目录下,使用javap命令

得到反编译后的

 Last modified 2024-12-27; size 541 bytesMD5 checksum 1705415cdaac31d861d20edc1e472d95Compiled from "HelloWorld.java"
public class jvm.HelloWorldminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref          #6.#20         // java/lang/Object."<init>":()V#2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;#3 = String             #23            // hello world#4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V#5 = Class              #26            // jvm/HelloWorld#6 = Class              #27            // java/lang/Object#7 = Utf8               <init>#8 = Utf8               ()V#9 = Utf8               Code#10 = Utf8               LineNumberTable#11 = Utf8               LocalVariableTable#12 = Utf8               this#13 = Utf8               Ljvm/HelloWorld;#14 = Utf8               main#15 = Utf8               ([Ljava/lang/String;)V#16 = Utf8               args#17 = Utf8               [Ljava/lang/String;#18 = Utf8               SourceFile#19 = Utf8               HelloWorld.java#20 = NameAndType        #7:#8          // "<init>":()V#21 = Class              #28            // java/lang/System#22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;#23 = Utf8               hello world#24 = Class              #31            // java/io/PrintStream#25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V#26 = Utf8               jvm/HelloWorld#27 = Utf8               java/lang/Object#28 = Utf8               java/lang/System#29 = Utf8               out#30 = Utf8               Ljava/io/PrintStream;#31 = Utf8               java/io/PrintStream#32 = Utf8               println#33 = Utf8               (Ljava/lang/String;)V
{public jvm.HelloWorld();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 4: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Ljvm/HelloWorld;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #3                  // String hello world5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 6: 0line 7: 8LocalVariableTable:Start  Length  Slot  Name   Signature0       9     0  args   [Ljava/lang/String;
}
SourceFile: "HelloWorld.java"

在代码区域,每条指令都会对应常量池表中一个地址,常量池表中的地址可能对应着一个类名、方法名、参数类型等信息。

ldc #3 在常量池中找一个编号为3的符号

4.2定义

常量池: 就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量信息 运行时常量池: 常量池是 *.class 文件中的,当该类被加载以后,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

4.3 StringTable

StringTable底层是一个哈希表

下面这些题你能作对吗:

答案:
public class Demo1 {
​public static void main(String[] args) {String s1 = "a";String s2 = "b";String s3 = "a" + "b"; // ab 常量池中String s4 = s1 + s2;   // new String("ab") 堆中String s5 = "ab";String s6 = s4.intern();
​
// 问System.out.println(s3 == s4); // falseSystem.out.println(s3 == s5); // trueSystem.out.println(s3 == s6); // true
​String x2 = new String("c") + new String("d"); // new String("cd") 堆中x2.intern();String x1 = "cd";//现在是true     上放一行 x1 != x2(false)
​
// 如果是jdk1.6呢(1.6是将x2拷贝一份入池,1.8是本身入池)System.out.println(x1 == x2);//现在是false     上放一行 x1 != x2(false)}
}
4.3.1StringTable的特性
  1. 常量池中的字符串仅是符号,只有在被用到时才会转化为对象

  2. 利用串池的机制,来避免重复创建字符串对象

  3. 字符串变量拼接的原理是StringBuilder(1.8)

  4. 字符串常量拼接的原理是编译器优化

  5. 可以使用intern方法,主动将串池中还没有的字符串对象放入串池中

    package jvm;
    ​
    public class Demo {
    ​//  ["ab", "a", "b"]public static void main(String[] args) {
    ​String x = "ab";String s = new String("a") + new String("b");
    ​// 堆  new String("a")   new String("b") new String("ab")这个ab是动态拼接的不在串池中,在堆中String s2 = s.intern(); // 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回
    ​System.out.println( s2 == "ab");//trueSystem.out.println( s == "ab");//trueSystem.out.println( s2 == x);//trueSystem.out.println( s == x );//false}
    ​
    }

intern方法 1.8 调用字符串对象的 intern 方法,会将该字符串对象尝试放入到串池中

  1. 如果串池中没有该字符串对象,则放入成功

  2. 如果有该字符串对象,则放入失败 无论放入是否成功,都会返回串池中的字符串对象

  3. 注意:此时如果调用 intern 方法成功,堆内存与串池中的字符串对象是同一个对象;如果失败,则不是同一个对象

4.4 StringTable的位置

jdk1.6 StringTable 位置是在永久代中,1.7,1.8 StringTable 位置是在堆中。

4.5 串池 、常量池 、 运行时常量池 的关系:

  1. 常量池(静态常量池):

    • 常量池通常指的是.class文件中的常量池,它包含了类、方法、字段的符号引用,以及字面量等信息。这些信息在类加载到JVM之前就已经确定,并且存储在.class文件中。

  2. 运行时常量池

    • 当类被加载到JVM时,其常量池的信息会被复制到运行时常量池中。运行时常量池是方法区的一部分,它包含了从.class文件中复制来的常量,以及在运行时动态生成的常量。

    • 运行时常量池相对于.class文件中的常量池具有动态性,可以在运行时添加新的常量。

  3. StringTable(串池)

    • StringTable是运行时常量池的一部分,专门用于存储字符串常量。它通过一个哈希表(数组+链表)来实现,确保存储的字符串常量唯一且不重复。

    • 在JDK 1.7及之前版本中,StringTable位于方法区(Perm Gen),而在JDK 1.8及之后版本中,StringTable被移到了堆中。

    • StringTable中存储的并不是String对象本身,而是指向堆中String对象的引用。

    • StringTable的创建是懒加载的,即只有当字符串常量第一次被使用时,才会在堆中创建String对象,并将其引用放入StringTable中。

        总结来说,StringTable是运行时常量池中专门用于管理字符串常量的部分,它通过优化存储机制来确保相同内容的字符串对象在JVM中只存在一份,从而节省内存。而常量池和运行时常量池则包含了更广泛的信息,包括类、方法、字段的引用和字面量等。StringTable与常量池的关系在于,常量池中的字符串常量在类加载时会被复制到运行时常量池中的StringTable里,而运行时常量池则包含了常量池的所有内容,并支持动态添加新的常

4.6 StringTable 垃圾回收

StringTable底层是一个哈希表

先设置虚拟机参数(便于输出观察):

-Xmx10m 指定堆内存大小 -XX:+PrintStringTableStatistics 打印字符串常量池信息 -XX:+PrintGCDetails -verbose:gc 打印 gc 的次数,耗费时间等信息

public class StringTable {public static void main(String[] args) throws InterruptedException {int i = 0;try {for (int j = 0; j < 100000; j++) { // j=100, j=10000String.valueOf(j).intern();i++;}} catch (Throwable e) {e.printStackTrace();} finally {System.out.println(i);}
​}
}

4.7StringTable的性能调优

因为StringTable是由HashTable实现的,所以可以适当增加HashTable桶的个数,来减少字符串放入串池所需要的时间

使用时加入配置:-Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=1009 (字符串很多时考虑)

-XX:StringTableSize=桶个数(最少设置为 1009 以上)

考虑是否需要将字符串对象入池 可以通过 intern 方法减少重复入池

三、直接内存

1.定义

操作系统的内存 Direct Memory

  • 常见于 NIO 操作时,用于数据缓冲区

  • 分配回收成本较高,但读写性能高

  • 不受 JVM 内存回收管理

2.使用直接内存的好处

2.1文件读写流程:

        因为 java 不能直接操作文件管理,需要切换到内核态,使用本地方法进行操作,然后读取磁盘文件,会在系统内存中创建一个缓冲区,将数据读到系统缓冲区, 然后在将系统缓冲区数据,复制到 java 堆内存中。缺点是数据存储了两份,在系统内存中有一份,java 堆中有一份,造成了不必要的复制。

2.2使用了 DirectBuffer 文件读取流程

直接内存是操作系统和 Java 代码都可以访问的一块区域,无需将代码从系统内存复制到 Java 堆内存,从而提高了效率。

3.直接内存回收原理

public class Code_06_DirectMemoryTest {public static int _1GB = 1024 * 1024 * 1024;public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
//        method();method1();}// 演示 直接内存 是被 unsafe 创建与回收private static void method1() throws IOException, NoSuchFieldException, IllegalAccessException {Field field = Unsafe.class.getDeclaredField("theUnsafe");field.setAccessible(true);Unsafe unsafe = (Unsafe)field.get(Unsafe.class);long base = unsafe.allocateMemory(_1GB);unsafe.setMemory(base,_1GB, (byte)0);System.in.read();unsafe.freeMemory(base);System.in.read();}// 演示 直接内存被 释放private static void method() throws IOException {ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1GB);System.out.println("分配完毕");System.in.read();System.out.println("开始释放");byteBuffer = null;System.gc(); // 手动 gcSystem.in.read();}}

        直接内存的回收不是通过 JVM 的垃圾回收来释放的,而是通过unsafe.freeMemory 来手动释放。 第一步:allocateDirect 的实现

public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);
}

 底层是创建了一个 DirectByteBuffer 对象。

第二步:DirectByteBuffer 类

DirectByteBuffer(int cap) {   // package-privatesuper(-1, 0, cap, cap);boolean pa = VM.isDirectMemoryPageAligned();int ps = Bits.pageSize();long size = Math.max(1L, (long)cap + (pa ? ps : 0));Bits.reserveMemory(size, cap);long base = 0;try {base = unsafe.allocateMemory(size); // 申请内存} catch (OutOfMemoryError x) {Bits.unreserveMemory(size, cap);throw x;}unsafe.setMemory(base, size, (byte) 0);if (pa && (base % ps != 0)) {// Round up to page boundaryaddress = base + ps - (base & (ps - 1));} else {address = base;}cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); // 通过虚引用,来实现直接内存的释放,this为虚引用的实际对象, 第二个参数是一个回调,实现了 runnable 接口,run 方法中通过 unsafe 释放内存。att = null;
}

        这里调用了一个 Cleaner 的 create 方法,且后台线程还会对虚引用的对象监测,如果虚引用的实际对象(这里是 DirectByteBuffer )被回收以后,就会调用 Cleaner 的 clean 方法,来清除直接内存中占用的内存。

 public void clean() {if (remove(this)) {try {// 都用函数的 run 方法, 释放内存this.thunk.run();} catch (final Throwable var2) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {if (System.err != null) {(new Error("Cleaner terminated abnormally", var2)).printStackTrace();}System.exit(1);return null;}});}}}

可以看到关键的一行代码, this.thunk.run(),thunk 是 Runnable 对象。run 方法就是回调 Deallocator 中的 run 方法,

		public void run() {if (address == 0) {// Paranoiareturn;}// 释放内存unsafe.freeMemory(address);address = 0;Bits.unreserveMemory(size, capacity);}

直接内存的回收机制总结

   使用了 Unsafe 类来完成直接内存的分配回收,回收需要主动调用freeMemory 方法

   ByteBuffer 的实现内部使用了 Cleaner(虚引用)来检测 ByteBuffer 。一旦ByteBuffer 被垃圾回收,那么会由 ReferenceHandler(守护线程) 来调用 Cleaner 的 clean 方法调用 freeMemory 来释放内存

注意:

/*** -XX:+DisableExplicitGC 显示的*/private static void method() throws IOException {ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1GB);System.out.println("分配完毕");System.in.read();System.out.println("开始释放");byteBuffer = null;System.gc(); // 手动 gc 失效System.in.read();}

一般用 jvm 调优时,会加上下面的参数:

-XX:+DisableExplicitGC  // 静止显示的 GC

        意思就是禁止我们手动的 GC,比如手动 System.gc() 无效,它是一种 full gc,会回收新生代、老年代,会造成程序执行的时间比较长。所以我们就通过 unsafe 对象调用 freeMemory 的方式释放内存。

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

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

相关文章

Browser Use:AI智能体自动化操作浏览器的开源工具

Browser Use:AI智能体自动化操作浏览器的开源工具 Browser Use 简介1. 安装所需依赖2. 生成openai密钥3. 编写代码4. 运行代码5. 部署与优化5.1 部署AI代理5.2 优化与扩展总结Browser Use 简介 browser-use是一个Python库,它能够帮助我们将AI代理与浏览器自动化操作结合起来;…

Spring Cloud——注册中心

介绍 什么是注册中心&#xff1f; 主要负责服务的注册与发现&#xff0c;确保服务之间的通信顺畅&#xff0c;具体来说&#xff0c;注册中心有以下主要功能&#xff1a;‌服务注册、服务发现、服务健康检查。 服务注册&#xff1a; 服务提供者在启动时会向注册中心注册自身服务…

CSS基础入门【2】

目录 一、知识复习 二、权重问题深入 2.1 同一个标签&#xff0c;携带了多个类名&#xff0c;有冲突&#xff1a; 2.2 !important标记 2.3 权重计算的总结 三、盒模型 3.1 盒子中的区域 3.2 认识width、height 3.3 认识padding 3.4 border 作业&#xff1a; 一、知识…

MATLAB语言的计算机基础

标题&#xff1a;MATLAB语言的计算机基础&#xff1a;探索科学计算的无限可能 一、引言 在当今这个数据爆炸的时代&#xff0c;科学计算和数据分析的重要性不言而喻。MATLAB作为一种高级的、交互式的编程环境&#xff0c;以其强大的数值计算能力、直观的图形展示以及丰富的工…

软路由系统 iStoreOS 中部署 Minecraft 服务器

商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。协议(License): 知识共享署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)作者(Author): lhDream链接(URL): https://blog.luhua.site/archives/1734968846131 软路由系统 iStoreOS 中部署 Minecraft…

.net core 的函数实现

Python基础 Python是一种广泛使用的高级编程语言&#xff0c;以其简洁易读的语法和强大的功能而闻名。它被广泛应用于数据分析、人工智能、网站开发、自动化脚本及其他众多领域。本文将详细介绍Python的基础知识&#xff0c;包括其安装及环境配置、基本语法、数据类型、控制结…

捋一捋相关性运算,以及DTD和NLP中的应用

捋一捋相关性运算&#xff0c;以及DTD和NLP中的应用 相关性和相干性,有木有傻傻分不清相关性数字信号的相关运算同维度信号的相关理解 相关--互相关--相干 回声消除过程如何套用这些知识相关性/相干性检测在DT中的应用时域的标量与向量结合的互相关方法适合block处理的频域相干…

Elasticsearch:normalizer

一、概述 ‌Elastic normalizer‌是Elasticsearch中用于处理keyword类型字段的一种工具&#xff0c;主要用于对字段进行规范化处理&#xff0c;确保在索引和查询时保持一致性。 Normalizer与analyzer类似&#xff0c;都是对字段进行处理&#xff0c;但normalizer不会对字段进…

go语言的成神之路-筑基篇-对文件的操作

目录 一、对文件的读写 Reader?接口 ?Writer接口 copy接口 bufio的使用 ioutil库? 二、cat命令 三、包 1. 包的声明 2. 导入包 3. 包的可见性 4. 包的初始化 5. 标准库包 6. 第三方包 ?7. 包的组织 8. 包的别名 9. 包的路径 10. 包的版本管理 四、go mo…

SQL Server中FIRST_VALUE和 LAST_VALUE窗口函数允许在一个指定的窗口内返回第一个或最后一个值

在 SQL Server 中&#xff0c;FIRST_VALUE 和 LAST_VALUE 是用于窗口函数&#xff08;Window Functions&#xff09;的两个非常有用的函数。它们允许你在一个指定的窗口内返回第一个或最后一个值。这两个函数通常与 OVER 子句一起使用&#xff0c;以定义窗口的范围和排序规则。…

【入门】拐角III

描述 输入整数N&#xff0c;输出相应方阵。 输入描述 一个整数N。&#xff08; 0 < n < 10 ) 输出描述 一个方阵&#xff0c;每个数字的场宽为3。 用例输入 1 5 用例输出 1 5 5 5 5 55 4 4 4 45 4 3 3 35 4 3 2 25 4 3 2 1 来源 二维数组…

攻防世界 ics-06

开启场景 可以交互的按钮不是很多&#xff0c;没有什么有用信息&#xff0c;查看页面源代码找到了index.php &#xff0c;后面跟着“报表中心” 传参访问 /index.php 看到了参数 id1&#xff0c;用 burp 抓包爆破&#xff08;这里应该不是 sql 注入&#xff09; 2333 的长度与众…

VMware虚拟机安装银河麒麟操作系统KylinOS教程(超详细)

目录 引言1. 下载2. 安装 VMware2. 安装银河麒麟操作系统2.1 新建虚拟机2.2 安装操作系统2.3 网络配置 3. 安装VMTools 创作不易&#xff0c;禁止转载抄袭&#xff01;&#xff01;&#xff01;违者必究&#xff01;&#xff01;&#xff01; 创作不易&#xff0c;禁止转载抄袭…

ByConity BSP 解锁数据仓库新未来

文章目录 前言BSP 模式简介基于 TPC-DS 的 ELT 活动测试环境登录 ECS数据查询配置 执行 02.sqlsql解释&#xff1a;1. 第一步&#xff1a;创建 wscs 临时表2. 第二步&#xff1a;创建 wswscs 临时表3. 第三步&#xff1a;对比 2001 年和 2002 年的数据子查询 1&#xff1a;提取…

Sql Sqserver 相关知识总结

Sql Sqserver 相关知识总结 文章目录 Sql Sqserver 相关知识总结前言优化语句查询&#xff08;select&#xff09;条件过滤&#xff08;Where&#xff09;分组处理&#xff08;GROUP BY&#xff09;模糊查询&#xff08;like&#xff09;包含&#xff08;in&#xff09;合集&am…

论文解读 | EMNLP2024 一种用于大语言模型版本更新的学习率路径切换训练范式

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 作者简介 王志豪&#xff0c;厦门大学博士生 刘诗雨&#xff0c;厦门大学硕士生 内容简介 新数据的不断涌现使版本更新成为大型语言模型&#xff08;LLMs&#xff…

Android OpenGl(二) Shader

一、Shader 1、什么是Shader&#xff0c;为什么要使用Shder &#xff08;1&#xff09;shader运行在gpu上的小程序 &#xff08;2&#xff09;以前使用固定管线&#xff0c;但缺点是灵活度不够&#xff0c;无法满足复杂需求&#xff0c;为了解决固定管线的缺点&#xff0c;出…

软考:系统架构设计师教材笔记(持续更新中)

教材中的知识点都会在。其实就是将教材中的废话删除&#xff0c;语言精练一下&#xff0c;内容比较多&#xff0c;没有标注重点 系统架构概述 定义 系统是指完成某一特定功能或一组功能所需要的组件集&#xff0c;而系统架构则是对所有组件的高层次结构表示&#xff0c;包括各…

TDengine 新功能 VARBINARY 数据类型

1. 背景 VARBINARY 数据类型用于存储二进制数据&#xff0c;与 MySQL 中的 VARBINARY 数据类型功能相同&#xff0c;VARBINARY 数据类型长度可变&#xff0c;在创建表时指定最大字节长度&#xff0c;使用进按需分配存储&#xff0c;但不能超过建表时指定的最大值。 2. 功能说明…

虚拟机桥接模式

主机Win10,虚拟机xp 1.虚拟机设置中选择桥接模式 2.在虚拟机菜单&#xff1a;编辑>虚拟机网络编辑&#xff0c;点击“更改设置”&#xff0c;可以看到三个网卡&#xff0c;这三个网卡分别对应不同的网络共享模式。桥接模式须使用VMnet0&#xff0c;如果没看到这个网卡&…