我正在加载大量将在我的应用程序中使用的图标。我计划在服务器启动时从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,所以我认为这会很快。
我在下面添加了个人资料结果的屏幕截图:
将setUseCache
设置为false后,这是一个配置文件:
答案 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),让读者了解想要使用(通常只是第一个),然后使用该阅读器获取所有图像,而无需再次搜索。