范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

golang2021数据格式(103)垃圾回收发展史

  GC 的历史及演进 #
   Go 历史各个版本在 GC 方面的改进? #
  Go      1:串行三色标记清扫
  Go      1.3:并行清扫,标记过程需要 STW,停顿时间在约几百毫秒
  Go      1.5:并发标记清扫,停顿时间在一百毫秒以内
  Go 1.6:使用      bitmap 来记录回收内存的位置,大幅优化垃圾回收器自身消耗的内存,停顿时间在十毫秒以内
  Go      1.7:停顿时间控制在两毫秒以内
  Go      1.8:混合写屏障,停顿时间在半个毫秒左右
  Go      1.9:彻底移除了栈的重扫描过程
  Go      1.12:整合了两个阶段的 Mark Termination,但引入了一个严重的 GC Bug 至今未修(见问题 20),尚无该 Bug 对 GC      性能影响的报告
  Go      1.13:着手解决向操作系统归还内存的,提出了新的 Scavenger
  Go      1.14:替代了仅存活了一个版本的      scavenger,全新的页分配器,优化分配内存过程的速率与现有的扩展性问题,并引入了异步抢占,解决了由于密集循环导致的 STW 时间过长的问题
  可以用下图直观地说明 GC 的演进历史:
  在 Go 1 刚发布时的版本中,甚至没有将 Mark-Sweep 的过程并行化,当需要进行垃圾回收时,所有的代码都必须进入 STW 的状态。而到了 Go 1.3 时,官方迅速地将清扫过程进行了并行化的处理,即仅在标记阶段进入 STW。
  这一想法很自然,因为并行化导致算法结果不一致的情况仅仅发生在标记阶段,而当时的垃圾回收器没有针对并行结果的一致性进行任何优化,因此才需要在标记阶段进入 STW。对于 Scavenger 而言,早期的版本中会有一个单独的线程来定期将多余的内存归还给操作系统。
  而到了 Go 1.5 后,Go 团队花费了相当大的力气,通过引入写屏障的机制来保证算法的一致性,才得以将整个 GC 控制在很小的 STW 内,而到了 1.8 时,由于新的混合屏障的出现,消除了对栈本身的重新扫描,STW 的时间进一步缩减。
  从这个时候开始,Scavenger 已经从独立线程中移除,并合并至系统监控这个独立的线程中,并周期性地向操作系统归还内存,但仍然会有内存溢出这种比较极端的情况出现,因为程序可能在短时间内应对突发性的内存申请需求时,内存还没来得及归还操作系统,导致堆不断向操作系统申请内存,从而出现内存溢出。
  到了 Go 1.13,定期归还操作系统的问题得以解决,Go 团队开始将周期性的 Scavenger 转化为可被调度的 goroutine,并将其与用户代码并发执行。而到了 Go 1.14,这一向操作系统归还内存的操作时间进一步得到缩减。
  17. Go GC 在演化过程中还存在哪些其他设计?为什么没有被采用? #
  并发栈重扫 #
  正如我们前面所说,允许灰色赋值器存在的垃圾回收器需要引入重扫过程来保证算法的正确性,除了引入混合屏障来消除重扫这一过程外,有另一种做法可以提高重扫过程的性能,那就是将重扫的过程并发执行。然而这一方案[11]并没有得以实现,原因很简单:实现过程相比引入混合屏障而言十分复杂,而且引入混合屏障能够消除重扫这一过程,将简化垃圾回收的步骤。
  ROC #
  ROC 的全称是面向请求的回收器(Request Oriented Collector)[12],它其实也是分代 GC 的一种重新叙述。它提出了一个请求假设(Request Hypothesis):与一个完整请求、休眠 goroutine 所关联的对象比其他对象更容易死亡。这个假设听起来非常符合直觉,但在实现上,由于垃圾回收器必须确保是否有 goroutine 私有指针被写入公共对象,因此写屏障必须一直打开,这也就产生了该方法的致命缺点:昂贵的写屏障及其带来的缓存未命中,这也是这一设计最终没有被采用的主要原因。
  传统分代 GC #
  在发现 ROC 性能不行之后,作为备选方案,Go 团队还尝试了实现传统的分代式 GC [13]。但最终同样发现分代假设并不适用于 Go 的运行栈机制,年轻代对象在栈上就已经死亡,扫描本就该回收的执行栈并没有为由于分代假设带来明显的性能提升。这也是这一设计最终没有被采用的主要原因。
  18. 目前提供 GC 的语言以及不提供 GC 的语言有哪些?GC 和 No GC 各自的优缺点是什么? #
  从原理上而言,所有的语言都能够自行实现 GC。从语言诞生之初就提供 GC 的语言,例如:
  Python
  JavaScript
  Java
  Objective-C
  Swift
  而不以 GC 为目标,被直接设计为手动管理内存、但可以自行实现 GC 的语言有:
  C
  C++
  也有一些语言可以在编译期,依靠编译器插入清理代码的方式,实现精准的清理,例如:
  Rust
  垃圾回收使程序员无需手动处理内存释放,从而能够消除一些需要手动管理内存才会出现的运行时错误:
  在仍然有指向内存区块的指针的情况下释放这块内存时,会产生悬挂指针,从而后续可能错误的访问已经用于他用的内存区域。
  多重释放同一块申请的内存区域可能导致不可知的内存损坏。
  当然,垃圾回收也会伴随一些缺陷,这也就造就了没有 GC 的一些优势:
  没有额外的性能开销
  精准的手动内存管理,极致的利用机器的性能
  19. Go 对比 Java、V8 中 JavaScript 的 GC 性能如何? #
  无论是 Java 还是 JavaScript 中的 GC 均为分代式 GC。分代式 GC 的一个核心假设就是分代假说:将对象依据存活时间分配到不同的区域,每次回收只回收其中的一个区域。
  V8 的 GC #
  在 V8 中主要将内存分为新生代和老生代。新生代中的对象为存活时间较短的对象,老生代中的对象为存活时间较长、常驻内存、占用内存较大的对象:
  新生代中的对象主要通过副垃圾回收器进行回收。该回收过程是一种采用复制的方式实现的垃圾回收算法,它将堆内存一分为二,这两个空间中只有一个处于使用中,另一个则处于闲置状态。处于使用状态的空间称为      From 空间,处于闲置的空间称为 To 空间。分配对象时,先是在 From 空间中进行分配,当开始垃圾回收时,会检查 From      空间中的存活对象,并将这些存活对象复制到 To 空间中,而非存活对象占用的空间被释放。完成复制后,From 空间和 To      空间的角色互换。也就是通过将存活对象在两个空间中进行复制。
  老生代则由主垃圾回收器负责。它实现的是标记清扫过程,但略有不同之处在于它还会在清扫完成后对内存碎片进行整理,进而是一种标记整理的回收器。
  Java 的 GC #
  Java 的 GC 称之为 G1,并将整个堆分为年轻代、老年代和永久代。包括四种不同的收集操作,从上往下的这几个阶段会选择性地执行,触发条件是用户的配置和实际代码行为的预测。
  年轻代收集周期:只对年轻代对象进行收集与清理
  老年代收集周期:只对老年代对象进行收集与清理
  混合式收集周期:同时对年轻代和老年代进行收集与清理
  完整 GC      周期:完整的对整个堆进行收集与清理
  在回收过程中,G1 会对停顿时间进行预测,竭尽所能地调整 GC 的策略从而达到用户代码通过系统参数(-XX:MaxGCPauseMillis)所配置的对停顿时间的要求。
  这四个周期的执行成本逐渐上升,优化得当的程序可以完全避免完整 GC 周期。
  性能比较 #
  在 Go、Java 和 V8 JavaScript 之间比较 GC 的性能本质上是一个不切实际的问题。如前面所说,垃圾回收器的设计权衡了很多方面的因素,同时还受语言自身设计的影响,因为语言的设计也直接影响了程序员编写代码的形式,也就自然影响了产生垃圾的方式。
  但总的来说,他们三者对垃圾回收的实现都需要 STW,并均已达到了用户代码几乎无法感知到的状态(据 Go GC 作者 Austin 宣称STW 小于 100 微秒 [14])。当然,随着 STW 的减少,垃圾回收器会增加 CPU 的使用率,这也是程序员在编写代码时需要手动进行优化的部分,即充分考虑内存分配的必要性,减少过多申请内存带给垃圾回收器的压力。
  20. 目前 Go 语言的 GC 还存在哪些问题? #
  尽管 Go 团队宣称 STW 停顿时间得以优化到 100 微秒级别,但这本质上是一种取舍。原本的 STW 某种意义上来说其实转移到了可能导致用户代码停顿的几个位置;除此之外,由于运行时调度器的实现方式,同样对 GC 存在一定程度的影响。
  目前 Go 中的 GC 仍然存在以下问题:
  1. Mark Assist 停顿时间过长 #
   1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73package main
  import (
              "fmt"
              "os"
              "runtime"
              "runtime/trace"
              "time"
      )
  const   (
              windowSize = 200000
              msgCount   = 1000000
      )
  var   (
              best    time.Duration = time.Second
              bestAt  time.Time
              worst   time.Duration
              worstAt time.Time
  start = time.Now()
      )
  func   main() {
              f, _ := os.Create("trace.out")
              defer   f.Close()
              trace.Start(f)
              defer   trace.Stop()
  for   i := 0; i < 5; i++ {
                      measure()
                      worst = 0
                      best =   time.Second
                      runtime.GC()
              }
      }
  func   measure() {
              var   c channel
              for   i := 0; i < msgCount; i++ {
                      c.sendMsg(i)
              }
              fmt.Printf("Best send delay %v at %v, worst   send delay: %v at %v. Wall clock: %v  ", best, bestAt.Sub(start), worst, worstAt.Sub(start),   time.Since(start))
      }
  type   channel [windowSize][]byte
  func   (c *channel) sendMsg(id int) {
              start := time.Now()
  // 模拟发送
              (*c)[id%windowSize] = newMsg(id)
  end := time.Now()
              elapsed :=   end.Sub(start)
              if   elapsed > worst {
                      worst = elapsed
                      worstAt = end
              }
              if   elapsed < best {
                      best = elapsed
                      bestAt = end
              }
      }
  func   newMsg(n int) []byte {
              m := make([]byte, 1024)
              for   i := range m {
                      m[i] = byte(n)
              }
              return m
      }
  运行此程序我们可以得到类似下面的结果:
  $ go run main.go
  Best send delay 330ns at 773.037956ms, worst send delay: 7.127915ms at 579.835487ms. Wall clock: 831.066632ms
  Best send delay 331ns at 873.672966ms, worst send delay: 6.731947ms at 1.023969626s. Wall clock: 1.515295559s
  Best send delay 330ns at 1.812141567s, worst send delay: 5.34028ms at 2.193858359s. Wall clock: 2.199921749s
  Best send delay 338ns at 2.722161771s, worst send delay: 7.479482ms at 2.665355216s. Wall clock: 2.920174197s
  Best send delay 337ns at 3.173649445s, worst send delay: 6.989577ms at 3.361716121s. Wall clock: 3.615079348s
  在这个结果中,第一次的最坏延迟时间高达 7.12 毫秒,发生在程序运行 578 毫秒左右。通过 go tool trace 可以发现,这个时间段中,Mark Assist 执行了 7112312ns,约为 7.127915ms;可见,此时最坏情况下,标记辅助拖慢了用户代码的执行,是造成 7 毫秒延迟的原因。
  2. Sweep 停顿时间过长 #
  同样还是刚才的例子,如果我们仔细观察 Mark Assist 后发生的 Sweep 阶段,竟然对用户代码的影响长达约 30ms,根据调用栈信息可以看到,该 Sweep 过程发生在内存分配阶段:
  3. 由于 GC 算法的不正确性导致 GC 周期被迫重新执行 #
  此问题很难复现,但是一个已知的问题,根据 Go 团队的描述,能够在 1334 次构建中发生一次 [15],我们可以计算出其触发概率约为 0.0007496251874。虽然发生概率很低,但一旦发生,GC 需要被重新执行,非常不幸。
  4. 创建大量 Goroutine 后导致 GC 消耗更多的 CPU #
  这个问题可以通过以下程序进行验证:
   1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28func   BenchmarkGCLargeGs(b *testing.B) {
              wg := sync.WaitGroup{}
  for   ng := 100; ng <= 1000000; ng *= 10 {
                      b.Run(fmt.Sprintf("#g-%d", ng), func(b *testing.B) {
                              // 创建大量 goroutine,由于每次创建的 goroutine   会休眠
                              // 从而运行时不会复用正在休眠的 goroutine,进而不断创建新的   g
                              wg.Add(ng)
                              for   i := 0; i < ng; i++ {
                                      go func() {
                                                time.Sleep(100 * time.Millisecond)
                                                wg.Done()
                                        }()
                              }
                              wg.Wait()
  // 现运行一次 GC 来提供一致的内存环境
                              runtime.GC()
  // 记录运行 b.N 次 GC 需要的时间
                              b.ResetTimer()
                              for   i := 0; i < b.N; i++ {
                                        runtime.GC()
                              }
                      })
  }
      }
  其结果可以通过如下指令来获得:
  1
      2       3       4       5       6       7       8   $ go test -bench=BenchmarkGCLargeGs -run=^$ -count=5   -v . | tee 4.txt       $ benchstat 4.txt       name                       time/op       GCLargeGs/#g-100-12       192µs ± 5%       GCLargeGs/#g-1000-12      331µs ±   1%       GCLargeGs/#g-10000-12    1.22ms ±   1%       GCLargeGs/#g-100000-12   10.9ms ±   3%       GCLargeGs/#g-1000000-12  32.5ms ± 4%   这种情况通常发生于峰值流量后,大量 goroutine 由于任务等待被休眠,从而运行时不断创建新的 goroutine, 旧的 goroutine 由于休眠未被销毁且得不到复用,导致 GC 需要扫描的执行栈越来越多,进而完成 GC 所需的时间越来越长。 一个解决办法是使用 goroutine 池来限制创建的 goroutine 数量

