当前位置: 首页 > news >正文

Java核心篇之JVM探秘:对象创建与内存分配机制

系列文章目录

第一章 Java核心篇之JVM探秘:内存模型与管理初探

第二章 Java核心篇之JVM探秘:对象创建与内存分配机制

第三章 Java核心篇之JVM探秘:垃圾回收算法与垃圾收集器

第四章 Java核心篇之JVM调优实战:Arthas工具使用及GC日志分析


目录

前言

一、对象创建过程

(1)类加载检查

(2)分配内存

1.对象创建与内存分配

2.对象填充

3.执行构造函数

4.并发问题处理

(3)初始化零值

(4)设置对象头

Mark Word

Type Pointer

Array Length (如果对象是数组)

(5)执行方法

二、对象内存分配

(1)对象在栈上分配

示例:

(2)对象在Eden上分配

Eden区分配的特点:

示例:

(3)大对象直接进入老年代

(4)长期存活的对象将进入老年代

(5)对象动态年龄判断

(6)老年代空间分配担保机制

三、对象内存回收

(1)引用计数法

示例:

(2)可达性分析算法

(3)常见引用类型

(4)finalize()方法最终判定对象是否存活

(5)如何判断一个类是无用的类

总结


前言

        Java虚拟机(JVM)是Java语言的核心组件之一,负责执行Java字节码。在JVM中,对象的创建和内存管理是一个复杂而精细的过程,涉及多个阶段和多种策略。本文将深入探讨JVM中的对象创建流程、内存分配机制以及它们如何影响程序性能。


一、对象创建过程

下边两张图分别是类加载机制和对象创建过程的流程图

(1)类加载检查

当程序请求创建一个新对象时,JVM首先会检查这个类是否已经被加载、解析和初始化过。如果尚未完成这些步骤,那么JVM会先执行类加载过程。

(2)分配内存

一旦类被确认可以使用,JVM会在堆内存中为新对象分配空间。对象的内存大小由其成员变量决定,包括实例变量和继承链上的变量。

在JVM中,内存分配主要发生在堆内存中,堆内存是所有线程共享的内存区域,用来存储所有Java对象实例和数组。内存分配的过程可以分为几个关键步骤:

1.对象创建与内存分配

当Java程序请求创建一个新对象时,JVM首先检查这个类是否已经被加载、解析和初始化过。如果类已经准备好,JVM会在堆中为新对象分配内存。内存分配的方式有两种主要策略:指针碰撞(Bump-the-Pointer)和空闲列表(Free List)。

  • 指针碰撞:如果堆内存是规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间维护一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离。
  • 空闲列表:如果堆内存是不规整的,已使用的内存和空闲的内存相互交错,那就需要维护一个列表,记录上面那些大小不一的空闲内存区间。

2.对象填充

分配好内存之后,JVM会对新对象进行填充,包括初始化对象的成员变量为默认值(例如,int类型为0,引用类型为null)。

3.执行构造函数

最后,JVM会执行对象的构造函数,完成对象的初始化。

4.并发问题处理

在多线程环境下,内存分配和对象创建可能会引发竞态条件和内存一致性问题。JVM采用了多种机制来解决这些问题:

  • 线程同步 JVM使用锁机制来保证在多线程环境下内存分配的原子性。当多个线程试图同时创建对象时,JVM会使用锁来确保一次只有一个线程能够进行内存分配,从而避免竞态条件。

  • 内存屏障 为了保证内存访问的有序性,防止指令重排序,JVM使用内存屏障(Memory Barrier)技术。内存屏障是一种特殊的指令,它可以阻止编译器和处理器对内存操作进行重排序,确保内存操作的顺序符合程序的预期。

  • 缓存一致性 在现代多核处理器中,每个CPU都有自己的缓存,为了保持缓存之间的一致性,处理器使用了一种称为MESI(Modified, Exclusive, Shared, Invalid)协议的缓存一致性协议。JVM利用硬件提供的缓存一致性协议来维持多线程环境下的内存一致性。

  • TLAB (Thread Local Allocation Buffer) 为了减少锁的使用,提高对象创建的效率,JVM提供了一个叫做TLAB(线程本地分配缓冲区)的概念。每个线程都有一个独立的TLAB,对象优先在自己的TLAB中分配,这样可以避免在多线程环境下频繁地获取锁,从而提高了对象创建的速度。

  • CAS (Compare and Swap) CAS是一种无锁编程技术,用于原子更新变量。JVM利用CAS操作来实现一些轻量级的同步机制,比如原子变量类(如java.util.concurrent.atomic包中的类)。

