2012-03-26 5 views
3

Я пытаюсь сделать то, что изначально считалось довольно простым. А именно:Повторные вызовы image.png.Decode() приводит к ошибкам памяти

Для каждого файла в списке входных файлов:

  1. открыть файл с png.Decode()
  2. сканировать каждый пиксель в файле и тест, чтобы увидеть, если это «серый ».
  3. Возвратите процент «серых» пикселей на изображении.

Это функция Я звоню:

func greyLevel(fname string) (float64, string) { 
    f, err := os.Open(fname) 
    if err != nil { 
      return -1.0, "can't open file" 
    } 
    defer f.Close() 

    i, err := png.Decode(f) 
    if err != nil { 
      return -1.0, "unable to decode" 
    } 

    bounds := i.Bounds() 

    var lo uint32 = 122 // Low grey RGB value. 
    var hi uint32 = 134 // High grey RGB value. 
    var gpix float64 // Grey pixel count. 
    var opix float64 // Other (non-grey) pixel count. 
    var tpix float64 // Total pixels. 

    for x := bounds.Min.X; x < bounds.Max.X; x++ { 
      for y := bounds.Min.Y; y < bounds.Max.Y; y++ { 
        r, g, b, _ := i.At(x, y).RGBA() 
        if ((r/255)-1 > lo && (r/255)-1 < hi) && 
          ((g/255)-1 > lo && (g/255)-1 < hi) && 
          ((b/255)-1 > lo && (b/255)-1 < hi) { 
          gpix++ 
        } else { 
          opix++ 
        } 
        tpix++ 
      } 
    } 
    return (gpix/tpix) * 100, "" 
} 

func main() { 
    srcDir := flag.String("s", "", "Directory containing image files.") 
    threshold := flag.Float64("t", 65.0, "Threshold (in percent) of grey pixels.") 
    flag.Parse() 

    dirlist, direrr := ioutil.ReadDir(*srcDir) 
    if direrr != nil { 
      log.Fatalf("Error reading %s: %s\n", *srcDir, direrr) 
    } 

    for f := range dirlist { 
      src := path.Join(*srcDir, dirlist[f].Name()) 

      level, msg := greyLevel(src) 

      if msg != "" { 
        log.Printf("error processing %s: %s\n", src, msg) 
        continue 
      } 

      if level >= *threshold { 
        log.Printf("%s is grey (%2.2f%%)\n", src, level) 
      } else { 
        log.Printf("%s is not grey (%2.2f%%)\n", src, level) 
      } 
    } 
} 

файлы относительно малы (960x720, 8-битный RGB)

Я зову ioutil.ReadDir(), чтобы создать список файлов, зацикливание над срезом и вызов greyLevel().

После около 155 файлов (из списка> 4000) Паника скрипт с:

runtime: memory allocated by OS not in usable range 
runtime: out of memory: cannot allocate 2818048-byte block (534708224 in use) 
throw: out of memory 

Я полагаю, что есть что-то просто мне не хватает. Я думал, что Go будет выделять память, выделенную в greyLevels(), но я не думаю?

Развейте:

После вставки runtime.GC() после каждого вызова greyLevels, использование памяти сглаживает. Вчера вечером я собрал около 800 изображений, затем остановился. Сегодня я позволяю ему работать по всему набору входных данных, примерно 6800 изображений.

После 1500 изображений, сверху выглядит следующим образом:

top - 10:30:11 up 41 days, 11:47, 2 users, load average: 1.46, 1.25, 0.88 
Tasks: 135 total, 2 running, 131 sleeping, 1 stopped, 1 zombie 
Cpu(s): 49.8%us, 5.1%sy, 0.2%ni, 29.6%id, 15.0%wa, 0.0%hi, 0.3%si, 0.0%st 
Mem: 3090304k total, 2921108k used, 169196k free,  2840k buffers 
Swap: 3135484k total, 31500k used, 3103984k free, 640676k cached 

    PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
28474 mtw  20 0 2311m 1.8g 412 R 99 60.5 16:48.52 8.out 

И оставался стабильным после обработки еще 5000 изображений.

+0

Пожалуйста, добавьте версию Go, версию ОС и информацию о архитектуре CPU. Также полезен полный код, который воспроизводит проблему. – zzzz

+0

@jnml Вы можете вывести архитектуру процессора из «из памяти ... (534708224 в использовании)». –

+0

@ Атом: Intel или ARM? ;-) – zzzz

ответ

1

похоже, что вы используете 32-разрядную машину. Вероятно, в программе заканчивается память, потому что сборщик мусора Go является консервативным. Консервативный сборщик мусора может не обнаружить, что некоторая область памяти больше не используется. Там в настоящее время нет обходного пути для этого в Go отличной избегая структуры данных, что сборщик мусор не может обрабатывать (например: struct {...; binaryData [256]byte}) программу

Try называть runtime.GC() в каждой итерации цикла, в котором вы вызываете функцию greyLevel. Возможно, это поможет программе обработать больше изображений.

Если при вызове runtime.GC() не удается улучшить ситуацию, вы можете изменить свою стратегию, чтобы программа обрабатывала меньшее количество PNG-файлов за один проход.

+0

runtime.GC() делает трюк. Резидентная память поднимается до 568M, затем сохраняется. Слишком плохо, обходной путь, как это требуется, но я уверен, что разработчики Go создадут исправление. Благодаря! – mtw

+0

Я не думаю, что утверждение о том, что GC не может обрабатывать структуру выше и о необходимости избегать некоторых конкретных данных в Go, истинно (по крайней мере в наши дни). – zzzz

+0

@mtw: GC должен автоматически включаться. Если программе требуется вручную вызывать runtime.GC(), чтобы работать, но я подозреваю, что ошибка во время выполнения может быть вокруг. – zzzz

0

Похоже на выпуск 3173, который был недавно исправлен. Не могли бы вы повторить последнюю неделю? (Предполагая, что вы используете некоторую версию до 2012-03-07).

+0

Хорошо, круто. Я запускаю версию go week.2012-03-22, и теперь процесс распределяет память нормально, но кажется, что мусор не собирается собираться. – mtw

Смежные вопросы