【Java JVM】对象回收判断

Java 对象回收判断是程序设计中至关重要的一环。在面向对象的编程中, 合理的对象回收策略直接影响着程序的性能和内存利用效率。
因此, 深入了解和准确判断 Java 对象的回收时机, 不仅可以优化程序的运行性能, 还能有效避免内存泄漏和资源浪费。
本文将简单的分析一下 JVM 中对象回收的判断机制, 了解一下整体的对象回收过程。

1 对象回收算法

在 JVM 中, 如果一个对象不被任何对象所引用的话, 那么这个对象就是可回收对象。
那么如何判断一个对象是可回收的话, 现在常用的有 2 种方式。

1.1 引用计数算法 (Reference Counting)

在对象中添加一个引用计数器, 每当有一个地方引用它时, 计数器值就加一, 当引用断开时, 计数器值就减一。
任何时刻计数器为零的对象就是不可能再被使用的, 可以判定为可以回收的对象。

优点: 实现简单, 判定的效率也很高
缺点: 需要占用一下额外的内存空间, 很多复杂的情景没有考虑, 很难解决对象之间相互循环引用的问题。

比如:

Obj a = new Obj();
Obj b = new Obj();a.attr = b;
b.attr = a;

上面 2 个对象实际已经没有作用了, 但是互相持有对方的引用, 导致他们的引用计数不为 0, 无法回收。
所以在主流的 Java 虚拟机中没有选用引用计数法作用内存管理的方式。

1.2 可达性分析算法 (Reachability Analysis)

先人为地将一批对象设为根节点, 作为起始节点, 从这些起始节点开始, 根据对象间的引用关系向下寻找其他的对象, 通过这些引用关系找到的对象就是需要的对象, 不可以回收。
同理如果某个对象跟这些根节点间没有任何直接或间接的引用关系, 则证明此对象是不可能再被使用, 可以回收的。

Alt '可达性分析算法过程'

如图: 从 GC Root 出发, 可以依次找到 obj1, obj2, obj3, 所以它们属于不可回收对象,
而 obj4, obj5, obj6 之间虽然有引用关系, 但是没有和 GC Root 相同的链路, 所以为可回收对象。

2 GC Roots

在上面可达性分析算法的介绍中, 整个算法的前提的需要先设定一批根节点, 而在 JVM 中这些根节点被称为 “GC Roots”。
官方的定义如下:

A pointer into the Java object heap from outside the heap. 
These come up, e.g., from static fields of classes, local references in activation frames, etc.从堆外部指向 Java 对象堆的指针。例如: 类的静态字段, 激活帧中的局部引用等。

那么具体哪些对象是可以作为 “GC Roots” 呢?
网上的说法有很多 (暂时未找到官方的定义), 但是大体的方向是一样的, 在细节上有些不同而已。

《深入理解java虚拟机》 中对 GC Roots 的分类如下:

  • 在虚拟机栈中引用的对象, 例如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等
  • 在方法区中类静态属性引用的对象, 例如 Java 类的引用类型静态变量
  • 在方法区中常量引用的对象, 例如字符串常量池 (String Table) 里的引用
  • 本地方法栈中 JNI (即 Native 方法) 引用的对象
  • Java 虚拟机内部的引用, 如基本数据类型对应的 Class 对象、常驻异常对象 (如 NullPointException 等)、系统类加载器
  • 所有被同步锁持有的对象
  • 反映 Java 虚拟机内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等

Java 语言里, 可作为 GC Roots 对象的包括如下几种

  • 虚拟机栈 (栈帧中的本地变量表) 引用的对象
  • 方法区中的类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中 JNI (即 Native 方法) 引用的对象

Eclipse Memory Analyzer (MAT) 文章 中对 GC Roots 的分类如下:

