重复调用image.png.Decode()会导致内存不足错误

时间:2012-03-26 07:31:19

标签: image go

我试图做我原先认为非常简单的事情。即:

对于输入文件列表中的每个文件:

  1. 使用png.Decode()
  2. 打开文件
  3. 扫描文件中的每个像素并测试它是否为“灰色”。
  4. 返回图像中“灰色”像素的百分比。
  5. 这是我打电话的功能:

    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()中分配的内存,但我猜不是吗?

    跟进:

    在每次调用greyLevels之后插入runtime.GC()之后,内存使用率都会变为平均值。昨晚我大约拍了800张照片然后停了下来。今天我让它在整个输入集上运行,大约6800张图像。

    在1500张图片之后,top看起来像这样:

    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张图像后保持稳定。

2 个答案:

答案 0 :(得分:1)

您似乎正在使用32位计算机。由于Go的垃圾收集器是保守的,因此程序可能会耗尽内存。保守的垃圾收集器可能无法检测到某些内存区域不再使用。目前在Go程序中没有解决方法,除了避免垃圾收集器无法处理的数据结构(例如:struct {...; binaryData [256]byte}

尝试在调用函数runtime.GC()的循环的每次迭代中调用greyLevel。也许它会帮助程序处理更多图像。

如果调用runtime.GC()无法改善这种情况,您可能需要更改策略,以便程序每次运行时处理的PNG文件数量较少。

答案 1 :(得分:0)

似乎最近修复了问题3173。你能不能用最新的每周重试一次? (假设您现在使用2012-03-07之前的版本)。