Java 垃圾回收机制详解

news2024/12/17 7:53:16

1 垃圾回收的概念

垃圾回收(Garbage Collection,GC)是自动管理内存的一种机制,用于释放不再使用的对象所占用的内存空间,防止内存溢出。垃圾回收器通过识别和回收那些已经死亡或长时间未使用的对象,来优化内存的使用。

在 Java 语言出现之前,开发者需要手动管理内存,例如在 C 和 C++ 中,开发者需要编写构造函数和析构函数来创建和销毁对象。Java 引入了垃圾回收机制,简化了内存管理,开发者无需手动释放内存,从而减少了内存泄漏和悬空指针等问题。

2 垃圾判断算法

在 Java 虚拟机(JVM)中,垃圾回收器需要判断哪些对象是垃圾,哪些对象仍然存活。垃圾判断算法是垃圾回收机制的核心部分,常见的算法包括引用计数算法可达性分析算法

2.1 引用计数算法

引用计数算法(Reference Counting)通过在对象头中维护一个引用计数器来记录对象被引用的次数。每当对象被引用时,引用计数器加 1;当引用被删除时,引用计数器减 1。当引用计数器为 0 时,对象被认为是垃圾,可以被回收。

2.1.1 示例

String s = new String("沉默王二");
s = null; // 引用计数为 0,对象可以被回收

在这个例子中,字符串对象 "沉默王二" 最初被 s 引用,引用计数为 1。当 s 被设置为 null 时,引用计数变为 0,对象可以被回收。

2.1.2 优点

  • 实时性:引用计数算法将垃圾回收分散到应用程序的运行过程中,而不是集中在垃圾收集时,因此不会导致“Stop-The-World”事件。

2.1.3 缺点

  • 循环引用问题:引用计数算法无法解决循环引用的问题。如果两个对象相互引用,即使它们不再被外部引用,它们的引用计数也不会为 0,导致无法被回收。

2.1.4 循环引用示例

public class ReferenceCountingGC {
    public Object instance;

    public static void testGC() {
        ReferenceCountingGC a = new ReferenceCountingGC();
        ReferenceCountingGC b = new ReferenceCountingGC();
        a.instance = b;
        b.instance = a;
        a = null;
        b = null;
    }
}

在这个例子中,ab 相互引用,导致它们的引用计数永远不会为 0,即使它们已经不再被外部引用,也无法被回收。

2.2 可达性分析算法

可达性分析算法(Reachability Analysis)通过从一组称为 GC Roots 的对象开始,遍历对象图,标记所有可达的对象。不可达的对象被认为是垃圾,可以被回收。

2.2.1 GC Roots

GC Roots 是一组必须活跃的引用,它们是程序运行时的起点,是一切引用链的源头。在 Java 中,GC Roots 包括以下几种:

  1. 虚拟机栈中的引用:方法的参数、局部变量等。
  2. 本地方法栈中 JNI 的引用:通过 JNI(Java Native Interface)调用的本地代码中的引用。
  3. 类静态变量:类的静态属性引用的对象。
  4. 运行时常量池中的常量:字符串常量或类类型常量。

2.2.2 示例

  1. 虚拟机栈中的引用
public class StackReference {
    public void greet() {
        Object localVar = new Object(); // 局部变量,存在于虚拟机栈中
        System.out.println(localVar.toString());
    }

    public static void main(String[] args) {
        new StackReference().greet();
    }
}

在这个例子中,localVar 是一个局部变量,存在于虚拟机栈中,可以被认为是 GC Roots。在 greet 方法执行期间,localVar 引用的对象是活跃的。当 greet 方法执行完毕后,localVar 的作用域结束,引用的对象将不再被 GC Roots 引用,因此可以被回收。

  1. 本地方法栈中 JNI 的引用
public native void nativeMethod();

JNIEXPORT void JNICALL Java_NativeExample_nativeMethod(JNIEnv *env, jobject thisObj) {
    jobject localRef = (*env)->NewObject(env, ...); // 本地方法栈中的 JNI 引用
}