System ClassClass loaded by bootstrap/system class loader. For example, everything from the rt.jar like java.util.* .JNI LocalLocal variable in native code, such as user defined JNI code or JVM internal code.JNI GlobalGlobal variable in native code, such as user defined JNI code or JVM internal code.Thread BlockObject referred to from a currently active thread block.ThreadA started, but not stopped, thread.Busy MonitorEverything that has called wait() or notify() or that is synchronized. For example, by calling synchronized(Object) or by entering a synchronized method. Static method means class, non-static method means object.Java LocalLocal variable. For example, input parameters or locally created objects of methods that are still in the stack of a thread.Native StackIn or out parameters in native code, such as user defined JNI code or JVM internal code. This is often the case as many methods have native parts and the objects handled as method parameters become GC roots. For example, parameters used for file/network I/O methods or reflection.FinalizableAn object which is in a queue awaiting its finalizer to be run.UnfinalizedAn object which has a finalize method, but has not been finalized and is not yet on the finalizer queue.UnreachableAn object which is unreachable from any other root, but has been marked as a root by MAT to retain objects which otherwise would not be included in the analysis.Java Stack FrameA Java stack frame, holding local variables. Only generated when the dump is parsed with the preference set to treat Java stack frames as objects.UnknownAn object of unknown root type. Some dumps, such as IBM Portable Heap Dump files, do not have root information. For these dumps the MAT parser marks objects which are have no inbound references or are unreachable from any other root as roots of this type. This ensures that MAT retains all the objects in the dump.

各种说法, 但是大体的方向是一样的, 就是细节的区分而已。

3 可达性分析算法在 JVM 中的大体步骤

总体的步骤就 2 步:

  1. 根节点枚举 (GC Roots Enumeration)
  2. 引用链查询标记

3.1 根节点枚举

整个可达性分析的过程, 第一步就是先确定哪些对象的是 GC Roots, 也就是根节点枚举 (有了 GC Roots 就能通过引用链查找可回收对象了)。

从上面列举的可作为 GC Roots 的对象列表可知, 可以作为 GC Roots 的主要在全局性的引用 (例如常量或类静态属性) 与执行上下文 (例如栈帧中的本地变量表) 中。
但是

  1. 尽管目标明确, 但是在程序实际运行中, 真正的 GC Roots 集合会随着程序的运行变更的
  2. 根节点枚举期间要求在一个能保障一致性的快照中才得以进行, 这里的一致性指定是: 在分析过程, 根节点集合的对象引用关系不会发生变化, 所以需要暂停所有的线程

所以在根节点枚举的过程, 如果一个个的遍历所有符合条件的对象, 将是一个耗时的过程。
现在主流的 Java 虚拟机使用了一组称为 OopMap 的数据结构来达到优化查找的过程, 避免一个不漏的检查所有的的对象。
通过扫描 OopMap 存储的引用类型的指针, 也就是 GC Roots 集合, 就能通过引用链找到存活的对象。

注: 现在所有的收集器, 在 GC Roots 这一步骤时都是必须暂停用户线程的, 也就是 “Stop the world”, 而耗时更长的查找引用链的过程已经可以做到与用户线程一起并发。

3.1.1 OopMap 是怎么样工作的

在上面的分析中, 可以知道 GC Roots 的主要在全局性的引用与执行上下文中, 对这个结论在更具体的说明, GC Roots 主要存在于

  1. 方法区中类的常量和静态属性
  2. Java 虚拟机栈中的本地变量表记录的引用对象
  3. 本地方法栈中 JNI (即 Native 方法) 引用的对象
3.1.1.1 方法区中类的常量和静态属性

在 HotSpot 中, 对象的类型信息 (即 Klass 对象, Java 的 .class 文件加载到 JVM 中就会形成一个 Klass 对象) 里有记录自己的 OopMap,
记录了在该类型的对象内什么偏移量上是什么类型的数据, 这些数据是在类加载过程中计算得到的, 后续直接从这个类型对象开始向外的扫描即可。

所以, 方法区内的静态属性引用, 常量, 这些不太会改变的 GC Roots 会在类加载成功后, 就确定好了。

3.1.1.2 Java 虚拟机栈中的本地变量表记录的引用对象

对于虚拟机方法栈的 GC Roots 的话, 则是这样的:
每个被 JIT 编译过后的方法会在一些特定的位置更新这个方法栈帧的 OopMap。 记录执行到这个位置时, 方法栈上和寄存器里哪些位置是引用。

上面说的特定的位置主要在:

  1. 循环的末尾
  2. 方法临返回前
  3. 调用方法的 call 指令后面
  4. 可能抛异常的位置

