Golang内存泄漏问题紧急求助!

Golang内存泄漏问题紧急求助!

// test project main.go
package main

import (
	"fmt"
	"math/big"
	"os"
	"runtime"
	"sync"
	"time"
)

var file, _ = os.Create("20-1.txt")
var round = 1
var input = 3

var size = 1048576
var block = 8192

var s_box = [16][16]int{
	{16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //0
	{0, 0, 0, 4, 0, 0, 0, 4, 0, 4, 0, 0, 0, 4, 0, 0},  //1
	{0, 0, 0, 2, 0, 4, 2, 0, 0, 0, 2, 0, 2, 2, 2, 0},  //2
	{0, 2, 0, 2, 2, 0, 4, 2, 0, 0, 2, 2, 0, 0, 0, 0},  //3
	{0, 0, 0, 0, 0, 4, 2, 2, 0, 2, 2, 0, 2, 0, 2, 0},  //4
	{0, 2, 0, 0, 2, 0, 0, 0, 0, 2, 2, 2, 4, 2, 0, 0},  //5
	{0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 0, 4, 2, 0, 0, 4},  //6
	{0, 4, 2, 0, 0, 0, 2, 0, 2, 0, 0, 0, 2, 0, 0, 4},  //7
	{0, 0, 0, 2, 0, 0, 0, 2, 0, 2, 0, 4, 0, 2, 0, 4},  //8
	{0, 0, 2, 0, 4, 0, 2, 0, 2, 0, 0, 0, 2, 0, 4, 0},  //9
	{0, 0, 2, 2, 0, 4, 0, 0, 2, 0, 2, 0, 0, 2, 2, 0},  //A
	{0, 2, 0, 0, 2, 0, 0, 0, 4, 2, 2, 2, 0, 2, 0, 0},  //B
	{0, 0, 2, 0, 0, 4, 0, 2, 2, 2, 2, 0, 0, 0, 2, 0},  //C
	{0, 2, 4, 2, 2, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0},  //D
	{0, 0, 2, 2, 0, 0, 2, 2, 2, 2, 0, 0, 2, 2, 0, 0},  //E
	{0, 4, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4}}  //F

func SPN(a []big.Rat, b [][]big.Rat, base int) {
	for k := base * block; k < (base+1)*block; k++ {
		//fmt.Println(k)
		if a[k].Cmp(big.NewRat(0, 1)) > 0 {
			first := k / 65536
			second := k / 4096 % 16
			third := k / 256 % 16
			fourth := k / 16 % 16
			fifth := k % 16
			for m := 0; m < 16; m++ {
				for n := 0; n < 16; n++ {
					for x := 0; x < 16; x++ {
						for y := 0; y < 16; y++ {
							for z := 0; z < 16; z++ {
								per := int64(s_box[first][m] * s_box[second][n] * s_box[third][x] * s_box[fourth][y] * s_box[fifth][z])
								if per > 0 {
									temp := big.NewRat(per, 1048576)
									temp.Mul(temp, &a[k])
									tt := m*65536 + n*4096 + x*256 + y*16 + z
									var pos [20]int
									for i := 0; i < 20; i++ {
										pos[i] = tt % 2
										tt >>= 1
										tt = 524288*pos[19] + 16384*pos[18] + 512*pos[17] + 16*pos[16] + 262144*pos[15] + 8192*pos[14] + 256*pos[13] + 8*pos[12] + 131072*pos[11] + 4096*pos[10] +
											128*pos[9] + 4*pos[8] + 65536*pos[7] + 2048*pos[6] + 64*pos[5] + 2*pos[4] + 32768*pos[3] + 1024*pos[2] + 32*pos[1] + 1*pos[0]
										b[base][tt].Add(temp, &b[base][tt])
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

func main() {
	start := time.Now()
	runtime.GOMAXPROCS(128)

	var wg sync.WaitGroup
	var a = make([]big.Rat, size)
	var b = make([][]big.Rat, 128)
	var c = make([]big.Rat, size)

	var dot string
	for i := 0; i < 128; i++ {
		b[i] = make([]big.Rat, size)
	}

	a[input].SetFrac(big.NewInt(1), big.NewInt(1))
	change := a[:]

	fmt.Printf("%v", a[input].String())
	for i := 0; i < round; i++ {
		for j := 0; j < 128; j++ {
			//fmt.Printf("%d", j)
			wg.Add(1)
			go func(wg *sync.WaitGroup, j int) {
				//SPN(a, b[j], j)
				SPN(a, b, j)
				for i := block * j; i < block*(j+1); i++ {
					a[i].SetFrac(big.NewInt(0), big.NewInt(1))
				}
				for i := 0; i < size; i++ {
					c[i].Add(&c[i], &b[j][i])
					b[j][i].SetFrac(big.NewInt(0), big.NewInt(1))
				}
				defer wg.Done()
			}(&wg, j)
		}
		wg.Wait()
		change = a
		a = c
		c = change
		/*for i := 0; i < 128; i++ {
			for j := 0; j < size; j++ {
				a[j].Add(&a[j], &b[i][j])
			}
		}
		for i := 0; i < 128; i++ {
			for j := 0; j < size; j++ {
				b[i][j].SetFrac(big.NewInt(0), big.NewInt(1))
			}
		}*/
	}

	for i := 1; i < size; i++ {
		dot = a[i].FloatString(20)
		file.WriteString(dot + "\r\n")
	}
	elapsed := time.Since(start)
	fmt.Printf("\nTook %s\n", elapsed)
}

结果如下:

TIM截图20180921105928


更多关于Golang内存泄漏问题紧急求助!的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

是的,可能是内存不足。因为我只有8GB内存

更多关于Golang内存泄漏问题紧急求助!的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


非常感谢您有用的回答,我确实学到了很多知识。我想知道您是如何了解内存使用比例的?

你的环境是什么(架构、操作系统等)?看起来你只是向操作系统请求了超出其愿意分配的内存。

你是否知道如何快速将 b[][] 设置为 (“0/1”),因为在我的问题中,生成它的速度是最慢的

我在我的Linux系统上尝试运行它,该系统具有8GB内存和8GB交换空间。在系统将其终止之前,它消耗了所有的内存和交换空间。

Dench:

我想知道你是如何确定内存使用比例的?

我不太明白您的问题。我是如何计算 b 的大小的?我只是将数组的大小乘以其包含的每个数组的大小,再乘以 big.Rat 类型的大小。

Dench:

以及你是否知道如何快速将 b 设置为 (“0/1”),因为在我的问题中,生成它的速度是最慢的

抱歉,这个问题我也没理解。您能再解释一下吗?

我理解您是在询问错误信息的含义,而不是在查找所有内存分配发生的位置。所以我运行并进行了性能分析,如果您感兴趣的话。这是将 GOMAXPROCS 设置为 4 时的结果:

memprof

这是原始 128 GOMAXPROCS 的情况(运行速度快了几秒,但几乎使您的系统无法使用):

memprof2

但我认为您不应该感到惊讶,您确实在分配大量内存。以 b 为例,它是 128 * 1048576 * 32(big.Rat 的大小)= 4294967296 字节,即仅此一项就超过 4 GB(或者正好是 4 GiB,即 2^32 字节,如预期的那样)。

分析您的代码,我发现了几个可能导致内存泄漏的问题:

主要问题分析

1. big.Rat 对象的大量创建和操作

SPN 函数中,每次循环都创建新的 big.Rat 对象,这会导致大量内存分配:

temp := big.NewRat(per, 1048576)  // 每次循环都创建新对象
temp.Mul(temp, &a[k])

2. 嵌套循环过深

5层嵌套循环会产生大量迭代,每次迭代都涉及内存分配:

for m := 0; m < 16; m++ {
    for n := 0; n < 16; n++ {
        for x := 0; x < 16; x++ {
            for y := 0; y < 16; y++ {
                for z := 0; z < 16; z++ {
                    // 内存分配操作
                }
            }
        }
    }
}

3. 切片 b 的内存管理

b 是一个二维切片,每个元素都是 big.Rat,在并发环境下频繁操作:

var b = make([][]big.Rat, 128)
for i := 0; i < 128; i++ {
    b[i] = make([]big.Rat, size)  // 1048576 个 big.Rat
}

修复方案

1. 重用 big.Rat 对象

func SPN(a []big.Rat, b [][]big.Rat, base int) {
    // 预分配临时变量
    temp := new(big.Rat)
    result := new(big.Rat)
    
    for k := base * block; k < (base+1)*block; k++ {
        if a[k].Cmp(big.NewRat(0, 1)) > 0 {
            // ... 循环逻辑保持不变
            
            for z := 0; z < 16; z++ {
                per := int64(s_box[first][m] * s_box[second][n] * s_box[third][x] * s_box[fourth][y] * s_box[fifth][z])
                if per > 0 {
                    // 重用 temp 而不是新建
                    temp.SetFrac64(per, 1048576)
                    temp.Mul(temp, &a[k])
                    
                    tt := m*65536 + n*4096 + x*256 + y*16 + z
                    var pos [20]int
                    for i := 0; i < 20; i++ {
                        pos[i] = tt % 2
                        tt >>= 1
                        tt = 524288*pos[19] + 16384*pos[18] + 512*pos[17] + 16*pos[16] + 262144*pos[15] + 8192*pos[14] + 256*pos[13] + 8*pos[12] + 131072*pos[11] + 4096*pos[10] +
                            128*pos[9] + 4*pos[8] + 65536*pos[7] + 2048*pos[6] + 64*pos[5] + 2*pos[4] + 32768*pos[3] + 1024*pos[2] + 32*pos[1] + 1*pos[0]
                        
                        // 重用 result 进行加法操作
                        result.Add(&b[base][tt], temp)
                        b[base][tt].Set(result)
                    }
                }
            }
        }
    }
}

2. 优化内存清理

func main() {
    // ... 初始化代码
    
    for i := 0; i < round; i++ {
        for j := 0; j < 128; j++ {
            wg.Add(1)
            go func(wg *sync.WaitGroup, j int) {
                defer wg.Done()
                SPN(a, b, j)
                
                // 使用 SetInt64 而不是 SetFrac 来避免内存分配
                for i := block * j; i < block*(j+1); i++ {
                    a[i].SetInt64(0)
                }
                for i := 0; i < size; i++ {
                    c[i].Add(&c[i], &b[j][i])
                    b[j][i].SetInt64(0)  // 重用对象而不是创建新对象
                }
            }(&wg, j)
        }
        wg.Wait()
        change = a
        a = c
        c = change
    }
    
    // ... 后续代码
}

3. 添加内存监控

func printMemStats() {
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
    fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
    fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
    fmt.Printf("\tNumGC = %v\n", m.NumGC)
}

func bToMb(b uint64) uint64 {
    return b / 1024 / 1024
}

// 在 main 函数中定期调用
func main() {
    go func() {
        for {
            time.Sleep(5 * time.Second)
            printMemStats()
        }
    }()
    // ... 原有逻辑
}

关键改进点

  1. 重用 big.Rat 对象:避免在循环中频繁创建新对象
  2. 使用 SetInt64 替代 SetFrac:减少内存分配
  3. 预分配临时变量:在循环外创建变量并在循环内重用
  4. 添加内存监控:帮助诊断内存使用情况

这些修改应该能显著减少内存分配和垃圾回收压力,解决内存泄漏问题。

回到顶部