在这个例子中,localRef 是一个 JNI 引用,存在于本地方法栈中。在本地方法执行期间,localRef 引用的对象是活跃的。一旦本地方法执行完毕,除非 localRef 是全局的,否则它引用的对象将被回收。

  1. 类静态变量
public class StaticFieldReference {
    private static Object staticVar = new Object(); // 类静态变量

    public static void main(String[] args) {
        System.out.println(staticVar.toString());
    }
}

在这个例子中,staticVar 是一个类静态变量,引用的对象存储在元空间中,可以被认为是 GC Roots。只要 StaticFieldReference 类未被卸载,staticVar 引用的对象就不会被回收。

  1. 运行时常量池中的常量
public class ConstantPoolReference {
    public static final String CONSTANT_STRING = "Hello, World"; // 字符串常量
    public static final Class<?> CONSTANT_CLASS = Object.class; // 类类型常量

    public static void main(String[] args) {
        System.out.println(CONSTANT_STRING);
        System.out.println(CONSTANT_CLASS.getName());
    }
}

在这个例子中,CONSTANT_STRINGCONSTANT_CLASS 是运行时常量池中的常量,它们引用的对象可以被认为是 GC Roots。只要 ConstantPoolReference 类未被卸载,这些常量引用的对象就不会被回收。

2.3 小结

  • 引用计数算法通过维护对象的引用计数来判断对象是否可以被回收,但它无法解决循环引用的问题。
  • 可达性分析算法通过从 GC Roots 开始遍历对象图,标记所有可达的对象,解决了循环引用的问题。

在 Java 中,垃圾回收器通常使用可达性分析算法来判断对象是否可以被回收。GC Roots 包括虚拟机栈中的引用、本地方法栈中的 JNI 引用、类静态变量和运行时常量池中的常量。

3 垃圾收集算法

在确定了哪些对象是垃圾之后,垃圾收集器需要选择合适的算法来回收这些垃圾。垃圾收集算法的设计目标是高效地回收垃圾,同时尽量减少对应用程序性能的影响。常见的垃圾收集算法包括标记-清除算法复制算法标记-整理算法分代收集算法

3.1 标记-清除算法(Mark-Sweep)

标记-清除算法是最基础的垃圾回收算法,分为两个阶段:

  1. 标记阶段:垃圾收集器从 GC Roots 开始,遍历所有可达的对象,并标记这些对象为存活对象。
  2. 清除阶段:垃圾收集器清除所有未被标记的对象,即垃圾对象。

优点

  • 实现简单:标记-清除算法的逻辑清晰,易于实现。

缺点

  • 内存碎片问题:清除阶段只是简单地回收垃圾对象,不会对内存进行整理,导致内存碎片化。内存碎片化可能会导致在分配大对象时无法找到足够的连续内存,从而触发新一轮的垃圾收集。

3.2 复制算法(Copying)

复制算法是为了解决标记-清除算法的内存碎片问题而设计的。它将可用内存划分为大小相等的两块区域,每次只使用其中一块。

  1. 复制阶段:当一块内存用完后,垃圾收集器将存活的对象复制到另一块内存中。
  2. 清理阶段:复制完成后,垃圾收集器清理已使用过的内存区域。

优点

  • 内存连续性:复制算法保证了内存的连续性,避免了内存碎片问题。
  • 高效性:由于只需要复制存活的对象,复制算法的效率较高。

缺点

  • 内存利用率低:复制算法只能利用一半的内存空间,另一半内存始终处于空闲状态,导致内存利用率较低。

3.3 标记-整理算法(Mark-Compact)

标记-整理算法结合了标记-清除算法和复制算法的优点,分为三个阶段:

  1. 标记阶段:与标记-清除算法相同,垃圾收集器标记所有可达的对象。
  2. 整理阶段:将所有存活的对象移动到内存的一端,确保内存的连续性。
  3. 清理阶段:清理掉端边界以外的内存区域。

优点

  • 解决内存碎片问题:标记-整理算法通过整理内存,避免了内存碎片问题。
  • 内存利用率高:与复制算法不同,标记-整理算法可以利用全部内存空间。

缺点

  • 效率较低:整理阶段需要移动所有存活的对象,导致效率较低。