这种位置被称为 “安全点” (safe point)。

安全点的选定标准

  1. 不能太少以至于让收集器等待时间过长 (太少, 2 个安全点之间的间隔会变大, 也就是 2 个安全点之间达到的时间也会变大)
  2. 不能太过频繁以至于过分增大运行时的内存负荷

所以选用一些比较关键的位置来记录就能有效的缩小需要记录的数据量, 但仍然能达到区分引用的目的。
同时, HotSpot 中进行 GC 也不是在任意位置都可以进入, 同样也是需要所有线程达到 safe point 处才会 GC, 所以选择这些位置基本足够记录完整的 OopMap

通过上面的分析, 可以知道安全点的作用:
线程执行到 安全点

  1. 更新这个方法栈帧的 OopMap
  2. 如果当前正在 GC, 当前线程进行挂起

至于如何让线程在 安全点 时挂起的, 在附录中再简单分析。

安全点的优化点
上面提到安全点的位置中有一个是循环的末尾, HotSpot 虚拟机为了避免安全点过多带来过重的负担, 对循环还做了一项优化措施:
认为循环次数较少的话, 执行时间应该也不会太长, 所以使用 int 类型或范围更小的数据类型作为索引值的循环默认是不会被放置安全点的, 这种优化被称为: 可数循环 (Counted Loop)。
相应的, 使用 long 或者范围更大的数据类型作为索引值的循环就被称为不可数循环 (Uncouted Loop), 将会被放置安全点。

但是循环执行的次数少, 并不是代表了循环执行的时间短, 如果每次执行里面的操作很耗时, 可能会导致线程执行到安全点需要消耗很多时间。
HotSpot 原本提供了 -XX:+UseCountedLoopSafepoints 参数去强制在可数循环中也放置安全点, 不过这个参数在 JDK8 下有 Bug。

3.1.1.3 本地方法栈中 JNI (即 Native 方法) 引用的对象

对 Java 线程中的 JNI 方法, 它们既不是由 JVM 里的解释器执行的, 也不是由 JVM 的 JIT 编译器生成的, 所以会缺少OopMap 信息。那么GC碰到这样的栈帧该如何维持准确性呢?

HotSpot 的解决方法是: 所有经过 JNI 调用边界 (调用 JNI 方法传入的参数, 从 JNI 方法传回的返回值) 的引用都必须用 “句柄” (handle) 包装起来, JNI 需要调用 Java API 的时候也必须自己用句柄包装指针。
在这种实现中, JNI 方法里写的 “jobject” 实际上不是直接指向对象的指针, 而是先指向一个句柄, 通过句柄才能间接访问到对象。
这样在扫描到 JNI 方法的时候就不需要扫描它的栈帧了 – 只要扫描句柄表就可以得到所有从 JNI 方法能访问到的 GC 堆里的对象。
但这也就意味着调用 JNI 方法会有句柄的包装/拆包装的开销, 是导致 JNI 方法的调用比较慢的原因之一。

Java 虚拟机栈中的本地变量表记录的引用对象

所以, 方法区内的静态属性引用, 常量, 这些不太会改变的 GC Roots 会在类加载成功后, 就确定好了。

对于方法栈的 GC Roots 的话, 则是这样的:
每个被 JIT 编译过后的方法会在一些特定的位置更新这个方法栈帧的 OopMap。 记录执行到这个位置时, 方法栈上和寄存器里哪些位置是引用。

3.2 引用链查询标记

通过根节点枚举, 得到当前应用所有的 GC Roots, 接下来通过这些 GC Roots 的引用, 就能确定出当前哪些对象是不可回收, 哪些是可回收的了。
但是根据引用链 (引用关系) 标记可回收对象, 随着堆的增大, 整个过程会随之增长。
为了解决这个问题, JVM 的解决方案是让用户线程和垃圾收集器并发并行, 但是并发的过程, 存在修改引用链的情况, 导致标记的结果不正确。

为了这个问题, 引入了 “三色标记 (Tri-color Marking)” 作为补助手段, 把遍历对象过程中遇到的对象, 按照 “是否访问过” 这个条件标记成以下三种颜色

