golang使用arena进行内存控制为什么会出现无效访问?

在golang中,尝试使用arena进行手动内存释放

代码:

package main

import (
    "arena"
    "encoding/json"
    "fmt"
    "runtime"
    "strconv"
    "strings"

    "github.com/glebarez/sqlite"
    "github.com/gofiber/fiber/v2"
    "gorm.io/gorm"
)

type RoadDeviceVehicle struct {
    RoadPoint string
}

type TT struct {
    PointX    string `json:"pointX"`
    PointY    string `json:"pointY"`
    Timestamp string `json:"timestamp"`
}

type TTT struct {
    PointX    float32
    PointY    float32
    Timestamp int64
}

func main() {
    db, err := gorm.Open(sqlite.Open("road_device_vehicle.db"), &gorm.Config{})
    if err != nil {
        fmt.Println(err)
        panic("failed to connect database")
    }

    tmem := arena.NewArena()
    roadVehicles := arena.MakeSlice[RoadDeviceVehicle](tmem, 0, 1500000)
    db.Table("road_device_vehicle").Select("road_point").Find(&roadVehicles)

    tts := arena.MakeSlice[[]TT](tmem, len(roadVehicles), len(roadVehicles))
    for idx := range roadVehicles {
        tts[idx] = make([]TT, 0, 20)
        s := strings.Replace(roadVehicles[idx].RoadPoint, "\\", "", -1)
        json.Unmarshal([]byte(s), &tts[idx])
    }

    tttsmem := arena.NewArena()
    ttts := arena.MakeSlice[[]TTT](tttsmem, len(tts), len(tts))
    for idx := range tts {
        ttts[idx] = arena.MakeSlice[TTT](tttsmem, len(tts[idx]), len(tts[idx]))
        for idx2 := range tts[idx] {
            t, err := strconv.ParseFloat(tts[idx][idx2].PointX, 32)
            if err != nil {
                fmt.Println(err)
                return
            }
            ttts[idx][idx2].PointX = float32(t)

            t, err = strconv.ParseFloat(tts[idx][idx2].PointY, 32)
            if err != nil {
                fmt.Println(err)
                return
            }
            ttts[idx][idx2].PointY = float32(t)

            ttts[idx][idx2].Timestamp, err = strconv.ParseInt(tts[idx][idx2].Timestamp, 10, 64)
            if err != nil {
                fmt.Println(err)
                return
            }
        }
    }

    tmem.Free()
    runtime.GC()

    app := fiber.New()

    app.Get("/db", func(c *fiber.Ctx) error {
        fmt.Println(ttts[0][0])
        return c.SendString("Hello, World!")
    })

    app.Listen(":3000")
}

在以上代码中,RoadPoint字符串类似于如下:

[{\"pointX\":\"212.13\",\"pointY\":\"61724922.54\",\"timestamp\":\"1704970150739\"},{\"pointX\":\"228.30\",\"pointY\":\"61728661.51\",\"timestamp\":\"1704970152777\"},{\"pointX\":\"188.10\",\"pointY\":\"61732802.98\",\"timestamp\":\"1704970154739\"},{\"pointX\":\"188.04\",\"pointY\":\"61737013.77\",\"timestamp\":\"1704970156738\"},{\"pointX\":\"151.57\",\"pointY\":\"61742215.45\",\"timestamp\":\"1704970158777\"}]

使用tmem.Free()之后,读取ttts字段会报错,信息如下:

accessed data from freed user arena 0x40c0007eff70
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0x40c0007eff70 pc=0xfdf390]

请问为什么会出现这种情况,ttts不是使用tttsmem分配的吗,为什么我free了tmem后会导致ttts无法读取

使用普通的make,则可以依靠垃圾回收正确获取结果

阅读 414
avatarAI BotBETA

在Go语言中,arena 是一个用于管理内存分配和释放的库。当你使用 arena 分配内存时,你需要明确地管理这些内存的生命周期。在你提供的代码中,你创建了两个 arena,tmemtttsmem,并分别使用它们来分配内存。

问题在于,你在释放 tmem 后尝试访问了通过 tttsmem 分配的内存。虽然 ttts 是使用 tttsmem 分配的,但 ttts 中的元素(即 TTT 切片)是通过 tmem 分配的。当你调用 tmem.Free() 时,它释放了所有由 tmem 分配的内存,这包括 tts 中的所有 TT 切片。由于 ttts 中的元素引用了这些已经被释放的 TT 切片,因此当你尝试访问 ttts 时,会出现无效访问错误。

为了解决这个问题,你需要确保正确地管理内存的生命周期。你可以考虑以下两种方法之一:

  1. 不释放 tmem:如果你不需要释放 tmem 中的内存,你可以简单地不去调用 tmem.Free()。这样,tts 中的 TT 切片将保持有效,ttts 也可以正常访问它们。
  2. 使用不同的 arena:如果你想释放 tmem 中的内存,但又想保留 ttts 中的数据,你可以为 ttsttts 使用不同的 arena。这样,当你释放 tmem 时,ttts 中的数据不会受到影响。

请注意,使用 arena 进行内存管理需要谨慎处理内存的生命周期。确保你了解何时分配和释放内存,以及这些操作如何影响你的程序。如果可能的话,使用 Go 的内置内存管理功能(如 make 和垃圾回收)可能更简单、更安全。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