Unity GC + C# GC + Lua GC原理

Unity垃圾回收原理

参考文章:垃圾回收 (计算机科学) - 维基百科,自由的百科全书 (wikipedia.org)

在计算机科学中,垃圾回收(英语:Garbage Collection,缩写为GC)是指一种自动的存储器管理机制。当某个程序占用的一部分内存空间不再被这个程序访问时,这个程序会借助垃圾回收算法向操作系统归还这部分内存空间。垃圾回收器可以减轻程序员的负担,也减少程序中的错误。

原理

垃圾回收器有两个基本的原理:

  1. 考虑某个对象在未来的程序执行中,将不会被访问。
  2. 回收这些对象所占用的存储器。

分类

收集器实现

引用计数收集器

主条目:引用计数

最早的也是最简单的垃圾回收实现方法,这种方法为占用物理空间的对象附加一个计数器,当有其他对象引用这个对象时计数器加一,反之引用解除时减一。这种算法会定期检查尚未被回收的对象的计数器,为零的话则回收其所占物理空间,因为此时的对象已经无法访问。这种方法无法回收循环引用的存储对象。

跟踪收集器

主条目:追踪垃圾回收

近现代的垃圾回收实现方法,这种算法会定期遍历它管理的内存空间,从若干根储存对象开始查找与之相关的存储对象,然后标记其余的没有关联的存储对象,最后回收这些没有关联的存储对象占用的内存空间。

回收算法

基于其标记和回收行为,又分为若干细致方法。

标记-清除

先暂停整个程序的全部运行线程,让回收线程以单线程进行扫描标记,并进行直接清除回收,然后回收完成后,恢复运行线程。这样会产生大量的空闲空间碎片,和使大容量对象不容易获得连续的内存空间,而造成空间浪费。

标记-压缩

和“标记-清除”相似,不同的是,回收期间同时会将保留的存储对象搬运汇集到连续的内存空间,从而整合空闲空间,避免内存碎片化。

复制

需要程序将所拥有的内存空间分成两个部分。程序运行所需的存储对象先存储在其中一个分区(定义为“分区0”)。同样暂停整个程序的全部运行线程,进行标记后,回收期间将保留的存储对象搬运汇集到另一个分区(定义为“分区1”),完成回收,程序在本次回收后将接下来产生的存储对象会存储到“分区1”。在下一次回收时,两个分区的角色对调。[3]

这种方式非常简单,但是因为只有一个“半空间”(semi-space)被用于分配对象,内存使用相较于其他算法是其两倍。这种技术也叫做“停止并复制”。Cheney算法是改进的半空间分配器。

增量回收器

需要程序将所拥有的内存空间分成若干分区。程序运行所需的存储对象会分布在这些分区中,每次只对其中一个分区进行回收操作,从而避免暂停所有正在运行的线程来进行回收,允许部分线程在不影响回收行为下保持运行,并且降低回收时间,增加程序响应速度。

分代

由于“复制”算法对于存活时间长,大容量的储存对象需要耗费更多的移动时间,和存在储存对象的存活时间的差异。需要程序将所拥有的内存空间分成若干分区,并标记为年轻代空间和年老代空间。程序运行所需的存储对象会先存放在年轻代分区,年轻代分区会较为频密进行较为激进垃圾回收行为,每次回收完成幸存的存储对象内的寿命计数器加一。当年轻代分区存储对象的寿命计数器达到一定阈值或存储对象的占用空间超过一定阈值时,则被移动到年老代空间,年老代空间会较少运行垃圾回收行为。一般情况下,还有永久代的空间,用于涉及程序整个运行生命周期的对象存储,例如运行代码、数据常量等,该空间通常不进行垃圾回收的操作。 通过分代,存活在局限域,小容量,寿命短的存储对象会被快速回收;存活在全局域,大容量,寿命长的存储对象就较少被回收行为处理干扰。

现今的GC(如Java和.NET)使用分代收集(generation collection),依照对象存活时间的长短使用不同的垃圾收集算法,以达到最好的收集性能。

原文链接:Unity GC 学习总结 - 知乎 (zhihu.com)

什么是GC

总所周知,内存是程序运行时所需要的重要资源,在程序运行时往往需要内存来临时存储各种数据,但是操作系统提供给进程的堆内存(注意是堆内存,栈上的内存会随函数调用自动被回收,下文提及的都是指堆内存)是有限的,所以我们需要对这有限的资源进行管理