3.4 分代收集算法(Generational Collection)

分代收集算法并不是一种独立的算法,而是根据对象的存活周期将内存划分为不同的代(通常是新生代老年代),并根据各代的特点采用不同的垃圾收集算法。

3.4.1 新生代(Young Generation)

  • 特点:新生代中的对象通常存活时间较短,大部分对象在创建后很快就会变成垃圾。
  • 算法:由于新生代中大部分对象是短命的,垃圾收集器通常采用复制算法。新生代进一步分为 Eden 区和两个 Survivor 区(From 和 To)。每次垃圾收集时,存活的对象从 Eden 区和 From 区复制到 To 区,然后清理 Eden 区和 From 区。

3.4.2 老年代(Old Generation)

  • 特点:老年代中的对象通常存活时间较长,对象存活率高。
  • 算法:由于老年代中的对象存活率高,垃圾收集器通常采用标记-清除算法标记-整理算法。老年代的垃圾收集称为 Major GC,通常会触发“Stop-The-World”事件。

优点

  • 优化性能:分代收集算法根据对象的存活周期选择合适的垃圾收集算法,从而优化垃圾收集的性能。
  • 减少停顿时间:通过将内存划分为不同的代,垃圾收集器可以更频繁地进行 Minor GC(新生代垃圾收集),减少 Major GC 的频率,从而减少停顿时间。

3.5 小结

  • 标记-清除算法是最基础的垃圾回收算法,但存在内存碎片问题。
  • 复制算法解决了内存碎片问题,但内存利用率较低。
  • 标记-整理算法结合了标记-清除和复制算法的优点,解决了内存碎片问题,但效率较低。
  • 分代收集算法根据对象的存活周期将内存划分为不同的代,并采用不同的垃圾收集算法,从而优化垃圾收集的性能。

不同的垃圾收集算法适用于不同的场景,垃圾收集器的设计需要在内存利用率、垃圾收集效率和停顿时间之间找到平衡。

4 新生代和老年代

在 Java 虚拟机(JVM)中,堆(Heap)是最大的一块内存区域,也是垃圾收集器管理的主要区域。堆内存被划分为新生代(Young Generation)老年代(Old Generation),其中新生代又进一步分为 Eden 区和两个 Survivor 区(From 和 To)。

4.1 新生代(Young Generation)

4.1.1 Eden 区

根据 IBM 的研究,大约 98% 的对象是“朝生夕死”的,即这些对象在创建后很快就会变成垃圾。因此,大多数对象在新生代的 Eden 区中分配。

  • 对象分配:当对象被创建时,首先在 Eden 区中分配内存。
  • Minor GC:当 Eden 区没有足够的空间分配新对象时,JVM 会触发一次 Minor GC(新生代垃圾收集)。Minor GC 的频率较高,回收速度也较快。
  • 存活对象处理:在 Minor GC 之后,Eden 区中绝大部分对象会被回收,而那些存活的对象会被移动到 Survivor 区的 From 区。如果 From 区空间不足,存活对象会直接进入 To 区

4.1.2 Survivor 区

Survivor 区是新生代中的一个缓冲区,分为 From 区To 区。它的主要作用是减少对象直接进入老年代的频率,从而减少 Major GC 的发生。

4.1.3 为什么需要 Survivor 区?

如果没有 Survivor 区,Eden 区每进行一次 Minor GC,存活的对象就会被直接送到老年代。这样会导致老年代很快被填满,进而频繁触发 Major GC。然而,很多对象虽然在一次 Minor GC 中存活下来,但可能在第二次或第三次 Minor GC 中就会被回收。因此,直接将这些对象送入老年代并不是一个明智的选择。

Survivor 区的存在意义在于:

  • 减少进入老年代的对象:Survivor 区通过预筛选机制,确保只有那些在多次 Minor GC 中存活下来的对象才会被晋升到老年代。
  • 减少 Major GC 的频率:通过减少进入老年代的对象数量,Survivor 区有效地减少了 Major GC 的发生频率。

4.1.4 Survivor 区为什么分为两块?

