go基础<5>

60 阅读4分钟

暂且提一下 内存管理 Go语言使用垃圾回收器进行内存管理,这个垃圾回收器主要负责管理堆(heap)中的内存。Go语言使用了三色标记清除算法(three-color marking algorithm)来实现垃圾回收。具体来说,垃圾回收器会跟踪程序中的所有指针,标记出所有可达的内存块,然后清除所有不可达的内存块。这种方法可以确保不会有内存泄漏,也能有效地避免内存碎片问题。另外,Go语言还提供了一些手动内存管理的工具,如sync.Pool和unsafe包,可以帮助开发者更加高效地使用内存。

Go语言的内存管理机制,它使用了自动垃圾回收(GC)的方式,通过跟踪对象的引用关系来判断哪些对象可以被释放。具体来说,Go语言中的内存分为堆和栈两部分,堆是非常灵活的,大小不固定,但是需要手动分配和释放,而栈则是固定大小的,每个协程都有一个独立的栈,它用于存放函数调用时的数据和局部变量等。Go语言利用指针来实现引用,在堆上分配需要手动进行内存分配,使用new和make函数去创建,不使用时需要手动回收以避免内存泄露。

举个栗子:

package main

import "fmt"

func main() {
    // 分配一个整形变量的指针
    p := new(int)
    // 给指针所指的变量赋值
    *p = 123
    // 打印指针的值和所指向的变量的值
    fmt.Printf("p = %v, *p = %v\n", p, *p)
    // 释放指针所指向的变量
    // 等价于: delete p
    p = nil
}

这个例子中,我们使用了new函数来分配一个整型变量的指针,然后使用*操作符给指针所指的变量赋值,并且打印出了指针的值和所指向的变量的值,最后手动释放了指针所指向的变量。

那么,继续说吧

  1. make 和 new 函数 经常用于申请内存并且初始化数据。
// 创建一个包含5个元素的切片
   a := make([]int, 5)

   // 创建一个长度为5并且预分配10个元素的切片
   b := make([]int, 5, 10)

   // 创建一个指向 int 类型的指针
   var c *int = new(int)
  1. append 函数 数组或切片增长时,使用append函数,它会自动分配新的内存并将数据添加到数组或切片。
// 将一个元素添加到切片
   a = append(a, 1)

   // 将多个元素添加到切片
   a = append(a, 2, 3, 4)

   // 将一个切片添加到另一个切片
   b = append(b, a...)

3.当一个变量不再需要时,Go的垃圾回收器会自动释放它所占用的内存。

var x *int = new(int)
   *x = 4

   // 将x这个指针变量赋值为nil,就会标记x所指向的内存可以回收
   x = nil
  1. sync.Pool 线程安全的内存池 有时候我们需要频繁地创建和销毁一些对象,但是这个过程会导致频繁调用内存分配器,耗费宝贵的计算资源。为了避免这种情况,我们可以使用 sync.Pool 来实现内存的重用。
// 定义一个池,用于存储字符串
   var stringPool = sync.Pool{
       New: func() interface{} {
           return ""
       },
   }

   // 获取一个字符串
   s := stringPool.Get().(string)

   // 将字符串拼接起来
   s += "hello"
   s += "world"

   // 将字符串放回池中,以便重复利用
   stringPool.Put(s)

这里提一嘴 go语言的缓存机制

包括两部分:L1缓存和L2缓存。L1缓存是CPU自带的缓存,大小一般为4-64KB,速度非常快,通常用于缓存CPU中经常使用的数据。L2缓存则是在CPU外部,大小为1-8MB,速度相对慢一些,但是比内存要快得多,通常用于缓存CPU中不太经常使用的数据。在Go语言中,可以通过在程序中使用sync.Pool来创建对象池,从而利用对象的重用来提高程序的性能。另外,Go语言中还有一种叫做channel的同步机制,可以用来在不同的goroutine之间传递数据和控制程序的执行顺序。

channel同步机制,一般是指在并发编程中用于协调多个goroutine之间的通信的机制。channel是一种有缓存和无缓存的两种类型,用于不同的场景。

无缓存的channel在发送和接收值时,都会阻塞当前goroutine,直到有另一个goroutine做相反的操作。这保证了两个goroutine在同一个时间点是同步的。

有缓存的channel则允许一定数量的值被缓存,发送操作只有在缓存区已满时才会阻塞,接收操作只有在缓存区空时才会阻塞。这比无缓存的channel更加灵活,但也更容易出现死锁等问题,需要谨慎使用。

在使用channel时,需要确保对channel的访问是并发安全的,避免出现竞态条件等问题。此外,go语言还提供了select语句用于同时处理多个channel,更加方便的协调多个goroutine之间的通信和同步。