(3)初始化零值

分配完内存后,JVM会将对象的成员变量初始化为默认值,如整型为0,浮点型为0.0,引用类型为null等。这一步是为了确保对象在构造函数执行前处于一致状态。

(4)设置对象头

当JVM为新对象分配完内存后,在初始化零值之前,它会先设置对象头。对象头通常包含以下信息:

Mark Word

Mark Word是对象头中的一部分,主要用于存储对象的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁等。Mark Word的布局会根据对象的锁定状态动态改变,以便支持轻量级锁、偏向锁、重量级锁等不同的锁级别。

Type Pointer

这是指向对象所属类的元数据的指针,JVM通过这个指针确定对象所属的类。在某些JVM实现中,类型指针可能不在对象头中,而是通过其他方式(如类指针压缩)来存储。

Array Length (如果对象是数组)

如果创建的对象是一个数组,对象头还会额外包含一个长度字段,用于存储数组的长度。

(5)执行<init>方法

执行<init>方法,也就是执行对象的构造函数,这是程序员定义的用于初始化对象状态的方法。构造函数可以调用其他方法或访问静态变量,但不能直接访问或修改非初始化的实例变量。

二、对象内存分配

(1)对象在栈上分配

通常情况下,对象是在堆内存中分配的,这是因为对象的生命周期不可预测,可能需要长期存在。然而,在某些特殊情况下,对象可以分配在栈上,这种情况被称为栈上分配(Stack Allocation)或标量替换(Scalar Replacement)。栈上分配主要应用于局部变量,尤其是那些在方法体内创建并很快就会被销毁的小型对象,这样可以显著减少垃圾收集的压力。 

示例:

假设有一个简单的Java方法,其中创建了一个局部变量对象,这个对象不会被方法之外的代码引用,而且它的生命周期仅限于该方法的执行期间。在这种情况下,如果JVM启用了栈上分配并且经过逃逸分析确定该对象不会逃逸,那么该对象就有可能在栈上分配。

public class StackAllocationExample {public void method() {User user = new User();// 使用user...// user仅在method方法的栈帧中存在}
}class User {String name;int age;
}

在这个例子中,User对象只在method方法内部创建和使用,如果它满足栈上分配的条件,那么它将会在栈上分配,而不是在堆上。 

(2)对象在Eden上分配

JVM的堆内存被划分为新生代和老年代。新生代又进一步分为一个Eden区和两个Survivor区(S0和S1)。Eden区是对象首次创建的地方,大部分对象在Eden区分配。

Eden区分配的特点

  • 对象创建时,首先尝试在Eden区内分配。
  • 如果Eden区没有足够的空间,或者对象太大,可能直接进入老年代。
  • 经过若干次垃圾回收(Minor GC),存活下来的对象会从Eden区晋升到Survivor区,或者直接晋升到老年代。

示例:

当一个对象创建时,默认情况下它会在Eden区内分配。Eden区是新生代的一部分,专门用于存放新创建的对象。如果对象在一次或多次垃圾回收后仍然存活,它将被移动到Survivor区,或者直接晋升到老年代。

public class EdenAllocationExample {public static void main(String[] args) {User user = new User();// 使用user...}
}class User {String name;int age;
}

在这个例子中,User对象创建在Eden区,如果它在接下来的垃圾回收过程中存活,那么它可能被晋升到Survivor区或老年代。 

(3)大对象直接进入老年代

大对象指的是需要大量连续内存空间的对象,例如大的数组或长字符串。JVM为了减少新生代的碎片化,避免频繁的Minor GC,采取了以下策略:

