0%

原文:详解 Go 语言的内存模型及堆的分配管理 ,建议 tm 反复观看

golang 的内存管理是借鉴了 TCMalloc(Thread Cache Malloc),随着 Go 的迭代,Go 的内存管理与 TCMalloc 不一致的地方也在扩大,但其主要思想是一致的。只是比 TCMalloc 多了逃逸分析垃圾回收

基本架构

架构

  • page: 图中的浅蓝方块,在 x86 架构中一个 page 大小为 8KB
  • span: 内存管理的基本单位,由一组连续的 page 组成,图中的浅蓝长方形和紫色方块
  • mcache: mcache 保存的是各种大小的 Span,并按 Span class 分类,小对象直接从 mcache 分配内存,它起到了缓存的作用,并且可以无锁访问。Go 中是每个 P 拥有 1 个 mcache。在 Go 程序中,当前最多有 GOMAXPROCS 个线程在运行,所以最多需要 GOMAXPROCS 个 mcache 就可以保证各线程对 mcache 的无锁访问,线程的运行又是与 P 绑定的,把 mcache 交给 P 刚刚好。
  • mcentral: 所有线程共享的缓存,需要加锁访问。按 Span class 对 Span 分类,然后串联成链表,当 mcache 的某个 class 的 Span 的内存被分配光时,它会向 mcentral 申请 1 个当前级别的 Span。
  • mheap: 堆内存的抽象,把从 OS 申请出的内存页组织成 Span,并保存起来。当 mcentral 的 Span 不够用时会向 mheap 申请内存,而 mheap 的 Span 不够用时会向 OS 申请内存。mheap 向 OS 的内存申请是按页来的,然后把申请来的内存页生成 Span 组织起来,同样也是需要加锁访问的。mheap 把 Span 组织成了树结构,共 2 棵树,然后把 Span 分配到 heapArena 进行管理,它包含地址映射和 span 是否包含指针等位图,这样做的主要原因是为了更高效的利用内存:分配、回收和再利用。

大小对象

对象分类

  • 小对象(<= 32KB)直接在 mcache 中分配
  • 大对象(> 32KB)直接从 mheap 中分配
阅读全文 »

UDP 协议仅仅在提供了复用/分解功能和少量的差错检验

1. UDP 报文结构

1
2
3
4
5
6
7
8
9
10
// 代码示例,真正报文结构为二进制
type struct udp {
// header
source_port int16 //源端端口
target_port int16 //目的端口
length int16 //长度
checksum int16 //检验和
// body
data any
}

头部共四个字段,每个字段由两个字节组成,共计 8 个字节

2. UDP 检验和

计算检验和时,加入伪头部

1
2
3
4
5
6
7
type struct udp_virtual_header {
source_ip int32
target_ip int32
_ int8
udp_type int8
udp_length int16
}

将伪头部和报文中所有 16bit 字(2Byte)相加,data 最后不足 16bit 补零。溢出位回卷(回卷:溢出位和 16bit 相加),然后进行反码运算。
在校验时,将伪头部和报文中所有 16bit 字相加,和为 1111111111111111 则校验成功,反之失败,说明报文受损。

3. UDP 相对于 TCP 的优势

  • 没有拥塞控制,更适合及时性质的传输
  • 无需建立连接,相比于 TCP 有更低的时延
  • 无状态连接,无需像 TCP 一般维护连接的参数(例如:接受和发送缓存、拥塞控制参数、序号和确认号等)
  • 首部开销更小,UDP 头部 8Byte, TCP 首部 20Byte

1. 概念

2. HTTP 报文

1

3. 持续连接和非持续连接

  • HTTP/1.0 默认使用非持续连接,如要使用持续连接,在报文头部添加Connection: keep-alive

  • HTTP/1.1 默认使用持续连接,如要使用非持续连接,在报文头部添加Connection: close

preview

1. Linux 系统代理设置

