Почему чтение файла построчно занимает больше памяти? - PullRequest
2 голосов
/ 26 сентября 2019

Я пытался прочитать большой файл в этом формате:

a string key, 200 values separated by comma

И записать его на карту.

Я написал этот код:

package main

import (
    "bufio"
    "unsafe"
    "fmt"
    "log"
    "os"
    "runtime"
    "strings"
)

func main() {

    file, err := os.Open("file_address.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    mp := make(map[string]float32)
    var total_size int64 = 0
    scanner := bufio.NewScanner(file)
    var counter int64 = 0

    for scanner.Scan() {
        counter++
        sliced := strings.Split(scanner.Text(), ",")
        mp[sliced[0]] = 2.2
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("loaded: %d. Took %d Mb of memory.", counter, total_size/1024.0/1024.0)
    fmt.Println("Loading finished. Now waiting...")

    var ms runtime.MemStats
    runtime.ReadMemStats(&ms)

    fmt.Printf("\n")
    fmt.Printf("Alloc: %d MB, TotalAlloc: %d MB, Sys: %d MB\n",
        ms.Alloc/1024/1024, ms.TotalAlloc/1024/1024, ms.Sys/1024/1024)
    fmt.Printf("Mallocs: %d, Frees: %d\n",
        ms.Mallocs, ms.Frees)
    fmt.Printf("HeapAlloc: %d MB, HeapSys: %d MB, HeapIdle: %d MB\n",
        ms.HeapAlloc/1024/1024, ms.HeapSys/1024/1024, ms.HeapIdle/1024/1024)
    fmt.Printf("HeapObjects: %d\n", ms.HeapObjects)
    fmt.Printf("\n")
}

Вот результат:

loaded: 544594. Took 8 Mb of memory.Loading finished. Now waiting...

Alloc: 2667 MB, TotalAlloc: 3973 MB, Sys: 2831 MB
Mallocs: 1108463, Frees: 401665
HeapAlloc: 2667 MB, HeapSys: 2687 MB, HeapIdle: 11 MB
HeapObjects: 706798

Done!

Хотя ключи занимают всего около 8 МБ, программе требуется около 2,7 ГБ памяти!Кажется, что sliced никогда не удаляется из кучи.Я попытался установить sliced=nil в конце for, но это не помогло.Я прочитал, что если я загружаю весь файл в память, а затем разделяю его, я могу избежать этой проблемы, но мне приходится читать файл построчно, потому что у меня недостаточно памяти для загрузки некоторых больших файлов.файлы.

Почему память занята?Как я могу освободить его после обработки каждой строки?

Ответы [ 2 ]

5 голосов
/ 26 сентября 2019

Для эффективного использования процессора и памяти,

key := string(bytes.SplitN(scanner.Bytes(), []byte(","), 2)[0])
mp[key] = 2.2
1 голос
/ 26 сентября 2019

Я думаю, что нашел проблему!Я нарезаю каждую строку большого файла.Возвращаемое значение []string представляет собой фрагмент, содержащий подстроки исходной строки (строки файла).Теперь проблема в том, что каждая подстрока не является новой строкой.Is это просто slice, в котором хранится ссылка на неразрезанную строку (строку файла!).Я сохраняю sliced[0] для каждой строки, следовательно, я сохраняю ссылку на каждую строку файла.Сборщик мусора не будет касаться строки чтения, потому что у меня все еще есть ссылка на нее.Технически я читаю и сохраняю все строки файла в памяти.

Решение состоит в том, чтобы скопировать нужную мне часть (sliced[0]) в новую строку, фактически теряя ссылку на всю строку.Я сделал это следующим образом:

    sliced := strings.Split(scanner.Text(), ",")
    key_rune_arr := []rune(sliced[0])
    key := string(key_rune_arr) // now key is a copy of sliced[0] without reference to line
    mp[key] = 2.2 //instead of mp[sliced[0]] = 2.2

Программа теперь становится:

package main

import (
    "bufio"
    "unsafe"
    "fmt"
    "log"
    "os"
    "runtime"
    "strings"
)

func main() {

    file, err := os.Open("file_address.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    mp := make(map[string]float32)
    var total_size int64 = 0
    scanner := bufio.NewScanner(file)
    var counter int64 = 0

    for scanner.Scan() {
        counter++
        sliced := strings.Split(scanner.Text(), ",")
        key_rune_arr := []rune(sliced[0])
        key := string(key_rune_arr) // now key is a copy of sliced[0] without reference to line
        mp[key] = 2.2 //instead of mp[sliced[0]] = 2.2
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("loaded: %d. Took %d Mb of memory.", counter, total_size/1024.0/1024.0)
    fmt.Println("Loading finished. Now waiting...")

    var ms runtime.MemStats
    runtime.ReadMemStats(&ms)

    fmt.Printf("\n")
    fmt.Printf("Alloc: %d MB, TotalAlloc: %d MB, Sys: %d MB\n",
        ms.Alloc/1024/1024, ms.TotalAlloc/1024/1024, ms.Sys/1024/1024)
    fmt.Printf("Mallocs: %d, Frees: %d\n",
        ms.Mallocs, ms.Frees)
    fmt.Printf("HeapAlloc: %d MB, HeapSys: %d MB, HeapIdle: %d MB\n",
        ms.HeapAlloc/1024/1024, ms.HeapSys/1024/1024, ms.HeapIdle/1024/1024)
    fmt.Printf("HeapObjects: %d\n", ms.HeapObjects)
    fmt.Printf("\n")
}

Результат такой, какой я хотел:

loaded: 544594. Took 8 Mb id memory.Loading finished. Now waiting...

Alloc: 94 MB, TotalAlloc: 3986 MB, Sys: 135 MB
Mallocs: 1653590, Frees: 1108129
HeapAlloc: 94 MB, HeapSys: 127 MB, HeapIdle: 32 MB
HeapObjects: 545461

Done!
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...