Survivor 区分为两块(From 和 To)的主要目的是解决内存碎片化问题。

  • 内存碎片化问题:如果没有 Survivor 区,或者 Survivor 区只有一个区域,那么在 Minor GC 后,Eden 区被清空,存活的对象被放入 Survivor 区。此时,Survivor 区中可能存在一些需要被回收的对象。在这种情况下,垃圾收集器只能采用标记-清除算法,这会导致内存碎片化。

  • 双 Survivor 区的优势:通过设置两个 Survivor 区,每次 Minor GC 时,存活的对象会从 Eden 区和 From 区复制到 To 区。第二次 Minor GC 时,From 区和 To 区的角色互换,存活的对象从 Eden 区和 To 区复制到 From 区。这种机制确保了在任何时候,总有一个 Survivor 区是空的,另一个 Survivor 区是无碎片的。

  • 为什么不是更多块?:如果 Survivor 区被划分为更多块,每一块的空间会变得更小,容易导致 Survivor 区满,进而增加对象进入老年代的频率。两块 Survivor 区是经过权衡后的最佳方案。

4.2 老年代(Old Generation)

老年代占据堆内存的 2/3,主要用于存放存活时间较长的对象。老年代的垃圾收集称为 Major GC,每次 Major GC 都会触发“Stop-The-World”事件,即暂停所有用户线程,直到垃圾收集完成。

4.2.1 老年代的垃圾收集算法

由于老年代中的对象存活率较高,复制算法在老年代中效率较低,因此老年代通常采用 标记-整理算法标记-清除算法

  • 标记-整理算法:将所有存活的对象移动到内存的一端,然后清理掉端边界以外的内存区域,避免了内存碎片问题。
  • 标记-清除算法:标记所有存活的对象,然后清除未标记的对象,但可能会导致内存碎片。

4.2.2 对象进入老年代的条件

除了通过内存担保机制将无法安置的对象直接进入老年代外,以下几种情况也会导致对象进入老年代:

  1. 大对象

大对象是指需要大量连续内存空间的对象。这类对象不管其生命周期长短,都会直接进入老年代。这是为了避免在 Eden 区和 Survivor 区之间进行大量的内存复制操作。

  1. 长期存活对象

JVM 为每个对象定义了一个 对象年龄(Age)计数器。对象在 Survivor 区中每经历一次 Minor GC,年龄就会增加 1 岁。当对象的年龄达到某个阈值(默认是 15 岁)时,它会被晋升到老年代。

  • 年龄阈值设置:可以通过 JVM 参数 -XX:MaxTenuringThreshold 来调整对象晋升到老年代的年龄阈值。默认值为 15,但可以通过以下命令查看:

    java -XX:+PrintFlagsFinal -version | grep MaxTenuringThreshold
    
  1. 动态对象年龄

JVM 并不强制要求对象必须达到 15 岁才能进入老年代。如果 Survivor 区中某个年龄段的对象总大小超过了 Survivor 区的一半,那么该年龄段及以上年龄段的所有对象都会在下一次垃圾回收时被晋升到老年代。

这种动态调整机制类似于负载均衡中的动态调度算法,能够根据对象的实际存活情况优化内存使用,减少垃圾收集的频率。

4.3 小结

  • 新生代:大多数对象在 Eden 区中分配,经过 Minor GC 后,存活的对象会被移动到 Survivor 区。Survivor 区分为 From 和 To 两个区域,用于减少内存碎片化,并减少对象进入老年代的频率。
  • 老年代:老年代用于存放存活时间较长的对象,垃圾收集采用标记-整理或标记-清除算法。对象进入老年代的条件包括大对象、长期存活对象和动态对象年龄。

通过合理的内存划分和垃圾收集算法,JVM 能够在保证内存利用率的同时,减少垃圾收集的停顿时间,从而提高应用程序的性能。

5 Stop The World

在 Java 垃圾收集(Garbage Collection,GC)过程中,“Stop The World” 是一个重要的概念。它指的是在垃圾收集器执行垃圾回收时,JVM 会暂停所有的用户线程(即应用程序线程),直到垃圾收集完成。这种暂停被称为 “Stop The World” 事件。

5.1 为什么需要 “Stop The World”?