白色: 对象尚未被垃圾收集器访问过。在可达性分析刚刚开始的阶段, 所有的对象都是白色的, 若在分析结束的阶段, 仍然是白色的对象, 即代表不可达, 对象可回收
黑色: 对象已经被垃圾收集器访问过, 且这个对象的所有引用都已经扫描过。黑色的对象代表已经扫描过, 它是安全存活的, 如果有其他对象引用指向了黑色对象, 无须重新扫描一遍。
黑色对象不可能直接 (不经过灰色对象) 指向某个白色对象
灰色: 对象已经被垃圾收集器访问过, 但这个对象上至少存在一个引用还没有被扫描过

标记的过程, 基本就是从根节点, 找到下一个引用, 标记为黑色或者灰色, 如果为黑色, 这里的引用结束了。如果为灰色, 从这个节点向下继续标记。

一般情况是这样的
Alt '三色标记法正常过程'

但是扫描的过程中, 用户线程与收集器是并发工作的过程, 收集器在对象标记了颜色, 同时用户线程在修改引用关系, 这会导致

  1. 把原本消亡的对象错误标记为存活, 这种情况, 是可以容忍的, 只不过产生了一点逃过本次收集的浮动垃圾而已, 下次收集清理掉就好。
  2. 把原本存活的对象错误标记为已消亡, 这种情况的话, 则会导致我们程序的异常了。
3.2.1 标记过程中的特殊情况
3.2.1.1 情况一: 多标了

假设已经遍历到 E (变为灰色了), 此时应用程序执行了 D.E = null (D -> E 的引用断开):

Alt '三色标记法多标过程'

此时, 对象 E/F/G 是 “应该” 被回收的。但是因为 E 已经变为灰色了, 其仍会被当作存活对象继续遍历下去。
最终的结果是: 这部分对象仍会被标记为存活, 即本轮 GC 不会回收这部分内存。
这部分本应该回收, 但是没有回收到的内存, 被称之为 “浮动垃圾(Floating Garbage)”。

另外, 在并发标记开始后, 创建的新的对象, 通常的做法是直接全部当成黑色, 本轮不会进行清除。
这部分对象期间可能会变为垃圾, 这也算是浮动垃圾的一部分。

3.2.1.2 情况二: 漏标了

假设已经遍历到 E (变为灰色了), 此时应用程序执行了

G g = E.g;
E.g = null;
D.g = g;

Alt '三色标记法漏标过程'

GC 线程继续跑, 因为 E 已经没有对 G 的引用了, 所以不会将 G 放到灰色集合, 尽管因为 D 重新引用了 G, 但因为 D 已经是黑色了, 不会再重新做遍历处理。
最终导致的结果是:G 会一直停留在白色集合中, 最后被当作垃圾进行清除。

但是通过分析上面的过程, 可以发现出现对象漏标的话, 需要同时满足 2 个条件

  1. 灰色对象断开了白色对象的直接或间接引用
  2. 黑色对象重新引用了该白色对象

因此, 我们要解决并发扫描时的对象消失问题, 只需破坏这两个条件的任意一个即可。
由此分别产生了两种解决方案: 原始快照 (Snapshot At The Beginning, SATB) 和增量更新 (Incremental Update)。

3.2.3 写屏障 (Writer Barrier)

原始快照和增量更新两种解决方案都是基于写屏障实现的, 具体是怎么样呢?

给某个对象的成员变量赋值时, 其底层代码大概长这样:

void oop_field_store(oop* field, oop new_value) { // 赋值操作*field = new_value; 
}

这里的写屏障不是解决并发的读写屏障, 看作在虚拟机层面对 “引用类型字段赋值” 这个动作的 AOP 切面, 在引用对象赋值时会产生一个环形 (Around) 通知,
供程序执行额外的动作, 也就是说赋值的前后都在写屏障的覆盖范畴内。
在赋值前的部分的写屏障叫作写前屏障 (Pre-Write Barrier), 在赋值后的则叫作写后屏障 (Post-Write Barrier)。

void oop_field_store(oop* field, oop new_value) {  // 写屏障-写前操作pre_write_barrier(field); // 赋值操作*field = new_value; // // 写屏障-写后操作post_write_barrier(field, value);
}

HotSpot 虚拟机只用到了写后屏障