在代码中,我们会反复地申请内存来完成各种计算,等到确认内存不需要使用时,我们就会归还这部分内存,从而可以将其用于其他地方。GC所做的事情,就是自动确定那些不需要的内存,或者说 Garbage ,然后将其归还。这样开发者就无需关心内存的管理。

GC的实现

实现GC的策略有很多种,其中最常见一种就是 Tracing garbage collection,或者叫 Mark-Sweep,这种算法会通过一个 root Object,遍历这个该对象引用的变量,并且标记,递归这个过程,这样就确定了所有reachable的对象,剩下的对象即视为garbage。

另一种常见的策略还有引用计数(Reference counting),它是通过为每个对象维护一个引用计数,这代表当前对该对象的引用数目,当引用为0,即代表该对象为 Garage。引用技术有如下缺点

  • 循环引用问题
  • 保存计数带来的空间开销
  • 修改引用数目带来的速度开销以及原子性要求
  • 非实时(一个引用的变化可能递归得导致一系列引用修改,内存释放)

有很多算法可以一定程度解决上述问题,顺便一提,C++使用的智能指针即是基于引用计数实现的,COM对象也使用了引用计数来管理。

GC的优缺点

优点

如上文提及的,可以将程序从对内存的维护中解放出来,专心于代码逻辑。不会发生因为内存管理不当而导致的问题,例如

  • 内存泄漏
  • 访问已经释放的指针
  • 反复释放指针

缺点

那么代价是什么呢?享受 GC 带来的便利,意味你必须承受 GC 开销对性能的影响,眼睁睁地看着它费老大劲去处理一个你一眼看出来的 Garbage 。比如

Unity 中的GC

Unity的脚本后端是基于Mono的实现(当然现在多了个IL2CPP,不过也是类似的GC实现),而Mono使用的GC是所谓的Boehm–Demers–Weiser garbage collector。是Mark-Sweep 的实现,它会在需要进行GC时占用主线程,进行遍历-标记-垃圾回收的过程,然后在归还主线程控制权。这会导致帧数的突然下降,产生卡顿(不过因为该实现是非压缩式的,所以卡顿现象相对较轻,但是对内存利用率进一步下降了,会有内存碎片的问题。。囧)。所以我们需要慎重地处理对象的创建(内存请求),还有释放(使用GC管理内存是没有主动释放内存的接口的,但是我们可以通过消除对某个对象的引用来做到这一点)。此外,Unity的代码分为两部分:托管与非托管,GC影响的只有托管部分的代码使用的堆内存。而且这个托管堆占用的地址空间不会返还给操作系统,非托管内存需要手动维护

GC的优化

上文讲到了GC对性能影响的原因(占用主线程进行大量工作),而优化GC即是减小GC占用主线程时花费的CPU时间,所以优化GC优化的是CPU时间,而非内存,事实上常见的优化GC的手段之一就是占用内存

排查热点

优化的第一步就是确定性能热点,我们可以使用 Unity 自带的 Profiler 中 CPU Usage里的Garbage Collector来确定,或者粗暴一点使用 GarbageCollector.GCMode 这一接口来关掉GC,然后观察 Profiler 中 Memory里的 Total GC Allocated 来确定。不过该接口无法用于编辑器下。

常见热点与优化方式

GC优化的核心在于消除垃圾,减小GC运行时间。GC的热点一般都是写了一些会产生大量垃圾的代码。

1.字符串

using UnityEngine;
using UnityEngine.UI;
using System.Collections;public class ExampleScript : MonoBehaviour {public Text scoreBoard;public int score;void Update() {string scoreText = "Score: " + score.ToString();scoreBoard.text = scoreText;}
}

上述代码中拼接字符串会导致一些额外的中间对象产生,所以会大量创建临时的变量,可以通过使用StringBuilder来优化。此外还在Update中每帧调用,进一步恶化了问题,创建了更多的临时变量。可以通过将变量改为非局部变量来解决(这也就是上面讲的占用内存,优化GC),上述代码即可以优化成

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Text;public class ExampleScript : MonoBehaviour
{public Text scoreBoard;public StringBuilder scoreText;public int score;public int oldScore;void Update(){if (score != oldScore){scoreText.Clear();scoreText.Append("Score: ");scoreText.Append(score.ToString());scoreBoard.text = scoreText.ToString();oldScore = score;}}
}

StringBuilder   