“Stop The World” 的主要目的是确保垃圾收集器能够准确地识别和回收垃圾对象。如果在垃圾收集过程中,用户线程继续运行并修改堆中的对象,可能会导致以下问题:

  1. 对象状态不一致:用户线程可能会修改对象的引用关系,导致垃圾收集器无法正确判断对象的可达性。
  2. 内存泄漏或误回收:如果垃圾收集器在收集过程中无法准确识别对象的引用关系,可能会导致某些对象被错误地回收(即本应存活的对象被回收),或者某些垃圾对象未被回收(即内存泄漏)。

因此,为了确保垃圾收集的准确性,JVM 在执行垃圾收集时会暂停所有用户线程,即触发 “Stop The World” 事件。

5.2 “Stop The World” 的影响

“Stop The World” 事件会对 Java 应用程序的性能产生显著影响,尤其是在停顿时间较长的情况下:

  • 响应时间变长:如果垃圾收集器的停顿时间过长,应用程序的响应时间会显著增加,尤其是在对实时性要求较高的应用中,如交易系统、游戏服务器等,这种情况是不可接受的。
  • 用户体验下降:对于用户交互频繁的应用程序(如 Web 应用或桌面应用),长时间的停顿会导致用户体验下降,甚至可能导致用户认为应用程序无响应。

5.3 减少 “Stop The World” 的策略

为了减少 “Stop The World” 事件对应用程序性能的影响,Java 提供了多种垃圾收集器,并引入了并发垃圾收集技术。以下是一些常见的策略:

5.3.1 并发垃圾收集器

并发垃圾收集器(如 G1(Garbage First)ZGC(Z Garbage Collector))通过并发执行垃圾收集任务,尽量减少 “Stop The World” 的停顿时间。

  • G1 收集器:G1 收集器通过将堆内存划分为多个区域(Region),并采用并发标记和混合收集的方式,减少了停顿时间。G1 的目标是让停顿时间控制在用户指定的范围内(通过 -XX:MaxGCPauseMillis 参数设置)。
  • ZGC 收集器:ZGC 是一种低延迟的垃圾收集器,它的设计目标是让停顿时间不超过 10 毫秒。ZGC 通过并发执行大部分垃圾收集任务,显著减少了 “Stop The World” 的影响。

5.3.2 分代收集

分代收集算法将堆内存划分为新生代和老年代,并根据对象的存活周期采用不同的垃圾收集策略。新生代中的对象通常存活时间较短,因此垃圾收集器可以更频繁地进行 Minor GC,而老年代中的对象存活时间较长,垃圾收集器会减少 Major GC 的频率。

通过分代收集,垃圾收集器可以更高效地管理内存,减少 “Stop The World” 事件的发生频率。

5.3.3 增量式垃圾收集

增量式垃圾收集器通过将垃圾收集任务分解为多个小任务,并在应用程序运行的间隙中逐步执行这些任务,从而减少 “Stop The World” 的停顿时间。

5.3.4 并行垃圾收集

并行垃圾收集器(如 Parallel GC)通过多线程并行执行垃圾收集任务,加快垃圾收集的速度,从而减少停顿时间。虽然并行垃圾收集器仍然会触发 “Stop The World” 事件,但它通过并行处理提高了垃圾收集的效率。

5.4 小结

“Stop The World” 是 Java 垃圾收集过程中不可避免的一个挑战。它确保了垃圾收集器能够准确地识别和回收垃圾对象,但同时也会对应用程序的性能产生影响。为了减少 “Stop The World” 的停顿时间,Java 提供了多种垃圾收集器(如 G1 和 ZGC),并通过并发、分代和增量式垃圾收集等技术,尽量减少对应用程序性能的影响。

在实际应用中,开发者需要根据应用程序的性能需求和内存使用情况,选择合适的垃圾收集器,并通过调优参数(如 -XX:MaxGCPauseMillis)来平衡内存的有效利用和应用程序的响应性能。

6 总结