3.2.3 漏标情况的解决
3.2.3.1 原始快照 (Snapshot At The Beginning, SATB)

原始快照要破坏的是第一个条件, 当灰色对象要删除指向白色对象的引用关系时, 就将这个要删除的引用记录下来, 在并发扫描结束之后, 再将这些记录过的
引用关系, 标记为灰色, 然后以这些灰色对象为根, 重新扫描一次。
这可以简化理解为, 无论引用关系删除与否, 都会按照刚刚开始扫描那一刻的对象图快照来进行搜索。

因为从灰色对象删除指向白色对象的引用关系时, 就把这个引用记录下来。那么存在的确是要删除这个引用关系的, 后续不在会对其做任何变更了, 那么重新把
这个引用当做灰色对象, 就会造成 “浮动垃圾”。

在 HotSpot 中, G1 和 Shenandoah 则是用原始快照来实现。

3.2.3.2 增量更新 (Incremental Update)

增量更新要破坏的是第二个条件, 当黑色对象插入新的指向白色对象的引用关系时, 就将这个新插入的引用记录下来, 等并发扫描结束之后, 再将这些记录过的
引用关系为根, 重新扫描一次。
这可以简化理解为, 黑色对象一旦新插入了指向白色对象的引用之后, 它就变回灰色对象了。

在 HotSpot 中, CMS 是基于增量更新来做并发标记的。

2 种方式, 都是通过写屏障实现的。

4 finalize 让对象再活一次

理论上通过可达性分析算法, 可以判定出一个对象是否可以回收。
可回收的对象会在最后执行一次他的 finalize 方法, 可以通过这个方法让这个对象再次活一次。

判定一个对象是否需要回收, 可以实际需要经过 2 次标记

  1. 对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链, 那它将会被第一次标记
  2. 在 1 的基础上, 再进行一次筛选, 筛选的条件是此对象是否有必要执行 finalize() 方法, 对象没有覆盖 finalize() 方法, 或者 finalize() 方法
    已经被虚拟机调用过 (每个对象的 finalize 方法只会被执行一次), 那么虚拟机将这两种情况都视为 “没有必要执行”, 标记为可回收

如果对象判定为需要执行 finalize 方法, 该对象将会先被放置在一个名为 F-Queue 的队列, 并在稍后由一条由虚拟机自动建立的, 低调度优先级
的 Finalizer 线程去执行它们的 finalize 方法 (虚拟机会触发这个方法开始运行, 但不承诺一定会等待它运行结束, 如果某个对象的 finalize方法执行缓慢,
或者死循环等, 这会导致 F-Queue 队列的对象消除的很慢 / 一直处于等待, 最终可能导致系统崩溃)。

对象可以在 finalize 让自己不被回收。
收集器将对 F-Queue 中的对象进行第二次小规模的标记时, 判定需要执行 finalize 方法。只要对象将自己和引用链上的任意一个对象进行关联, 比如把
自己 (this 关键字) 赋值给某个类变量或者对象的成员变量, 那在第二次标记时它将被移出 “可回收” 的集合。

5 附录: 线程挂起

对于安全点, 另外一个需要考虑的问题是, 如何在垃圾收集发生时让所有线程 (这里其实不包括执行 JNI 调用的线程) 都跑到最近的安全点, 然后停顿下来。

这里有两种方案可供选择: 抢先式中断 (Preemptive Suspension) 和主动式中断 (Voluntary Suspension) 。

抢先式中断不需要线程的执行代码主动去配合, 在垃圾收集发生时, 系统首先把所有用户线程全部中断, 如果发现有用户线程中断的地方不在安全点上, 就恢复这条线程执行, 让它一会再重新中断, 直到跑到安全点上。
现在几乎没有虚拟机实现采用抢先式中断来暂停线程响应 GC 事件

主动式中断的思想是当垃圾收集需要中断线程的时候, 不直接对线程操作, 仅仅简单地设置一个标志位, 各个线程执行过程时会不停地主动去轮询这个标志, 一旦发现中断标志为真时, 就在自己最近的安全点上主动中断挂起。

5.1.3 安全区域 (Safe Region)

