有 gc 的语言在 gc 过程中是不是都会暂停,这种暂停在什么情况下会难以接受。
如 c c++ rust ,他们在回收内存的过程中会暂停吗?
1
cubecube 2022-07-01 09:43:17 +08:00
有 gc 基本都会有暂停,但是也存在一些消除暂停的 GC 算法,比如商业的 Zing JVM 。
c rust 不暂停主要是因为自己管内存,用完及时释放了。 |
2
ql562482472 2022-07-01 09:52:51 +08:00
我记得 java 中 stop the world 是因为它有一刻要遍历所有的对象找到 Root 对象吧
如果没有 root 对象的设计,应该就不会全部暂停 |
3
liaohongxing 2022-07-01 09:55:49 +08:00
go 的 gc 已改进到停顿 1ms, 非极端场景基本可忽略 , 到了 1ms 都忍受不了,可以换 c/c++/rust
|
4
zmqiang 2022-07-01 09:56:43 +08:00
我印象中,需要暂停的 gc ,是因为运行时需要获取当前内存的使用情况,所以程序逻辑必须暂停。
c c++是手动释放内存的,运行时不参与,所以不存在暂停,各自管各自的就行。并且不需要扫描,哪些内存需要释放是交给程序员决定的 rust 不是很了解,但印象中好像也是不存在 gc 的 go 最新 gc 算法,可以基本避免程序暂停的,是可以实现运行时和程序逻辑并行运行的 |
5
bruce0 2022-07-01 10:01:26 +08:00
go 现在只会在开启和关闭内存屏障的时刻会暂停一下, 基本在 1ms 左右, 性能基本可以忽略不计了,
C++智能指针 通过引用计数器做的, 没有 GC 不会有暂停的问题, rust 不了解, 好像是在编译时期计算得到生命周期 实现内存管理的, 也没有 GC 不会有暂停问题 |
6
nicebird 2022-07-01 10:05:03 +08:00
c\c++\rust 手动管理,按需释放。做的一般的,释放过程被离散开了,不会暂停。做的差的,集中释放大量内存,一样会卡住。
|
7
Kould 2022-07-01 10:09:48 +08:00 1
现有的所有垃圾收集算法实际上并不能避免 STW ,而现在只能做到尽可能减少 STW 时间,而有些像 ZGC 这样的就是主打低延迟的垃圾收集器。而 STW 主要影响那种尾部延迟(tail latencies ,多个请求下,响应最慢的请求延迟)比较敏感的服务。ddia 中有这样描述延迟影响的一段话:
“响应时间的高百分位点(也称为 尾部延迟,即 tail latencies )非常重要,因为它们直接影响用户的服务体验。例如亚马逊在描述内部服务的响应时间要求时是以 99.9 百分位点为准,即使它只影响一千个请求中的一个。这是因为请求响应最慢的客户往往也是数据最多的客户,也可以说是最有价值的客户 —— 因为他们掏钱了 [19] 。保证网站响应迅速对于保持客户的满意度非常重要,亚马逊观察到:响应时间增加 100 毫秒,销售量就减少 1% [20] ;而另一些报告说:慢 1 秒钟会让客户满意度指标减少 16% [21 ,22] 。” 而类似 c c++ rust 则属于没有 gc 的语言,需要开发者自己控制内存释放,所以微观来说回收内存会导致指令级的停顿,但是因为开发者有着对对象清晰的结构梳理,在使用得当的情况下则不会导致类似 GC 这样相对来说集中且漫长的延迟(除非你自己整了一套 gc 机制)。 |
8
Suddoo 2022-07-01 10:11:12 +08:00 via iPhone 2
JVM 的 gc 暂停时间已经控制在 1ms 以内了、而且也没有什么乱七八糟的参数让二把手们去“调优”了,这个时间对于大部分场景都可以接受,又不是造航天飞机
牺牲了 1ms 的暂停时间,换来开发效率上质的提升,比手动管理内存的语言,降低了手动释放内存的心智负担 |
9
cppc 2022-07-01 10:13:32 +08:00
如果你问的 GC 是指这个 https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)
那么 c c++ rust 不存在 GC 这一说,这活得程序员自己干,换句话说,内存的申请、释放在程序代码里体现得清清楚楚。 |
10
libook 2022-07-01 10:22:42 +08:00
按我自己的话来说,编程语言视运行时内存管理模式可以划分有 GC 和无 GC 两种,有 GC 就是运行时会自动检测无用的内存数据并释放掉,无 GC 就是开发者决定哪些内存数据什么时候释放掉,不管是显式释放( C 、C++),还是隐式释放( Rust )。
GC 机制本身不一定非要暂停应用,这取决于使用的 GC 算法。 比如一种算法是先标记可达对象,再删除没被标记的对象,那么如果不暂停应用的话,可能在标记和删除操作之间产生了新对象,而这些新对象还没被标记,而删除策略是删除所有未标记对象,就会造成误删。 很多有 GC 机制的语言同时支持多种 GC 算法,具体可以了解一下是不是每种都会暂停应用。 |
12
hxtheone 2022-07-01 11:30:53 +08:00 via iPhone
遍历和标记都可以并发操作, 主要是一些 gc 实现里内存压缩这一步, 在清理碎片的过程会导致引用的内存地址变动, 这里的 stw 是省不了的, 要不就换 gc 算法, 要不就不用 gc 自己管理
|
13
ksco 2022-07-01 12:28:50 +08:00
|
14
jhdxr 2022-07-01 12:44:29 +08:00
时代的变迁啊,以前讨论 GC 的时候,拿出来说的基本上都是 java 。现在默认都成 go 了么?
|
15
duke807 2022-07-01 13:23:34 +08:00 via Android
譬如需要確保實時性的任務,preempt-rt linux 環境,用戶空間寫 c 程序,內存是要在初始化的時候申請好,中途不能釋放內存,也不能申請內存,以保證實時性。
|
16
agagega 2022-07-01 13:30:21 +08:00
C++/Rust 释放内存时不会有你想象的那种「暂停」,但如果内存分配器做得不好的话,运行期间也会有大量零散的内存释放,所以有人说这种情况下无 GC 的性能反而不如 GC
|
17
12101111 2022-07-01 14:22:28 +08:00
延迟和吞吐量是两回事, 好的 gc 能做到高的吞吐量, 但是 1ms 的延迟说实在还是很高的, 原子引用计数基本上都是纳秒级别的(几百个时钟周期)
|
18
yyysuo 2022-07-01 14:36:53 +08:00 1
高潮的过程中会暂停?
|
19
VictorJing94 2022-07-01 15:09:04 +08:00
实时性要求高的时候..........弄 C#遇到过,偶尔程序会卡一下,后面排查发现有人在自旋代码块里加了一句 GC.Collect.......
|
21
dqzcwxb 2022-07-01 15:42:21 +08:00
|
23
sujin190 2022-07-01 17:57:52 +08:00
@12101111 #17 原子引用计数单个确实不高,但是明显你忽略了引用计数需要的操作数可是远远超过了 GC 的,引用计数只是不会导致全局暂停延时而已,并不能降低整体延时,甚至是增加延时的
|
24
secondwtq 2022-07-01 18:46:04 +08:00
”进程”会暂时呆住,但是”应用”不一定。这俩并不是同一个概念
其实 Tracing GC 影响的事所有和进行 GC 的堆相关的对象,你在一个进程里放多个 GC 堆隔离起来,也能避免进程暂停 RAII 也有类似的问题,简单粗暴的 RAII 如果出现一个对象拖着一大坨对象,出作用域的时候有得你回收的 |
25
0x2CA 2022-07-01 18:48:09 +08:00
gc 是有不同的算法的,常见的有 Reference Counting ,Mark Sweep ,Copy Collection 等等,你可以参考这个文章 https://zhuanlan.zhihu.com/p/54111851 ,了解大概
|
26
FrankHB 2022-07-01 22:17:12 +08:00
不一定,现代的一些 GC 实现可以并发回收,暂停个别线程而不是整个进程。实用上(暂停时间能保证短至分时系统的个别时间片粒度)只影响 GC 线程而完全避免阻塞应用线程的实现很少,像 Asul C4 。代价是感人的内存使用率。
C/C++之类用全局堆之类实现的自由存储一样不能完全避免对全局共享的分配器内部管理数据结构加锁而可能暂停,但是任何靠谱的实现中这个暂停相对几乎所有 GC 都非常短而基本可以忽略。一些实现如 snmalloc 在同一个线程中的分配和释放可以完全不暂停其它线程。用户实现的只对特定线程可用的分配器可以不影响其它线程。 以上 GC 专指全局 tracing GC 。多实例 GC 堆和引用计数对整个应用的影响在此都可以视同后者。 当然如果程序是单线程的,那么不管是 GC 还是确定性分配器都会直接阻塞,因为没什么实现无聊到在释放算法里实现分时多任务。 |
27
FrankHB 2022-07-01 22:30:07 +08:00
@Kould 即使使用得当,不限深度的嵌套数据结构集中释放也很容易出现高延迟的问题。(这不属于使用不当,因为语言允许轻易地构造这种对象。)
实际上不容易观察到类似的问题,是因为在此之前,大多数 naive 实现在递归调用释放资源的例程中就直接爆栈了( |
28
hackfly 2022-07-01 23:43:55 +08:00
某语言对对象标识 0 代,1 代 ,N 代,应该是防止这些的
|
29
Cbdy 2022-07-02 08:23:41 +08:00 via Android
广义的讲,当你开始使用智能指针的时候,你就已经开始使用垃圾回收了
狭义的说,当前理论上有无暂停的 gc 算法,但是工程实现难度过大,一般还是会选择引入极短时间的暂停(如 Zing JVM ) |
30
DoctorDeng 2022-07-02 10:51:04 +08:00
感觉鱼与熊掌不可兼得,自动化内存管理和手动内存管理各有利弊。手动内存管理好处是你就是上帝,可以控制任何东西,坏处是容易出问题(因为人容易出问题),这也是为啥 Linux 大部分漏洞都是内存安全问题方面的。自动化内存管理可以让一般开放人员无关心内存这种相对底层的问题,让开发人员更专注于业务开发,但是对于一些比较复杂的场景,如果垃圾收集器不能够很好的处理,则就是漫长的 GC 调优之路了。不过现在垃圾收集器也慢慢在发展,有的可以自适应了,不再需要我们去调整各种 GC 参数来获得更良好的性能。
|
31
roundgis 2022-07-02 13:17:28 +08:00
|
32
LeegoYih 2022-07-02 15:33:44 +08:00
会暂停,但是现代 GC 暂停时间都很短
毫秒级经典垃圾收集器:Garbage First (G1) 亚毫秒级低延迟垃圾收集器:Shenandoah 、ZGC 徹底解剖「 G1GC 」実装編 http://www.narihiro.info/ebook/g1gc-impl-20120914.pdf |
33
neoblackcap 2022-07-02 19:26:24 +08:00
上面很多楼已经说过了,哪怕是有 GC 的语言,现代垃圾回收器都会 pauseless 的类型。不会使主线程堵塞。
至于 C/C++/Rust 之类的语言,其实回收内存的时候已经不能称为暂停。因为这个是可预知。只能算成是程序 /函数运行的成本。更类似于你计算一个复杂的问题,函数没法立刻返回结果。 |
34
ToBeHacker 2022-07-02 21:53:39 +08:00
现在的过 gc 一般都不会暂停了
|