Java 的垃圾回收机制通过自动管理内存,简化了开发者的内存管理工作。垃圾回收器通过引用计数算法和可达性分析算法判断垃圾对象,并使用标记清除、复制、标记整理和分代收集等算法进行垃圾回收。新生代和老年代的划分进一步优化了垃圾回收的效率。尽管“Stop The World”事件会对性能产生影响,但现代垃圾收集器通过并发收集技术,尽量减少了停顿时间。

7 思维导图

在这里插入图片描述

8 参考链接

深入理解 JVM 的垃圾回收机制

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

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

相关文章

拿到小米 Offer,却迷茫了。。

大家好&#xff0c;我是程序员鱼皮&#xff0c;12 月了&#xff0c;很多小伙伴也拿到了秋招的 Offer&#xff08;没拿到也不要灰心&#xff09;&#xff0c;但即使拿到 Offer&#xff0c;可能还会有一些其他的顾虑。今天分享我们编程导航一位鱼友的提问&#xff0c;给大家作为学…

医疗领域的网络安全预防:保障患者隐私与医疗数据安全

医疗领域的网络安全预防&#xff1a;保障患者隐私与医疗数据安全 随着信息技术的不断发展和医疗行业的数字化转型&#xff0c;网络安全在医疗领域变得愈加重要。医疗行业处理着大量的敏感数据&#xff0c;包括患者的个人信息、医疗记录、诊疗方案等&#xff0c;这些数据一旦被…

实现线性回归笔记 # 自用

线性模型可以看作是一个单层的神经网络。 对于n个输入[x1, x2, ...., xn]&#xff0c;由n个权重[w1, w2, ......, wn]以及一个偏置常数b得到的输出y&#xff0c;则称y x1w1x2w2......xnwnb称为线性模型。 即 线性模型是对n维输入的加权外加偏差。 要利用线性模型进行预测&a…

实景视频与模型叠加融合?

[视频GIS系列]无人机视频与与实景模型进行实时融合_无人机视频融合-CSDN博客文章浏览阅读1.5k次&#xff0c;点赞28次&#xff0c;收藏14次。将无人机视频与实景模型进行实时融合是一个涉及多个技术领域的复杂过程&#xff0c;主要包括无人机视频采集、实景模型构建、视频与模型…

c语言——数据结构【链表:单向链表】

上篇→快速掌握C语言——数据结构【创建顺序表】多文件编译-CSDN博客 一、链表 二、单向链表 2.1 概念 2.2 单向链表的组成 2.3 单向链表节点的结构体原型 //类型重定义,表示存放的数据类型 typedef int DataType;//定义节点的结构体类型 typedef struct node {union{int l…

【LC】876. 链表的中间结点

题目描述&#xff1a; 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个中间结点…

Bugku---misc---隐写2

题目出处&#xff1a;首页 - Bugku CTF平台 ✨打开发现是一张图片&#xff0c;于是查看属性&#xff0c;放在010查看&#xff0c;这都是基本步骤了&#xff0c;发现里面有一个flag.rar&#xff01;&#xff01;&#xff01;拿binwalk分析也确实存在 ✨于是按照压缩包的起始位置…

无需公网IP,本地可访问TightVNC 服务端

TightVNC 是一款免费而且开源的远程桌面软件&#xff0c;它允许用户在不同的操作系统之间实现无缝连接&#xff0c;TightVNC支持 Windows、macOS 和 Linux 等多个操作系统&#xff0c;为用户提供高效便捷的远程控制体验。在 Windows 系统电脑端安装使用 TightVNC 服务端和客户端…

【Unity基础】Unity中如何实现图形倒计时

为了在Unity中实现一个图形倒计时&#xff0c;除了代码部分&#xff0c;还需要一些UI元素的创建和设置。本文以环形倒计时为例&#xff0c;以下是完整的步骤&#xff0c;涵盖了如何创建UI元素、设置它们&#xff0c;以及如何编写控制环形倒计时进度的脚本。 1. 创建UI元素 创建…

Excel/VBA 正则表达式归纳汇总

1.with结构。以下语句用来提取A列中的“成品”两个字前面的部分的中文&#xff0c;不含成品两个字&#xff0c;结果存放在第2列。使用了On Error Resume Next&#xff0c;表示错误时继续下一条。 Sub 提取口味() Set regx CreateObject("vbscript.regexp") On Err…

