ImageIO.read(...) - 它很慢,有更好的方法吗?

时间:2017-05-24 23:39:40

标签: java

我正在加载大量将在我的应用程序中使用的图标。我计划在服务器启动时从jar加载所有这些。但是,由于数百个图像总计超过9MB,因此执行此任务仍需要30秒以上。我现在在一个单独的线程中这样做,但它让我想知道我是否在我的代码中做了一些效率低下的事情。我从SO借用代码将信息加载到我的结构中。我将代码放入测试类并对其进行了分析。 99%的配置文件在ImageIO.read(..)方法上。所以这绝对是瓶颈。下面是该测试类,它应该提供关于我如何使用ImageIO的图片。

public class IconTest {

/**
 * @param args the command line arguments
 * @throws java.net.URISyntaxException
 * @throws java.io.IOException
 */
public static void main(String[] args) throws URISyntaxException, IOException {
    URI uri = IconTest.class.getResource("Icons").toURI();
    Path myPath;
    if (uri.getScheme().equals("jar")) {
        FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
        myPath = fileSystem.getPath("Icons/");
    } else {
        myPath = Paths.get(uri);
    }
    IconFolder root = new IconFolder(myPath.toFile().getName());
    IconFolder parentFolder = root;
    HIcon currentIcon = null;
    IconFolder folder = null;
    HashMap<String,IconFolder> folders = new HashMap<>();
    folders.put(parentFolder.getName(), parentFolder);
    Stream<Path> walk = Files.walk(myPath, 5);
    Iterator<Path> it = walk.iterator();it.next();
    while(it.hasNext()){
        Path path = it.next();
        if(path.toFile().isDirectory()){
            folder = new IconFolder(path.toFile().getName());
            folders.put(folder.getName(), folder);
            String parentName = path.getParent().toFile().getName();
            parentFolder = folders.get(parentName);
            parentFolder.addSubFolder(folder);
            currentIcon =null;
            System.out.println("Directory: " + path);
        }else{
            URL url = path.toUri().toURL();

            ImageIcon icon = new ImageIcon(ImageIO.read(url));
            //Image image = Toolkit.getDefaultToolkit().getImage(url);
            //ImageIcon icon = new ImageIcon(image);
            String[] iconName;
            iconName = path.getFileName().toString().replaceAll("_000000", "").replaceAll(".png","").split("_",2);
            String imageName = iconName[0];
            String imageSize = iconName[1];
            if(currentIcon==null||!currentIcon.getName().equals(imageName)){
                currentIcon = new HIcon(imageName);
                folder.addIcon(currentIcon);
                currentIcon.setIcon(icon, imageSize );
            }else{
                currentIcon.setIcon(icon, imageSize);
            }
            //System.out.println("Image: " + imageName+"-->"+imageSize);
        }
    }
    System.out.println("");
}
}

任何指针都会有所帮助。我查看了一些我认为指出同样内容的SO帖子。我正在使用带有SSD的MacBook Air,所以我认为这会很快。

我在下面添加了个人资料结果的屏幕截图:

Java profile in netbeans

setUseCache设置为false后,这是一个配置文件:

Java profile after setUseCache = false

3 个答案:

答案 0 :(得分:2)

您可以尝试使用<?php echo exec('whoami'); ?>来使用基于内存的缓存而不是基于磁盘的缓存(因为它是默认缓存)。

参考:https://docs.oracle.com/javase/7/docs/api/javax/imageio/ImageIO.html#setUseCache(boolean)

答案 1 :(得分:0)

我终于找到了一个解决方案。以下是最新资料:

The Java Language Specification (17.4 Memory Model)

这是一个非常有趣的解决方案。我决定使用执行程序,以便可以并行化读取操作。我已经创建了一个用于运行整个任务的线程,但是将其分解为更加精细的任务,这真的很快。

最后我能够从38.9秒下降到3.4秒。这是我书中的一大收获。这对我很重要,因为我希望我的服务器尽可能快地启动,我绝对不想失去39秒的启动时间。

我选择将其硬编码为5个线程,因为我在我的mac上进行开发,它只有4个核心。我尝试了15个及以上的核心+ 1它失去了效率。

以下是调整后的示例代码:

public class IconTest {

/**
 * @param args the command line arguments
 * @throws java.net.URISyntaxException
 * @throws java.io.IOException
 */
public static void main(String[] args) throws URISyntaxException, IOException {
    test4();
}

public static void test4() throws MalformedURLException, IOException, URISyntaxException{
    ExecutorService service = Executors.newFixedThreadPool(5);
    URI uri = IconTest.class.getResource("../resources/Icons").toURI();
    Path myPath;
    if (uri.getScheme().equals("jar")) {
        FileSystem fileSystem = FileSystems.newFileSystem(uri, Collections.<String, Object>emptyMap());
        myPath = fileSystem.getPath("Icons/");
    } else {
        myPath = Paths.get(uri);
    }
    IconFolder root = new IconFolder(myPath.toFile().getName());
    IconFolder parentFolder = root;
    HIcon currentIcon = null;
    IconFolder folder = null;
    HashMap<String,IconFolder> folders = new HashMap<>();
    folders.put(parentFolder.getName(), parentFolder);
    Stream<Path> walk = Files.walk(myPath, 10);
    Iterator<Path> it = walk.iterator();it.next();
    Info.Info("Starting loading icons....");
    ImageIO.setUseCache(false);
    while(it.hasNext()){
        Path path = it.next();
        if(path.toFile().isDirectory()){
            folder = new IconFolder(path.toFile().getName());
            folders.put(folder.getName(), folder);
            String parentName = path.getParent().toFile().getName();
            parentFolder = folders.get(parentName);
            parentFolder.addSubFolder(folder);
            currentIcon =null;
            Info.Info("Directory: " + path);
        }else{
            ImageLoadingTask task;
            URL url = path.toUri().toURL();
            String[] iconName;
            iconName = path.getFileName().toString().replaceAll("_000000", "").replaceAll(".png","").split("_",2);
            String imageName = iconName[0];
            String imageSize = iconName[1];
            if(currentIcon==null||!currentIcon.getName().equals(imageName)){
                currentIcon = new HIcon(imageName);
                folder.addIcon(currentIcon);
                task = new ImageLoadingTask(url,currentIcon,imageSize);
                service.submit(task);
            }else{
                task = new ImageLoadingTask(url,currentIcon,imageSize);
                service.submit(task);
            }
            //Info.Info("Image: " + imageName+"-->"+imageSize);
        }
    }
    service.shutdown();
    Info.Info("Finished loading icons....");
}

static public class ImageLoadingTask implements Callable<ImageIcon> {

    private final URL url;
    private final HIcon hIcon;
    private final String size;

    public ImageLoadingTask(URL url, HIcon hIcon, String size ) {
        this.url = url;
        this.hIcon=hIcon;
        this.size=size;
    }

    @Override
    public ImageIcon call() throws Exception {
        ImageIcon icon = new ImageIcon(ImageIO.read(url));
        hIcon.setIcon(icon, size);
        return icon;
    }
}
}

答案 2 :(得分:0)

ImageIO#read是一种实用方法,可以完成一堆内容。

例如,如果您知道所有图片的格式相同,则可以使用ImageIO#getImageReadersByFormatName一次获取读者(显然这是启动时间的3/4),让读者了解想要使用(通常只是第一个),然后使用该阅读器获取所有图像,而无需再次搜索。