安全点机制保证了程序执行时, 在不太长的时间内就会遇到可进入垃圾收集过程的安全点。
但是线程还有可能存在不在执行的状态, 比如 Sleep 状态或者 Blocked 状态, 这些线程无法走到安全点的位置, 然后中断挂起自己。

而虚拟机也不会等待这些线程唤醒然后执行的, 这种情况虚拟机通过 “安全区域” 的方式进行解决。
安全区域是指能够确保在某一段代码片段之中, 引用关系不会发生变化, 因此, 在这个区域中任意地方开始垃圾收集都是安全的。

当用户线程执行到安全区域里面的代码时, 首先会标识自己已经进入了安全区域, 那样当这段时间里虚拟机要发起垃圾收集时就不必去管这些已声明自己在安全区域内的线程了。
当线程要离开安全区域时, 它要检查虚拟机是否已经完成了根节点枚举

如果完成了, 那线程就当作没事发生过, 继续执行
否则它就必须一直等待, 直到收到可以离开安全区域的信号为止

可以认为: 可以使线程挂起的代码, 就是在安全区域中。

另一种情况: 当一个线程在执行 native 方法时, 由于此时该线程在执行 JVM 管理之外的代码, 不能对 JVM 的执行状态做任何修改, 因而 JVM要 进入 safe point 不需要关心它。
所以也可以把正在执行 native 函数的线程看作 “已经进入了 safe point”, 或者把这种情况叫做 “在 safe-region 里”。

6 参考

《深入理解Java虚拟机》- 周志明
找出栈上的指针/引用
JVM系列十六(三色标记法与读写屏障).

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

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

相关文章

JRT缓存协议测试

JRT由DolerGet提供可信的缓存数据获取,在OMR修改和删除数据后会更新缓存的数据,这样的话本Web下通过DolerGet取的数据是可信的。在多个Web之间要保证缓存数据的可信度,需要同步修改的数据到其他Web,为此仿照了缓存协议的效果&…

MySQL的三大范式

文章目录 简介第一范式第二范式第三范式: 简介 在MySQL的使用中, 要根据实际灵活设计表,一般来说我们通常遵循三大范式(啥是范式:是一些约束、规范、规则, 来优化数据库表的设计和存储),三大范…

Stable Diffusion 模型分享:3D Animation Diffusion(3D动漫)

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八 下载地址 模型介绍 3D Animation Diffusion 是 Lykon 大神的 3D 动漫模型。 作者述:在迪士尼、皮…

停止Tomcat服务的方式

运行脚本文件停止 运行Tomcat的bin目录中提供的停止服务的脚本文件 关闭命令 # sh方式 sh shutdown.sh# ./方式 ./shutdown.sh操作步骤 运行结束进程停止 查看Tomcat进程,获得进程id kill进程命令 # 执行命令结束进程 kill -9 65358 操作步骤 注意 kill命令是…

ffmpeg使用vaapi解码后的视频如何基于x11或EGL实现0-copy渲染?

技术背景 对于ffmpeg硬解码后渲染常见的做法是解码后通过av_hwframe_transfer_data方法将数据从GPU拷贝到CPU,然后做一些转换处理用opengl渲染,必然涉及到譬如类似glTexImage2D的函数将数据上传到GPU。而这样2次copy就会导致CPU的使用率变高&#xff0c…

智能物联时代下RFID技术在汽车零部件智能制造中的引领作用

RFID(Radio Frequency Identification,射频识别)技术在汽车零部件加工中有广泛的应用,其工作原理是通过无线电频率进行自动识别。在汽车零部件加工中,RFID技术可以发挥重要作用,提高生产效率、降低成本和减…

js形参传递特殊字符

在前端我们给其他页面传值或者传数据到后台的时候,字符串经常将一些特殊符号识别成字符集。这种情况下会将数据打断或者打乱,比如字符串里面包含*/&这些符号的时候就会错误。 我们可以通过将字符中的特殊字符替换成十六进制的字符,一些特…

【Linux从青铜到王者】进程信号

——————————————————————————————————————————— 信号入门 在了解信号之前有许多要理解的相关概念 我们可以先通过一个生活例子来初步认识一下信号 1.生活角度的信号 你在网上买了很多件商品,再等待不同商品快递的到来…