xshell连接虚拟机,更换网络模式:NAT->桥接模式

NAT模式&#xff1a;虚拟机通过宿主机的网络访问外网。优点在于不需要手动配置IP地址和子网掩码&#xff0c;只要宿主机能够访问网络&#xff0c;虚拟机也能够访问。对外部网络而言&#xff0c;它看到的是宿主机的IP地址&#xff0c;而不是虚拟机的IP。但是&#xff0c;宿主机可…

优选算法《双指针》

在学习了C/C的基础知识之后接下来我们就可以来系统的学习相关的算法了&#xff0c;这在之后的笔试、面试或竞赛都是必须要掌握的&#xff1b;在这些算法中我们先来了解的是一些非常经典且较为常用的算法&#xff0c;在此也就是优选出来的算法&#xff0c;接下来在每一篇章中我们…

SQL server学习06-查询数据表中的数据(中)

目录 一&#xff0c;聚合函数 1&#xff0c;常用聚合函数 2&#xff0c;具体使用 二&#xff0c;GROP BY子句分组 1&#xff0c;基础语法 2&#xff0c;具体使用 3&#xff0c;加上HAVING对组进行筛选 4&#xff0c;使WHERE记录查询条件 汇总查询&#xff1a;在对数…

上传文件时获取音视频文件时长和文本文件字数

获取音视频文件时长和文本文件字数 一、获取音视频文件时长二、计算文本文件字数 最近有个需求&#xff0c;要求上传文件时获取音视频文件时长和文本文件字数&#x1f436;。 发现这样的冷门资料不多&#xff0c;特做个记录。本文忽略文件上传功能&#xff0c;只封装核心的工具…

C语言学习day22:进程ID获取工具/GetWindowThreadProcessId函数

简言&#xff1a; 每个人都有身份证号&#xff0c;这个身份证号就是个人的唯一标识符 进程也是如此&#xff0c;每个进程也有唯一的标识符&#xff0c;来标记自身是独一无二的 如下图:其中PID &#xff1a;Process ID&#xff0c;即进程ID 但是我们怎么去在编程中去获取某个…

使用Localstorage(Mapty)

使用Localstorage(Mapty) 首先&#xff0c;我们创建一个函数名&#xff0c;先在app中去调用它 // 为所有的锻炼创建本地存储this._setLocalStorage();之后我们就开始编写这个函数的功能 _setLocalStorage() {localStorage.setItem(workouts, JSON.stringify(this.#workouts));…

如何用细节提升用户体验?

前端给用户反馈是提升用户体验的重要部分&#xff0c;根据场景选择不同的方式可以有效地提升产品的易用性和用户满意度。以下是常见的方法&#xff1a; 1. 视觉反馈 用户执行了某些操作后&#xff0c;需要即时确认操作结果。例如&#xff1a;按钮点击、数据提交、页面加载等。…

OpenHarmony-3.HDF input子系统(5)

HDF input 子系统OpenHarmony-4.0-Release 1.Input 概述 输入设备是用户与计算机系统进行人机交互的主要装置之一&#xff0c;是用户与计算机或者其他设备通信的桥梁。常见的输入设备有键盘、鼠标、游戏杆、触摸屏等。本文档将介绍基于 HDF_Input 模型的触摸屏器件 IC 为 GT91…

旅游资源系统|Java|SSM|VUE| 前后端分离

【技术栈】 1⃣️&#xff1a;架构: B/S、MVC 2⃣️&#xff1a;系统环境&#xff1a;Windowsh/Mac 3⃣️&#xff1a;开发环境&#xff1a;IDEA、JDK1.8、Maven、Mysql5.7 4⃣️&#xff1a;技术栈&#xff1a;Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html 5⃣️数据库可…

Docker网络与数据管理

Docker网络与数据管理 1. Docker网络基础&#xff1a;桥接网络、主机网络和自定义网络 Docker提供了多种网络模式&#xff0c;以满足不同应用场景的需求。理解Docker的网络模式对于容器间通信、网络安全性及性能优化至关重要。在Docker中&#xff0c;每个容器都可以连接到不同…