linux 的系统代理是通过设置 http_proxy、https_proxy、all_proxy 等环境变量来实现。本人用过的如下两种。

  • 环境变量写入/etc/environment
    • 优点:许多程序都能走代理,不需要额外设置。
    • 缺点:所有 HTTP 请求都会走代理,从而导致离你的代理服务器较远的请求会很慢。
  • 设置alias proxy="all_proxy=socks://127.0.0.1:1080"
    • 优点:在终端中需要代理时,在命令前加上proxy即可。
    • 缺点:许多程序无法使用代理,例如 steam。浏览器也需要安装代理管理插件,像 SwitchyOmega。

Tips: 建议自行设置DNS服务器,根据不同的网络管理,在不同的配置文件中配置DNS服务器

  • NetworkManager: 在/etc/resolv.conf中添加nameserver 8.8.8.8nameserver 8.8.4.4
  • systemd-resolved: 在/etc/systemd/resolved.conf中添加或修改
1
2
[Resolve]
DNS=8.8.8.8

2. Privoxy

privoxy 是一个 HTTP 协议过滤代理。这里不作赘述。想要了解的可以到Archwiki
我们的目的是用过 privoxy 实现 PAC 代理的效果。

阅读全文 »

1. pprof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"log"
"net/http"
// import pprof to init
_ "net/http/pprof"
)

func main() {
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}

1.1 web

http 访问http://localhost:8080/debug/pprof可以查看到如下内容
pprof

  • 内存分配
  • goroutine数量
  • 堆分配
  • 资源竞争
  • 线程创建

1.2 go tool

1
2
3
$ go tool pprof -http=:8000 http://127.0.0.1:8080/debug/pprof/profile
$ go tool pprof -http=:8000 cpu.out
$ go tool pprof -http=:8000 pprof.XXX.samples.cpu.001.pb.gz

开启另一个web服务分析获取的信息,浏览器打开http://localhost:8000即可查看到如下页面
pprof
并包含有火焰图

本篇文章是学习go-advice时所写,包含了大部分写 go 需要注意的点

1. 谏言

  • 不要通过共享内存进行通信,通过通信共享内存
  • 并发不是并行
  • 管道用于协调;互斥量(锁)用于同步
  • 接口越大,抽象越弱
  • 利用好零值
  • 空结构interface{}any没有任何类型约束
  • gofmt 的风格不是人们最喜欢的,但 gofmt 是每个人的最爱
  • 一点点重复比引入一点点依赖更好
  • 始终使用构建标记保护系统调用和 cgo
  • cgo 不是 go
  • 使用unsafe不能保证能如期运行
  • 清晰比聪明更好
  • 反射永远不清晰
  • 错误是值
  • 不要只检查错误,还要优雅的处理它们
  • 设计架构,命名组件,文档记录细节
  • 文档是供用户使用的
  • 不要(在生产环境)使用panic()
阅读全文 »

前言

安装 ArchLinux,起因是因为我的这台 XiaoMI Air13 老古董,在 windows 环境下跑个 IDEA 卡的不行,严重影响编程效率,而且只有 8G 内存,实在是不想把内存分配给一些无用的程序。
起初对于系统的了解是皮毛的皮毛,所以没有选择裸机安装。当然,我也没有用虚拟机测试,因为我的硬盘也不够用,所以我起初选择的是Manjaro + i3wm 的组合安装镜像,不用输入命令,有安装引导界面,很适合新手使用。当然还有跟多炫酷桌面管理,比如 GNOME、PLASMA、XFCE 等,我选择 i3 的理由是内存占用小。慢慢的我也喜欢上了平铺式窗口管理器。有需要的进入后面的链接观看。🔗
Manjaro 的配置可以说相当完善,相对于 Arch 稳定了不少(插句嘴,Manjaro 是 Arch 的衍生版),我也用了一段时间,但是后面就会有些小问题,Manjaro 的 Wiki 相对于 Arch 比较差,所以每次有问题就会去 ArchWiki 中寻找解决方法,但是许多相同的问题并不能得到结局。半年后我还是选择重装了 Arch 系统。
不想听我逼逼赖赖的同学可以去最全 Arch Linux 安装教程——打造真正属于你的操作系统,这个老哥的视频相当到位,配合弹幕食用更佳。

阅读全文 »