我们有一个为图像服务的应用程序,为了加快响应时间,我们直接将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
检查内存使用情况:
即使我手动Perform GC
,内存也不能释放。
虽然只有300+ BufferedImage
缓存,并且使用了20M+
内存,但1.3G+
内存被保留。事实上,通过"firebug“,我可以确保生成的映像小于1Kb
。所以我认为记忆的使用是不健康的。
一旦我不使用缓存(注释下一行):
//cacher.put(lkey, imageData);
内存使用情况看起来很好:
因此,缓存的BufferedImage
似乎会导致内存泄漏。
然后,我尝试将BufferedImage
转换为byte[]
,并缓存byte[]
而不是对象本身。内存的使用仍然是正常的。然而,我发现Serialization
和Deserialization
为BufferedImage
需要花费太多的时间。
所以我想知道你们是否有过图像缓存的经验?
最新情况:
因为有那么多人说没有内存泄漏,但是我的传道者使用了太多的内存,我不确定,但是我尝试过直接缓存byte[]
而不是BufferedImage
,而且内存的使用看起来很好。我无法想象322张图片会占用1.5G+内存,事件就像@BrettOkken说的那样,总大小应该是(256 * 256 * 4byte) * 322 / 1024 / 1024 = 80M
,远远小于1Gb。
刚才,我更改为缓存byte
并再次监视内存,代码更改如下:
BufferedImage ig = generateImage(layer,coordinate rwidth, rheight);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(ig, "png", bos);
imageData = bos.toByteArray();
tileCacher.put(lkey, imageData);
以及内存的使用:
同样的代码,同样的操作。
发布于 2014-07-15 01:43:27
请注意,这两个VisualVM屏幕截图都表明,4313个int我认为它是通过缓存的缓冲图像实例消耗的97.5%的内存不是在非缓存版本中使用的。
尽管您的PNG图像小于1K (按PNG格式压缩),但这个图像是由多个缓冲图像实例(未压缩)生成的。因此,不能将图像大小从浏览器直接关联到服务器上占用的内存。因此,这里的问题不是内存泄漏,而是缓存缓冲图像的未压缩层所需的内存量。
解决此问题的策略是调整缓存机制:
发布于 2014-07-14 22:42:29
不确定您正在使用的缓存API或请求中的实际值是什么。然而,基于可视化,在我看来,字符串对象正在泄漏。此外,正如您提到的,如果您关闭缓存,问题就解决了。
考虑以下代码片段的摘录。
String lkey = layer + "-" + coordinate;
BufferedImage imageData = cacher.get(lkey);
下面是一些代码需要考虑的事项。
发布于 2014-07-18 22:06:43
VisualVM是一个开始,但它并没有给出完整的情况。
在应用程序使用大量内存时,需要触发堆转储。您可以从VisualVM触发堆转储。如果将此vmarg添加到java进程中,也可以在OOME上自动完成此操作:
-XX:+HeapDumpOnOutOfMemoryError
使用内存分析器工具打开和检查堆转储。
该工具非常有能力,可以帮助您遍历对象引用以发现:
https://stackoverflow.com/questions/24666779
复制相似问题