[C#] StringBuilder简介及使用方法_c# stringbuilder length方法-CSDN博客

C#中,StringBuilder弥补了string在赋值时开辟新空间不足之处。

StringBuilder类型变量会初始化一段长度,供后续对该变量进行增加。当然也可以手动定义其长度

StringBuilder builder = new StringBuilder(10);

其缺点是需要较为精确估算出StringBuilder类型变量的长度,否则若在使用中实际builder长度超出了定义的长度,会自动开辟一段新的StringBuilder空间,并将原先的数据赋值给新的空间,旧的地址就变成了垃圾。

StringBuilder builder = new StringBuilder(10);
for (int i = 1; i < 10; i++) 
{builder.Append(i); // 0123456789
}

整个操作都是在一处内存地址,提高了内存利用率。在Unity实际开发时也有很大的用处

实例方法

单词反转 Hello world => world Hello

private static StringBuilder t3(string str)
{string[] str2 = str.Split(' ');StringBuilder builder = new StringBuilder(str.Length);for (int i = str2.Length - 1; i >= 0; i--){builder.Append(str2[i]);if (i != 0)builder.Append(" ");}return builder;
}// 调用
Console.WriteLine(t3("Hello world")); // world Hello

2.闭包

闭包的使用也需要慎重,因为闭包除了函数指针还会将捕获的变量一起包装起来创建到堆上,相当于 new 了个对象,性能敏感部分代码还是要慎重使用。可以通过将匿名函数改为成员函数,捕获变量改为成员变量一定程度上缓解,不过还是会有影响。

3.装箱

还有要小心装箱,这也会隐式地导致对象的创建。从而产生意想不到的垃圾。用枚举值当字典的key的时,各种字典操作会调用 Object.getHashCode 获取哈希值 ,该方法会导致装箱。Unity5.5版本以前 foreach 会导致装箱,这之后的版本修复了这个问题,但是 foreach相比起直接使用下标遍历还是要慢(因为有一些额外的方法调用),不过这就和GC没啥关系了。

4.返回数组的Unity API

应该是为了防止意外修改内部值,Unity API返回数组对象时返回的是一份拷贝。类似下面的代码

for(int i = 0; i < mesh.vertices.Length; i++){float x, y, z;x = mesh.vertices[i].x;y = mesh.vertices[i].y;z = mesh.vertices[i].z;// ...DoSomething(x, y, z);   }

会导致4次数组拷贝,可以通过cache返回的数组(返回引用解决)来解决。

5.空数组

空数组(长度为0的数组)的创建事实上也会导致堆内存的分配。所以应该将其提前创建出来并复用。

上述问题的原因都是类似的,即大量地创建了短暂使用的对象(垃圾),基本上都可以通过将会反复使用的对象创建为非局部变量来解决(或者更进一步,使用所谓对象池的技术,基本原理是一样的)。有些地方就只能通过避免会造成垃圾产生的接口来解决。总之优化GC,核心在于消灭垃圾

特别的技巧

1.关闭GC

可以把需要的内存先全部创建完,然后关掉GC,不过感觉这种方式应用场景太有限。

2.主动定时GC

游戏的卡顿来自与不稳定的帧数变化(稳定的低帧数和不稳定的高帧数前者可以带来更平滑的体验),所以可以按一定间隔主动地调用 System.GC.Collect 进行GC,这样就不会有剧烈的毛刺产生,当然这个间隔不能太小,否则就和不主动调用区别不大,但也不能太小,否则会对帧数造成明显影响,具体数值的确定还是很难的。

3.主动扩大托管堆

Mono的GC会尽量避免内存的扩展,所以说它对判断 需要进行GC 了的阈值比较低,可能已分配内存达到当前GC管理内存的70%~80%就会进行GC了,如果GC的持有内存足够大的话,就会减少GC的触发,可以通过类似下面的代码

using UnityEngine;
using System.Collections;public class ExampleScript : MonoBehaviour {void Start() {var tmp = new System.Object[1024];// make allocations in smaller blocks to avoid them to be treated in a special way, which is designed for large blocksfor (int i = 0; i < 1024; i++)tmp[i] = new byte[1024];// release referencetmp = null;}
}

来强行扩大GC管理内存的大小。不过实际开发中还有贴图之类的内存大户,留给GC的可以内存实在不多,盲目请求过大的内存可能会被操作系统无情干掉,要慎重。而且因为托管堆占用的地址空间并不会归还,所以请求太大的托管堆会导致内存的浪费。。这种做法算是空间换时间。

增量式GC(incremental garbage collection)

上文提到的 Unity GC实现是非分代式的,也就是是说,要么不做,要做就一次性作完。unity 在 2018 的版本推出了所谓增量式GC的功能,还是基于 Boehm–Demers–Weiser garbage collector 的实现,但是不再是非分代式的,这能带来特别的技巧♂中第二点同样的好处,即均衡负载到多帧,消除毛刺。可以缓解卡顿。因为GC的执行分配到每帧了,所以单帧GC的执行时间会受到垂直同步 还有 unity 的 Application.targetFrameRate 的影响。

增量式GC目前还是抢先体验版本,因为它事实上还是存在一些问题,它的基本实现原理还是标记-清扫,但是在两次增量式GC之间,对象的引用可能会发生变化,导致前一次GC的标记失效,需要重新进行遍历标记,最糟的情况会退化为普通的非分代GC(其实更糟,因为前面的工作全白费了)。比如这样的代码

void Update(){if (Time.frameCount % 2 == 0){s = "A";}else{s = "B";}}

"A" 和 "B" 在垃圾与不是垃圾之间反复横跳(字符串常量的引用可能不太一样,但这里是为了表达对象引用情况反复变化的意思)。而且增量式GC还需要额外的开销来确定对象的引用是否变化,这开销也不可忽视,实际项目看对毛刺的容忍程度来确定要不要使用增量式GC,而且要好好地做Profiler,很容易一不小心就负优化了。

贝姆垃圾收集器  原文链接:贝姆垃圾收集器 - 维基百科,自由的百科全书 (wikipedia.org)

Boehm-Demers-Weiser garbage collector,也就是著名的Boehm GC,是计算机应用在C/C++语言上的一个保守的垃圾回收器

原文链接:Unity 垃圾回收GC的原理? - 知乎 (zhihu.com)

1、BoehmGC中的内存分配

作为一个重量级的基础组件库,BoehmGC的使用方法非常简单,只需要把系统函数malloc替换为GC_malloc即可,之后你就完全不用管何时free的问题。在小型项目里,你甚至可以直接

#define malloc(n) GC_malloc(n)

然后再不用管free,BoehmGC自会帮你打理好一切。

既然全盘接管了内存分配,那就必须做到以下两点,才能称得上是合格的分配器

1). 分配的效率要高

2). 尽量避免内存浪费,避免碎片化等

那BoehmGC是怎么做的呢?

2、BoehmGC的内存分配架构

在整个内存分配链的最底部,BoehmGC通过平台相关接口来向操作系统申请内存(可能是malloc, sbrk, 或者mmap等)。为了提高效率会根据配置,每次批量申请4K的倍数大小,除了用户能使用的内存之外,还有BoehmGC内部维护的数据结构(通过GC_scratch_alloc分配)。

分配器的核心是一个分级的结构,BoehmGC把每次申请根据内存大小归类成小内存对象(Small Object)和大内存对象(Large Object),这点和STL的分配器也比较相似。归类的依据具体来说就是,

1)不超过PageSize/2,也就是2048字节的对象为小内存对象