老年代步车怎么选?哪个品牌性价比更高?行内人说了实话您在阅读前,先点击上面的关注。感谢您的支持,我们将为您带来更多有价值的内容。正值低速车国标转正之际,低速车市场大战一触即发。除了技术领先的新能源汽车企业布局之外,还引来了不少三轮车2K直屏!天玑90005000mAh,vivo影像旗舰来袭想要在竞争激烈的手机市场中脱颖而出,智能手机厂商们除了强大的资金支持,在手机品质以及科技创新方面是必不能少的,毕竟现在消费者买手机不只是单纯的看参数了,手机的颜值各项配置的打磨等都滴滴暂停国外2国业务,国内发出新警告,1500万司机优势将不再昔日的滴滴一手遮天,无人能敌,如今虽依旧稳居高位,但却有一种墙倒众人推的架势。01滴滴裁员,暂停国外2国业务近期在滴滴身上又传来几个新消息,但无一例外都预示着它正在朝坏方向发展。这EX5Z,一款内外兼修的新能源作为扎根于中国的硬科技创新企业,威马汽车深入践行节能环保的理念,旗下热销车型威马EX5(图片配置询价)Z威马W6(图片配置询价)以智能纯电为技术路径,零污染零排放的绿色属性,持续为消息称iPhone14全系设计已定稿摒弃刘海屏启用全新外形据市场消息,iPhone14Pro系列将会采用类似药丸形的居中打孔设计,虽然目前还不确定苹果到底是用哪种方式来展示,但至少可以肯定的是,新机的屏占比会大幅提升。据供应链最新消息称,华为高管终于官宣,Mate50被正式确认,外观配置均已被提前曝光友商的旗舰新机是一款接着一款,但华为公司这边就显得有些安静了,而许多花粉们都非常期待华为公司能举办发布会发布新机,尤其是期待华为Mate50这款华为重磅新旗舰,所以当余承东宣布将在华为Mate50被官方确认,花粉们高兴吗?在智能手机关于去年数据爆发期,每出来一个数据大家都会发出心疼华为的声音。是啊!曾经的全球第二,由于断供的问题现在全球前五都进不去,而且各个市场全部是下滑的趋势。但是对于华为来讲下滑为了什么而工作谈字节跳动员工猝死事件北京字节跳动总部资料片事件经过2月21日晚上18点,28年的字节跳动员工吴同学在健身房中感到头晕,随后情况越来越危机,19点送往医院,经过41小时的将抢救,心肌梗死治疗无效身亡。吴全球最大黑客团伙对俄罗斯宣战,俄罗斯该何去何从呢?近日,全球最大的黑客团伙匿名者正式宣战俄罗斯,并攻陷了当日俄罗斯的电视台与网站,甚至有黑客在个人推文中加了乌克兰字样的标签。根据当日俄罗斯电视台的报道,至莫斯科时间24日17点开始世界最大黑客组织匿名者对俄宣战,匿名者到底什么来头?黑客组织匿名者什么来头,为何敢对俄罗斯宣战?匿名者没错,就是那个戴着面具的盖伊福克斯(GuyFawkes),2010年代的黑客组织,曾经攻击过包括奥巴马政府在内的强大组织又回来了。新能源(纯电)车实际用车成本与燃油车哪个高?要做出正确的结论,必须是同级别,同类型车的比较,本文以B级合资或外资SUV车型进行对比,用车环境为华北地区,每年行驶里程20000(10000)公里,一半里程为市内通勤,一半里程为
挖孔屏iPhone无人脸解锁,却神似国产机,网友难怪要刘海现如今的国内手机市场,安卓阵营凭借着充电速率各种细节体验等等优势,成功将苹果手机市场蚕食,而反观苹果,每年的市场占比份额都在缓慢下降,但是不可不言的就是,它凭借着其芯片性能以及系统电池突破?125w闪充5K毫安重量堪忧,160Hz够诱人吗自从联发科发布了最新的天玑1200之后,各大手机厂商都开始透露新机的风声,作为手机市场新秀的realme以同样不甘落后,官方宣布将会首批采用骁龙888和天玑1200处理器。而其中最苹果联手现代造车或成特斯拉对手,续航却被质疑,网友洗洗睡吧虽然汽车工业已经有着上百年的历史,但因为新能源的出现和政策的扶持,让造车再次成为了一个香饽饽。阿里华为腾讯等大企业口嫌体正直,嘴上说着不造车,但纷纷和汽车企业联起手来。在这之中苹果苹果要出折叠屏了,小米折叠新机将发布,网友华为入网了2021年不仅是高通与联发科新一轮决战的时间,还是苹果华为小米折叠屏的开战时间。跟以往单纯比较旗舰机性能不同,近段时间,折叠屏手机消息不断,有网友猜测,2021年是折叠屏手机加速普小米再次发挥鲶鱼效应,激活AI语音技术发展,米粉逼着友商进步鲶鱼效应,指的是鲶鱼在搅动小于生存环境的同时,也激活了小鱼的求生能力。作为一个概念,鲶鱼效应也可指在一个相对平静的环境中,活跃的鲶鱼加剧行业的竞争同时也激发了其他个体的活力。如果把如果夏普做正经厂商,手机高刷起码提前3年,还是无线充电领军者说到夏普,每每都会想到知名足球解说韩乔生,在1996年的英超比赛中,韩乔生的精彩片段是这样子的7号球员夏普分球,传给9号球员,他也叫夏普,他们可能是兄弟。毕竟亲兄弟上阵还是满热血的盐是什么?今天看见一篇帖子我听朋友说加盐可以抵御黑客,这令我感觉非常疑惑,请告诉我在服务器上撒盐来抵御黑客的原理。还是我理解错误?我们先来看看各位程序员的调侃回答下面我们总结一下盐到底是个什effectiveC01使用explicit禁止编译器执行非预期类型转换classApublicA()defaultctorclassBpublicexplicitB(intx0,boolbtrue)defaultctorclassCpublicexp其实90的人被错误定投思想所误导了大家好,我是老李。昨天一觉醒来,我相信所有投资者心都哇凉哇凉的,因为美国股市又大跌了。事实上,不仅是美国股市,全球股市昨日的表现都相当糟糕。但好在我们的大A股昨日表现非常出色。低开黄金避险属性我没看出来,但空头们看你像块肥肉大家好,我是老李。最近的黄金市场可以说是火热到了一个新的境界,上一次那么火热还要追溯到7年前的中国大妈横扫华尔街了,这不黄金刚创了历史新高,立马就来了急速猛跌。今天我就来和大家聊聊曾被雷军骗进小米,甚至两度为其裸奔,如今52岁身家远超黄章现在说起小米,总是会想起雷军卢伟冰,但是其实小米背后的所有人都是不可或缺的,在发展历程中,正是最初林斌周光平刘德黎万强黄江吉和洪锋6个人,才有了小米的今天,而其中有一个人的影响力可