从事测绘地信,你需要这些插件、软件、小工具、图源...

特别声明,本篇是来自公众号GIS前沿的资源,看着比较好,特别给大家推荐。加粗样式 今天,我们又来汇总了一些工作中实用的插件、小工具、数据等等,小助手又来帮你提高工作效率了****。 因为小助手每年都会总结一次&…

15.Django总结

文章目录 1.Django创建项目的命令2.MVC,MVT的理解3.Django中间件的使用4.WSGI,uWSGI服务器 和 uwsgi协议5.nginx和uWISG 服务器之间如何配合工作的6.django开发中数据库做过什么优化7.Python中三大框架各自的应用场景8.django如何提升性能(高并发)9. 什么是restful api谈谈你的…

MySQL性能优化-数据库调优有哪些维度可以选择

数据库调优的目标 简单来说,数据库调优的目的就是要让数据库运行得更快,也就是说响应的时间更快,吞吐量更大。 不过随着用户量的不断增加,以及应用程序复杂度的提升,我们很难用“更快”去定义数据库调优的目标&#…

Stable Diffusion ———LDM、SD 1.0, 1.5, 2.0、SDXL、SDXL-Turbo等版本之间关系现原理详解

前言 2021年5月,OpenAI发表了《扩散模型超越GANs》的文章,标志着扩散模型(Diffusion Models,DM)在图像生成领域开始超越传统的GAN模型,进一步推动了DM的应用。 然而,早期的DM直接作用于像素空…

cmd模式下启动mysql

1.打开cmd输入services.msc,找到MYSQL,右击属性,找到可执行文件路径,加载到环境变量。 2.打开cmd,启动MYSQL:输入net start mysql; 3.登陆MYSQL,需要管理权限; 输入:my…

Docker容器化解决方案

什么是Docker? Docker是一个构建在LXC之上,基于进程容器的轻量级VM解决方案,实现了一种应用程序级别的资源隔离及配额。Docker起源于PaaS提供商dotCloud 基于go语言开发,遵从Apache2.0开源协议。 Docker 自开源后受到广泛的关注和…

数据链路层----滑动窗口协议的相关计算

目录 1.窗口大小的相关计算 •停等协议: •后退N帧协议: •选择重传协议: 2.信道利用率相关计算 •停等协议的信道利用率: •连续ARQ(后退N帧协议,选择重传协议)的信道利用率:…

工具函数模板题(蓝桥杯 C++ 代码 注解)

目录 一、Vector容器: 二、Queue队列 三、Map映射 四、题目(快递分拣 vector): 代码: 五、题目(CLZ银行问题 queue): 代码: 六、题目(费里的语言 map&…

每日学习总结20240301

20240301 1. strchr VS strrchr strchr和strrchr是C语言标准库中的字符串处理函数,用于在字符串中查找特定字符的位置。 1.1 strchr函数 strchr函数用于在字符串中查找第一次出现指定字符的位置,并返回该位置的指针。函数原型如下: char…

你都了解2024程序员拿捏高薪的新方式吗?

2024年,程序员该如何拿高薪呢? 道理是这么讲,那我们到底应该如何去践行呢?以我自身的经验来看,网络接单无疑是我们程序员来钱最快的方式,既可以做到兼顾本职工作和快点搞钱,又可以充分利用好每一…

信息安全系列04-安全启动介绍

本文框架 1. 基本概念1.1 基本概念回顾1.2 数字签名及验签流程 2. 安全启动实施2.1 信任根选择2.1.1 使用HSM作为信任根2.1.2 使用最底层Bootloader作为信任根 2.2 校验方法确认2.2.1 基于非对称加密算法(数字签名)2.2.2 基于对称加密算法 2.3 安全启动方…

llvm AST consumer 示例

示例源码 Makefile LLVM_CONFIG ? llvm-config #CXX : clang ifndef VERBOSE QUIET : endifSRC_DIR ? $(PWD) LDFLAGS $(shell $(LLVM_CONFIG) --ldflags) COMMON_FLAGS -Wall -Wextra CXXFLAGS $(COMMON_FLAGS) $(shell $(LLVM_CONFIG) --cxxflags) LCXX :$(shell $(L…