2)大于PageSize/2的对象为大内存对象

//heap block定义了一个页,大小为4K的倍数
struct hblk {char hb_body[HBLKSIZE];
};

对于Large Object,向上取整到4K的倍数大小,直接以整数个hblk的形式给出。

而Small Object则会先申请一个hblk出来,而后在这块内存上进一步细分为Small Objects,形成free-list。

3、BoehmGC的内存管理策略

为了尽量减少碎片化和加速分配,BoehmGC在设计上就做了一些限制,充分体现了“物以类聚”的思想。

首先,GC管理的对象有一个最小的“粒度”,即Granule。

32位上这个值是8字节,64位则是16字节。

在64位环境下,即使用户申请的内存是10个字节,也会被向上调整到16字节。

一个在用的hblk如果不是属于一个large object,那就是容纳了若干个等大小的small object。

对于有一定内存分配器实现经验的开发者来说,以上两点应该都比较熟悉了,不过BoehmGC把这种“物以类聚”的设计贯彻落实得更加彻底。

对于大内存对象(large object),按照对应的hblk数,把他们归类到若干个freelist中。具体的做法可以参考GC_hblk_fl_from_blocks和GC_allochblk_nth。

当大内存对象被垃圾回收的时候,会尝试把相邻的hblk合并,减少内存碎片。

对于小内存对象的大小分档,也不是完全按照Granule的等差数列来决定。有些临近的大小会被优化合并掉,比如系统当前有很多1024字节的闲置块,但申请1008字节的小内存对象仍然可能miss。此时用1024字节的块可能是更好的选择,适当的合并临近的block size可以优化内存分配效率。这块的做法可以参考GC_init_size_map和GC_extend_size_map。