  • 如果对象的大小超过了一定的阈值(通常是JVM的一个参数设置),这个对象会被直接分配到老年代中。
  • 直接进入老年代可以避免新生代的频繁垃圾回收,因为大对象通常生命周期较长,不易被回收。
  • 这种策略有助于提高大对象密集型应用的性能,减少垃圾收集的次数。

(4)长期存活的对象将进入老年代

既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。为了做到这一点,虚拟机给每个对象一个对象年龄(Age)计数器。

如果对象在Eden出生并经过第一次Minor GC后仍然能够存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将对象年龄设为1。对象在Survivor中每熬过一次MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁,CMS收集器默认6岁,不同的垃圾收集器会略微有点不同),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数–XX:MaxTenuringThreshold来设置。

(5)对象动态年龄判断

对象的年龄是指对象从创建开始到被垃圾收集器回收之间所经历的Minor GC次数。JVM给每个对象分配一个年龄计数器(Age Counter)。对象在Eden区创建后,如果在第一次Minor GC后依然存活,它会被移动到一个Survivor区,并且年龄计数器会加1。此后,每次经历Minor GC并且没有被回收,年龄计数器都会递增,直到达到一定的阈值(默认为15)。当对象的年龄达到这个阈值时,它将被提升到老年代。

对象年龄的判断机制有助于将长期存活的对象及时移动到老年代,避免新生代的频繁垃圾回收,同时也减少了老年代的垃圾回收压力,因为老年代的垃圾回收(Full GC或Major GC)成本更高。

(6)老年代空间分配担保机制

在进行Minor GC之前,JVM会检查老年代是否有足够的空间来接收可能从新生代提升过来的对象。如果老年代的空间不足以担保这次Minor GC后所有存活对象的迁移,JVM将触发一次Full GC,以腾出足够的空间。这种机制被称为老年代空间分配担保(Space Allocation Guarantee),其目的是避免在新生代进行垃圾回收时出现内存不足的情况,确保Minor GC的顺利进行。

如果担保机制检测到老年代空间不足,JVM会进行如下操作:

