BufferedImage
class Provider {
@Override
public IData render(String... layers,String coordinate) {
int rwidth = 256 , rheight = 256 ;
ArrayList<BufferedImage> result = new ArrayList<BufferedImage>();
for (String layer : layers) {
String lkey = layer + "-" + coordinate;
BufferedImage imageData = cacher.get(lkey);
if (imageData == null) {
try {
imageData = generateImage(layer, coordinate,rwidth, rheight, bbox);
cacher.put(lkey, imageData);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
if (imageData != null) {
result.add(imageData);
}
}
return new Data(rheight, rheight, width, result);
}
private BufferedImage generateImage(String layer, String coordinate,int rwidth, int rheight) throws IOException {
BufferedImage image = new BufferedImage(rwidth, rheight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.RED);
g.drawString(layer+"-"+coordinate, new Random().nextInt(rwidth), new Random().nextInt(rheight));
g.dispose();
return image;
}
}
class Data implements IData {
public Data(int imageWidth, int imageHeight, int originalWidth, ArrayList<BufferedImage> images) {
this.imageResult = new BufferedImage(this.imageWidth, this.imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = imageResult.createGraphics();
for (BufferedImage imgData : images) {
g.drawImage(imgData, 0, 0, null);
imgData = null;
}
imageResult.flush();
g.dispose();
images.clear();
}
@Override
public void save(OutputStream out, String format) throws IOException {
ImageIO.write(this.imageResult, format, out);
out.flush();
this.imageResult = null;
}
}
用途:
class ImageServlet extends HttpServlet {
void doGet(req,res){
IData data= provider.render(req.getParameter("layers").split(","));
OutputStream out=res.getOutputStream();
data.save(out,"png")
out.flush();
}
}
注意:
provider
字段是单个实例。但是,似乎存在内存泄漏的可能性,因为当应用程序持续运行大约2分钟时,我将得到
Out Of Memory
异常。然后我使用
visualvm
检查内存使用情况:即使手动操作,内存也无法释放。
尽管只有300+
Perform GC
缓存,并且使用了BufferedImage
内存,但20M+
内存仍然保留。事实上,通过“firebug”,我可以确保生成的图像小于1.3G+
。所以我认为内存使用是不健康的。一旦我不使用缓存(请注释以下行):
//cacher.put(lkey, imageData);
内存使用情况看起来不错:
所以缓存的
1Kb
似乎导致了内存泄漏。然后我尝试将
BufferedImage
转换为BufferedImage
并缓存byte[]
而不是对象本身。内存使用仍然正常。但是我发现byte[]
和Serialization
的Deserialization
将花费太多时间。所以我想知道你们有没有图像缓存的经验?
更新:
因为有很多人说没有内存泄漏,但是我的缓存使用了太多的内存,我不确定,但是我试着缓存
BufferedImage
而不是直接缓存byte[]
,内存使用看起来不错。而且我无法想象322个镜像会占用1.5g+内存,事件正如@brettokken所说,总大小应该BufferedImage
,远远小于1gb。刚才,我更改为缓存
(256 * 256 * 4byte) * 322 / 1024 / 1024 = 80M
并再次监视内存,代码更改如下:BufferedImage ig = generateImage(layer,coordinate rwidth, rheight);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(ig, "png", bos);
imageData = bos.toByteArray();
tileCacher.put(lkey, imageData);
以及内存使用情况:
同样的代码,同样的操作。
最佳答案:
从两个visualvm截图中注意到,在非缓存版本中,4313个int[]实例(我假设是缓存的缓冲图像)消耗的97.5%内存没有消耗。
尽管您的png图像小于1k(按png格式压缩),但此单个图像是由多个缓冲图像实例(未压缩)生成的。因此,您不能直接将浏览器中的图像大小与服务器上占用的内存关联起来。所以这里的问题不是内存泄漏,而是缓存这些未压缩的缓冲图像层所需的内存量。
解决此问题的策略是调整缓存机制:
如果可能,使用缓存层的压缩版本而不是原始版本
图像
通过限制缓存大小,确保永远不会耗尽内存
按实例或使用的内存量。使用LRU或LIR
缓存收回策略
使用自定义键对象,将坐标和图层作为两个独立的对象
使用equals/hashcode重写变量以用作键。
观察该行为,如果缓存未命中太多,则
需要更好的缓存策略,否则可能不需要缓存
开销。
我相信您正在缓存层,正如您所期望的那样
和坐标,因此不能缓存最终图像,但取决于
如果可能,您可能希望考虑该选项的请求模式