三、和lua GC有什么区别?

不知道有没有同学想过,lua中的GC用到是什么算法。

Lua 5.3 的垃圾回收机制采用的是标记-清除算法(mark-and-sweep),它会对所有经过 Lua 管理的内存进行垃圾回收,但不会回收非 Lua 管理的内存,例如使用 malloc 或者 new 分配的内存。该算法的优点是实现简单,效率高,但缺点是可能会产生内存碎片

而Boehm GC(Garbage Collector)是一个通用的垃圾回收库,可以用于 C 和 C++ 语言中的动态分配的内存

四、和Java 、C# GC的区别?

Java和 C# GC 都是精准式GC,而Boehm GC 是保守式的。

下面是一个比较官方的回答

“保守式垃圾回收是一种通过近似方式识别和回收垃圾对象的方式。在保守式垃圾回收中,垃圾回收器并不直接访问对象的内部结构和引用关系,而是通过扫描内存中的数据块,识别出可能是指向对象的指针,并将其标记为活动对象。然后,垃圾回收器将从活动对象出发,递归地遍历和标记其他可达的对象,并回收那些未被标记为活动对象的内存。保守式垃圾回收不需要额外的内存开销来维护对象之间的引用关系,但可能会存在一定的误判,即将某些实际上是垃圾的对象错误地标记为活动对象。”

用大白话讲就是Boehm GC无法区分指针和非指针,这就可能由于误判导致有些已经可以释放的内存无法释放。

而Java和 C# GC是可以的,但是需要付出一定的代价。

Lua GC

以后补充

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

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

相关文章

配置之道:深入研究Netty中的Option选项

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 配置之道&#xff1a;深入研究Netty中的Option选项 前言Option的基础概念ChannelOption与Bootstrap Option常见的ChannelOption类型ChannelConfig的使用Option的生命周期不同传输协议的Option 前言 在…

Linux下检查端口占用

很多网站都给出方法检查出端口占用的进程&#xff0c;直接就kill掉&#xff0c;并不检查占用进程的详细情况&#xff0c;如&#xff1a;https://www.runoob.com/w3cnote/linux-check-port-usage.html 正常情况下&#xff0c;需要对进程进行检查 ls /proc/进程ID -l

Tomcat部署Web服务器及基础功能配置

前言 Tomcat作为一款网站服务器&#xff0c;目前市面上Java程序使用的比较多&#xff0c;作为运维工人&#xff0c;有必要了解一款如何去运行Java环境的网站服务。 目录 一、Java相关介绍 1. Java历史 2. Java跨平台服务 3. Java实现动态网页功能 3.1 servelt 3.2 jsp …

Revit-二开之创建TextNote-(1)

Revit二开之创建TextNote TextNode在Revit注释模块中&#xff0c;具体位置如图所示 图中是Revit2018版本 【Revit中的使用】 Revit 中的操作是点击上图中的按钮在平面视图中点击任意放置放置就行&#xff0c; 在属性中可以修改文字 代码实现 创建TextNode ExternalComm…

Web Tomcat

目录 1 前言2 Tomcat的安装3 Tomcat文件的构成4 Tomcat的使用步骤 1 前言 Tomcat是一个 http(web)的容器&#xff0c;笼统的理解一下所有的网站都叫做web。这个web容器可以把我们的前端(htmlcssjs)和后端(servlet)代码都运行起来。 Tomcat是一个免费的开源的Servlet容器&#…

Flutter中的三棵树

Widget Tree&#xff1a; 页面配置信息。 Element Tree&#xff1a; Widget tree的实例化对象&#xff0c;创建出renderObject&#xff0c;并关联到element.renderobject属性上&#xff0c;最后完成RenderObject Tree的创建。 RenderObject Tree&#xff1a;完成布局和图层绘制…

【C++ 函数重载】

C 函数重载 ■ C 函数重载简介■ C 运算符重载■ 一元运算符重载■ 二元运算符重载 &#xff08;&#xff0c;-&#xff0c;*&#xff0c;/&#xff09;■ 关系运算符重载 &#xff08; < 、 > 、 < 、 > 、 等等&#xff09;■ 输入/输出运算符重载&#xff08;运…

【嵌入式学习】网络编程day03.02

一、项目 1、TCP机械臂测试 #include <myhead.h> #define SER_IP "192.168.126.32" #define SER_PORT 8888 #define CER_IP "192.168.126.42" #define CER_PORT 9891 int main(int argc, const char *argv[]) {int wfd-1;//创建套接字if((wfdsocke…