  1. 尝试压缩老年代,回收部分空间。
  2. 如果压缩后仍然不足,将触发Full GC,清理整个堆内存,包括老年代和永久代(在JDK 8中是方法区)。
  3. 如果Full GC后仍然不足,将抛出OutOfMemoryError异常。

三、对象内存回收

堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象是没有被任何地方使用的。

(1)引用计数法

引用计数法是一种简单的内存管理策略,它通过跟踪指向一个对象的引用数量来确定对象是否可被回收。每当有一个地方引用一个对象时,它的引用计数器就会加1;当引用失效时,计数器减1。当一个对象的引用计数变为0时,表明没有任何引用指向它,此时它就可以被回收了。

优点:实现简单,运行时不需要进行额外的计算或全局性的搜索,即时性好。

缺点:无法处理循环引用的问题。如果有两个对象相互引用对方,即使它们不再被外部引用,引用计数法也无法正确识别它们为垃圾,从而导致内存泄露。

示例:

尽管Java的垃圾收集器不采用引用计数法,但我们可以通过一个类似场景的示例来说明,如果在引用计数法下,两个对象互相引用时可能导致的内存泄漏情况。下面是一个简化版的伪代码示例,用于说明这个问题:

// 注意:以下代码仅用于演示,实际上Java的垃圾收集器不使用引用计数法
class Node {private Node reference;public Node(Node ref) {this.reference = ref;}public void setReference(Node ref) {this.reference = ref;}public Node getReference() {return reference;}
}public class ReferenceCountingDemo {public static void main(String[] args) {Node nodeA = new Node(null);Node nodeB = new Node(nodeA);nodeA.setReference(nodeB);// 现在nodeA和nodeB互相引用,如果没有垃圾收集器,将导致它们都无法被回收}
}

在引用计数法中,nodeAnodeB互相引用对方,即使它们不再被任何外部引用持有,但由于它们相互之间的引用,它们的引用计数都不会降到0,因此按照引用计数法,这两个对象将永远不会被回收,导致内存泄漏。 

(2)可达性分析算法

这是JVM中常用的垃圾回收算法。它基于一个基本思想:通过一系列称为“GC Roots”的根对象作为起始点,从这些根对象向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,即可判定此对象是不可达的,即不可能再被使用。

GC Roots通常包括:

  • 正在执行的方法中声明的局部变量对象。
  • 方法调用栈中引用的对象。
  • 本地方法栈中JNI(Native方法)引用的对象。
  • Java虚拟机内部引用的对象,如基本类型的Class对象,或者常量池中的引用。

(3)常见引用类型

Java中定义了四种强度不同的引用类型,它们分别是强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。

  • 强引用是最常用的引用类型,只要强引用存在,垃圾回收器就不会回收掉对象。
  • 软引用用于描述还有用但非必需的对象。当系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收后还没有足够的内存,才会抛出内存溢出异常。
  • 弱引用比软引用的强度更弱一些,被弱引用关联的对象只能生存到下一次垃圾回收发生之前。
  • 虚引用也称为幽灵引用或幻影引用,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

(4)finalize()方法最终判定对象是否存活

在Java中,finalize()方法是Object类的一个保护方法,允许在对象被垃圾回收前做一些必要的清理工作。当垃圾回收器准备回收一个对象时,如果发现这个对象中定义了finalize()方法,就会自动调用这个方法。但是,finalize()方法的调用并不是强制的,也不保证一定会被调用,且其执行时机不确定,不应依赖它进行资源释放,应使用try-finally或try-with-resources语句进行资源的显式释放。

示例:

在Java中 finalize()方法可以用来执行一些清理工作,如关闭文件、网络连接等资源。然而,finalize()方法的调用不是强制的,且其实现细节和调用时机由JVM决定,因此不应该依赖它来确保资源的释放。下面是一个使用finalize()方法的示例:

class ResourceManagedObject {private boolean isClosed = false;protected void finalize() throws Throwable {if (!isClosed) {// 执行清理工作,例如关闭文件或网络连接System.out.println("Resource cleaned up by finalize()");isClosed = true;}super.finalize();}public void close() {// 显式关闭资源System.out.println("Resource closed explicitly");isClosed = true;}
}public class FinalizeDemo {public static void main(String[] args) throws Exception {ResourceManagedObject obj = new ResourceManagedObject();// 使用obj...// 显式关闭资源obj.close();// 删除引用,使obj成为垃圾收集的目标obj = null;System.gc(); // 请求垃圾收集// 等待垃圾收集器运行Thread.sleep(1000); // 假设垃圾收集器会在1秒内运行}
}

在这个示例中,ResourceManagedObject类实现了finalize()方法,用于在对象被垃圾收集前执行资源清理工作。然而,最佳实践是不要依赖finalize()方法,而应该在不再需要对象时显式地调用close()方法来释放资源,这是因为finalize()方法的执行是不确定的,且其执行可能带来性能上的开销。此外,从Java 9开始,finalize()方法的使用已被弃用,建议使用其他资源管理技术,如try-with-resources语句或显式的资源关闭逻辑。 

(5)如何判断一个类是无用的类

类的卸载(Class Unloading)在Java中并不常见,但在某些特定情况下,如Web容器中,可能需要卸载不再使用的类。判断一个类是否无用,通常考虑以下几点:

  • 类的所有实例都已经回收。
  • 没有任何引用指向该类的Class对象。
  • 类的加载器已经回收或可被回收。

总结

        JVM中对象的创建和内存分配是一个多步骤、多策略的过程,涉及类加载、内存布局、垃圾回收等多个层面。理解和优化这一机制对于提升Java应用程序的性能至关重要。通过合理的设计和编码实践,我们可以最大限度地发挥JVM的优势,构建高效稳定的应用系统。

相关文章:

Java核心篇之JVM探秘:对象创建与内存分配机制

系列文章目录 第一章 Java核心篇之JVM探秘&#xff1a;内存模型与管理初探 第二章 Java核心篇之JVM探秘&#xff1a;对象创建与内存分配机制 第三章 Java核心篇之JVM探秘&#xff1a;垃圾回收算法与垃圾收集器 第四章 Java核心篇之JVM调优实战&#xff1a;Arthas工具使用及…...

Nuxt框架中内置组件详解及使用指南(五)

title: Nuxt框架中内置组件详解及使用指南&#xff08;五&#xff09; date: 2024/7/10 updated: 2024/7/10 author: cmdragon excerpt: 摘要&#xff1a;本文详细介绍了Nuxt框架中和组件的使用方法与配置&#xff0c;包括安装、基本用法、属性详解、示例代码以及高级功能如…...

python开发遇到的坑汇总

文章目录 1.点击导入操作&#xff0c;所有配置全没了 1.点击导入操作&#xff0c;所有配置全没了 在 PyCharm 中&#xff0c;如果你遇到了点击导入&#xff08;import&#xff09;操作后&#xff0c;项目似乎进行了重新安装或重新部署的情况&#xff0c;这通常不是由简单的导入…...

【线性表,线性表中的顺序表和链表】

目录 1、线性表的定义和基本操作1.1、线性表的定义1.2、线性表的基本操作 2、顺序表和链表的比较2.1、顺序表2.1.1、顺序表的定义和特点2.1.2、顺序表的实现&#xff08;1&#xff09;顺序表的静态分配&#xff1a;&#xff08;2&#xff09;顺序表的动态分配 2.1.3、顺序表的基…...

46 mysql 客户端拿不到具体的错误信息

前言 这是最近碰到的一个问题 同样的一个 环境的问题, 在正常的 mysql 环境会返回 具体的错误信息, 然后 在我的另外一个环境里面 只能返回一些 unknown error 之类的 十分抽象的环境 然后 我们这里 来看一下 具体的情况 我们这里从 错误的环境 往前推导 来查看 并解决这个…...

Java语言程序设计——篇三(2)

循环结构 概述1️⃣while循环例题讲解 2️⃣do-while循环例题讲解 &#x1f6a9;while循环与do-while循环区别3️⃣for循环例题讲解 4️⃣循环的嵌套&#x1f3ee;例题讲解 概述 ⭐️Java语言提供了4种循环结构&#xff1a; (1) while循环 (2) do-while循环 (3) for循环 (4)增…...

如何实现一个分布式锁

如何实现一个分布式锁 本篇内容主要介绍如何使用 Java 语言实现一个注解式的分布式锁&#xff0c;主要是通过注解AOP 环绕通知来实现。 1. 锁注解 我们首先写一个锁的注解 /*** 分布式锁注解*/ Retention(RetentionPolicy.RUNTIME) Target({ElementType.METHOD}) Documente…...

Ajax从零到实战

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…...

编程参考 - 在C++移动构造函数声明中使用noexcept

在 C 中&#xff0c;noexcept 是用于表示函数不抛出异常的指定符。它既可用于常规函数&#xff0c;也可用于特殊成员函数&#xff0c;包括构造函数和析构函数。使用 noexcept 可以帮助编译器进行优化&#xff0c;提高代码的安全性和正确性。 In C, noexcept is a specifier use…...

Vue2/Vue3实现全局/局部添加防篡改水印的效果。删除元素无效!更改元素属性无效!支持图片、元素、视频等等。

水印目的 版权保护:水印可以在图片、文档或视频中嵌入作者、品牌或版权所有者的信息,以防止未经授权的复制、传播或使用。当其他人使用带有水印的内容时,可以追溯到原始作者或版权所有者,从而加强版权保护。 身份识别:水印可以用作作者或品牌的标识符,使观众能够轻松识…...

GuLi商城-商品服务-API-属性分组-获取分类属性分组

获取分类属性分组接口开发 操作的是这张表 造数据: 后台代码: @Override public PageUtils queryPage(Map<String, Object> params, Long catelogId) {//select * from pms_attr_group where catelog_id=? and (attr_group_id=key or attr_group_name like %key%)Stri…...

安全测试理论

安全测试理论 什么是安全测试&#xff1f; 安全测试&#xff1a;发现系统安全隐患的过程安全测试与传统测试区别 传统测试&#xff1a;发现bug为目的 安全测试&#xff1a;发现系统安全隐患什么是渗透测试 渗透测试&#xff1a;已成功入侵系统为目标的的攻击过程渗透测试与安全…...

序列化和反序列化

面试题&#xff1a;对序列化和反序列化的理解&#xff1f; 我们之所以需要序列化&#xff0c;它核心的目的是为了解决网络通信之间的对象传输的问题&#xff0c;也就是说&#xff0c;如何把当前JVM进程的一个对象&#xff0c;通过跨网络传输到另一个JVM进程里面&#xff0c;而序…...

OpenCV中使用Canny算法在图像中查找边缘

操作系统&#xff1a;ubuntu22.04OpenCV版本&#xff1a;OpenCV4.9IDE:Visual Studio Code编程语言&#xff1a;C11 算法描述 Canny算法是一种广泛应用于计算机视觉和图像处理领域中的边缘检测算法。它由John F. Canny在1986年提出&#xff0c;旨在寻找给定噪声条件下的最佳边…...

基于springboot+vue+uniapp的机电公司管理信息系统

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…...

电子期刊制作实战教程:从零开始制作

​随着互联网的普及&#xff0c;电子期刊已经成为了信息传递的重要载体。它以便捷、环保、互动性强等特点受到了越来越多人的青睐。那么&#xff0c;如何从零开始制作一份吸引人的电子期刊呢&#xff1f; 1.要制作电子杂志,首先需要选择一款适合自己的软件。比如FLBOOK在线制作…...

11.FreeRTOS_事件组

事件组概述 事件组的作用&#xff1a; 可以等待某一个事件发生可以等待若干个事件发生可以等待若干个事件中的某一个事件发生 同步点是事件组的另一个使用方式&#xff0c;它可以让多个任务进行阻塞等待&#xff0c;当全部事件完成后&#xff0c;再一起解除任务的阻塞。常常…...

Python爬虫-爬取三国演义文本数据-bs4

bs4进行数据解析 -数据解析的原理: - 1.标签定位 -2.提取标签、标签属性中存储的数据值 - bs4数据解析的原理: - 1.实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中 -2.通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取 - 环境安装: - pi…...

html5——列表、表格

目录 列表 无序列表 有序列表 自定义列表 表格 基本结构 示例 表格的跨列 表格的跨行 列表 无序列表 <ul>【声明无序列表】 <li>河间驴肉火烧</li>【声明列表项】 <li>唐山棋子烧饼</li> <li>邯郸豆沫</li> <l…...

【Python字符串攻略】:玩转文字,编织程序的叙事艺术

文章目录 &#x1f680;一.字符串基础&#x1f308;二.查看数据类型⭐三.转化❤️四.字符串索引&#x1f6b2;五.字符串切片&#x1f3ac;六.字符串切片-步长☔七.反向切片注意事项&#x1f6b2;八.字符串&#x1f4a5;查&#x1f4a5;改&#x1f4a5;删 ❤️九.字符串拼接&…...

element form表单中密码框被自动赋值,并默认背景色为白色,手动输值后背景色才是自己配置的背景色,与表单的自动填充有关

事件背景&#xff1a; 一个表单&#xff0c;有两组需要输入密码的地方&#xff0c;两组都被填充用户名密码&#xff0c;其中一组是其他信息&#xff0c;不是用户名密码&#xff0c;也被填充了&#xff0c;且input背景色是白色&#xff0c;表单中的input已经手动配置为无背景色&…...

【UE5.1 角色练习】15-枪械射击——子弹发射物

目录 效果 步骤 一、创建并发射子弹 二、优化子弹 效果 步骤 一、创建并发射子弹 1. 在前面的文章中&#xff08;【UE5.1 角色练习】06-角色发射火球-part1&#xff09;我们创建了蓝图“BP_Skill_FireBall” 这里我们复制一份命名为“BP_Ammo_5mm”&#xff0c;用于表示…...

Zynq7000系列FPGA中的DMA控制器的编程限制

有关DMAC编程时适用的限制信息&#xff0c;有四个考虑因素&#xff1a; 固定非对齐突发Endian swap size restrictions&#xff1a;在数据传输或处理过程中&#xff0c;不同字节序&#xff08;Endian&#xff09;之间的转换和对应的限制在DMA周期内更新通道控制寄存器当MFIFO满…...

超简易高效的 AI绘图工具—与sd-webui一致界面,6G显存最高提升75%出图速率!(附安装包)

大家好&#xff0c;我是灵魂画师向阳 今天给大家分享一个基于Stable Diffusion WebUI 构建的AI绘图工具—sd-webui-forge&#xff0c;该工具的目标在于简化插件开发&#xff0c;优化资源管理&#xff0c;加速推理。 Forge承诺永远不会对Stable Diffusion WebUI用户界面添加不…...

ArduPilot开源代码之OpticalFlow_backend

ArduPilot开源代码之OpticalFlow_backend 1. 源由2. Library设计3. 重要例程3.1 OpticalFlow_backend::_update_frontend3.2 OpticalFlow_backend::_applyYaw 4. 总结5. 参考资料 1. 源由 光流计是一种低成本定位传感器&#xff0c;所有的光流计设备传感驱动代码抽象公共部分统…...

设计模式探索:适配器模式

1. 适配器模式介绍 1.1 适配器模式介绍 适配器模式&#xff08;adapter pattern&#xff09;的原始定义是&#xff1a;将一个类的接口转换为客户期望的另一个接口&#xff0c;适配器可以让不兼容的两个类一起协同工作。 适配器模式的主要作用是把原本不兼容的接口&#xff0c…...

OpenCV 寻找棋盘格角点及绘制

目录 一、概念 二、代码 2.1实现步骤 2.2完整代码 三、实现效果 一、概念 寻找棋盘格角点&#xff08;Checkerboard Corners&#xff09;是计算机视觉中相机标定&#xff08;Camera Calibration&#xff09;过程的重要步骤。 OpenCV 提供了函数 cv2.findChessboardCorners…...

【深度学习】PyTorch深度学习笔记02-线性模型

1. 监督学习 2. 数据集的划分 3. 平均平方误差MSE 4. 线性模型Linear Model - y x * w 用穷举法确定线性模型的参数 import numpy as np import matplotlib.pyplot as pltx_data [1.0, 2.0, 3.0] y_data [2.0, 4.0, 6.0]def forward(x):return x * wdef loss(x, y):y_pred…...

10.FreeRTOS_互斥量

互斥量概述 在博文“ FreeRTOS_信号量 ”中&#xff0c;使用了二进制信号量实现了互斥&#xff0c;保护了串口资源。博文链接如下&#xff1a; FreeRTOS_信号量-CSDN博客 但还是要引入互斥量的概念。互斥量与二进制信号量相比&#xff0c;能够多实现如下两个功能&#xff1a…...

EtherCAT总线冗余让制造更安全更可靠更智能

冗余定义 什么是总线冗余功能&#xff1f;我们都知道&#xff0c;EtherCAT现场总线具有灵活的拓扑结构&#xff0c;设备间支持线型、星型、树型的连接方式&#xff0c;其中线型结构简单、传输效率高&#xff0c;大多数的现场应用中也是使用这种连接方式&#xff0c;如下图所示…...