2017-08-15 34 views
0

我试图用Java ImageIO复制大量图像。每个副本都会调整原始图片的大小。由于图像集的大小很大(60,000)。我尝试使用多线程来解决问题。这里是代码:产生多线程图像的java.lang.OutOfMemory异常

package generate.image 

import scala.util.Random._ 
import scala.math._ 
import java.io.File 
import java.io.PrintWriter 
import java.util.concurrent.{ExecutorService, TimeUnit, Executors} 
import java.awt.Image 
import java.awt.image.BufferedImage 
import javax.imageio.ImageIO 

class ImageResizer{ 
    def resizeImage(srcImgPath: String, distImgPath: String, width: Int, height: Int){ 
     val srcFile: File = new File(srcImgPath) 
     val srcImg: Image = ImageIO.read(srcFile) 
     val buffImg: BufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB) 

     buffImg.getGraphics().drawImage(
       srcImg.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 
       0, null 
      ) 
     ImageIO.write(buffImg, "JPEG", new File(distImgPath)) 
    } 
} 

class ImageWorker(imgSrc: String, imgName: String, width: Int, height: Int) extends Runnable{ 
    override def run(): Unit = { 
     val resizer = new ImageResizer() 
     resizer.resizeImage(imgSrc, imgName, width, height); 
    } 
} 

object ImageGenerate { 

    def main(args:Array[String]): Unit = { 
     // parameters 
     val dirName = args(0) 
     val images = new File(dirName).listFiles.filter(_.getName.endsWith(".JPEG")) 
     val imgCnt = images.length 


     // threadpool 
     val pool = Executors.newFixedThreadPool(25) 


     // copy with norm 
     for(i <- 0 until imgCnt){ 
      for(cnt <- 1 to 20){ 
       val width = nextInt(200) + 300 
       val height = nextInt(200) + 300 
       val imgSrc: String = images(i).getAbsolutePath 
       val imgName: String = "img/%s_%d_%d_%d.JPEG".format(splitFilename(images(i).getName), width, height, cnt) 
       pool.execute(new ImageWorker(imgSrc, imgName, width, height)) 
      } 
     } 


     pool.shutdown() 
     pool.awaitTermination(Long.MaxValue, TimeUnit.NANOSECONDS) 
    } 

    // split file name 
    def splitFilename(fileName: String) = { 
     fileName.substring(0, fileName.lastIndexOf(".")) 
    } 

} 

ImageResizer复制工作。它将图像读入BufferedImage,将其重新调整为新的BufferedImage,最后写入JPEG文件。

ImageWorker线程是否工作。它由ExecuteServive中的工作线程执行。

ImageGenerate做调度工作。它读取args(0)(第一个参数)中的所有图像文件,生成新的随机宽度和高度,并将新作业提交至pool

编译并运行:scalac ImageGenerate.scalascala generate.image.ImageGenerate test。图像的大小平均为150kb。

正在运行,程序将引发java.lang.OutOfMemoryErrorError Image 有时,出现Exception in thread "pool-1-thread-36" java.lang.OutOfMemoryError: GC overhead limit exceeded错误。

如果我设置参数-J-Xmx2048m,程序将平稳运行。但是,我只运行400个图像。我的代码有没有优化?

感谢您分享您的想法,祝好。

回答

1

你应该叫dispose()

像这样(未经)

val graphics = buffImg.getGraphics() 
    graphics.drawImage(
    srcImg.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 
    0, null 
) 
    ImageIO.write(buffImg, "JPEG", new File(distImgPath)) 
    graphics.dispose() 
+0

感谢您分享您的想法。我认为我向线程池提交的任务不是流量控制的。当ExecuteService中有太多任务正在等待时,内存就会耗尽。因此,我使用信号量来限制提交的速度。它的工作很多。 – jiexray