ubuntu创建账号和samba共享目录

新建用于登录Ubuntu图形界面的用户 sudo su #切换为root用户获取管理员权限用于新建用户 adduser username #新建用户&#xff08;例如用户名为username&#xff09; adduser username sudo #将用户添加到 sudo 组 新建只能用于命令行下登录的用户 sudo su #切换为root用户…

《TCP/IP详解 卷一》第8章 ICMPv4 和 ICMPv6

目录 8.1 引言 8.1.1 在IPv4和IPv6中的封装 8.2 ICMP 报文 8.2.1 ICMPv4 报文 8.2.2 ICMPv6 报文 8.2.3 处理ICMP报文 8.3 ICMP差错报文 8.3.1 扩展的ICMP和多部报文 8.3.2 目的不可达和数据包太大 8.3.3 重定向 8.3.4 ICMP 超时 8.3.5 参数问题 8.4 ICMP查询/信息…

划分开始结束位置设置标记

划分开始结束位置 初始音轨如下图所示 在想开始地方单击左键&#xff0c;长按直到你想要的结束位置松开。就可以划分开始和结束位置 设置标记 方式1 &#xff1a;直接点击该图标 方式二&#xff1a;使用快捷键M 设置标记点可以自定义名称方便检索标记点

javaWebssh酒店客房管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh酒店客房管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0…

STL容器之string类

文章目录 STL容器之string类1、 什么是STL2、STL的六大组件3、string类3.1、string类介绍3.2、string类的常用接口说明3.2.1、string类对象的常见构造3.2.2、string类对象的容量操作3.2.3、string类对象的访问及遍历操作3.2.4、 string类对象的修改操作3.2.5、 string类非成员函…

车辆维护和燃油里程跟踪器LubeLogger

什么是 LubeLogger &#xff1f; LubeLogger 是一个自托管、开源、基于网络的车辆维护和燃油里程跟踪器。 LubeLogger 比较适合用来跟踪管理您的汽车的维修、保养、加油的历史记录&#xff0c;比用 Excel 强多了 官方提供了在线试用&#xff0c;可以使用用户名 test 和密码 123…

oracle-long类型转clob类型及clob类型字段的导出导入

1、若oracle数据库表字段类型有long类型&#xff0c;有时候我们需要模糊匹配long类型字段时&#xff0c;是查询不出来结果的&#xff0c;此时使用TO_LOB&#xff0c;将long类型转成clob类型&#xff0c;就可以模糊匹配信息。 例如&#xff1a;oracle数据库查询所有视图内容中包…

机器学习-4

文章目录 前言数组创建切片索引索引遍历切片编程练习 总结 前言 本篇将介绍数据处理 Numpy 库的一些基本使用技巧&#xff0c;主要内容包括 Numpy 数组的创建、切片与索引、基本运算、堆叠等等。 数组创建 在 Python 中创建数组有许多的方法&#xff0c;这里我们使用 Numpy 中…

机器学习-5

文章目录 前言Numpy库四则运算编程练习 前言 本片将介绍Numpy库中的四则运算。 Numpy库四则运算 Numpy库可以直接进行一些四则运算&#xff0c;快速的处理两个Numpy数组&#xff1a; a np.array([[1,2,3],[4,5,6]]) b np.array([[4,5,6],[1,2,3]])向量与向量之间 1.加法 …

14.最长公共前缀

题目&#xff1a;编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串""。 解题思路&#xff1a;横向扫描&#xff0c;依次遍历每个字符串&#xff0c;更新最长公共前缀。另一种方法是纵向扫描。纵向扫描时&#xff0c;从前…

基于tomcat的JavaWeb实现

Tomcat服务器 免费&#xff0c;性能一般的服务器 安装配置 基于Java&#xff0c;故需要配置环境变量&#xff0c;新加系统路径JAVA_HOME&#xff0c;路径为jdk的主目录。 而后打开bin目录下的startup.bat文件出现如下窗口说明配置成功 idea继承tomcat服务器 使用java开发…

Linux 之压缩与解压相关命令的基础用法

目录 1、zip 与 unzip 2、gzip 命令 3、tar 命令 1、zip 与 unzip 在桌面新建一个文件和文件夹用于测试 在 test 目录下有一个 1.txt 文件 我们使用 zip 命令对其压缩 用法&#xff1a; zip 自定义压缩包名 被压缩文件路径位置 zip myon.zip 1.txt 因为我们这里就是在 …