hgu*_*ser 9 java caching memory-leaks bufferedimage
我们有一个提供图像的应用程序,为了加快响应时间,我们将其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;
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
注意:该provider字段是单个实例.
但是,似乎存在可能的内存泄漏,因为Out Of Memory当应用程序继续运行大约2分钟时,我将获得异常.
然后我visualvm用来检查内存使用情况:

即使我Perform GC手动,内存也无法释放.
虽然只有300多个BufferedImage缓存,并且20M+使用了1.3G+内存,但内存仍然保留.事实上,通过"firebug",我可以确保生成的图像小于1Kb.所以我认为内存使用不健康.
一旦我不使用缓存(注释以下行):
//cacher.put(lkey, imageData);
Run Code Online (Sandbox Code Playgroud)
内存使用率看起来很好:

所以看起来缓存BufferedImage会导致内存泄漏.
然后我尝试转换BufferedImage为byte[]和缓存byte[]而不是对象本身.内存使用情况仍然正常.但是我发现Serialization,并Deserialization为BufferedImage将花费太多时间.
所以我想知道你们有没有图像缓存的经验?
更新:
既然有这么多人说没有内存泄漏但是我的cacher使用了太多的内存,我不确定,但我试图缓存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);
Run Code Online (Sandbox Code Playgroud)
和内存使用情况:

相同的代码,相同的操作.
不确定您正在使用什么缓存 API 或您的请求中的实际值是什么。然而,根据 VisualVM,我认为 String 对象正在泄漏。另外,正如您提到的,如果关闭缓存,问题就可以解决。
考虑以下代码片段的摘录。
String lkey = layer + "-" + coordinate;
BufferedImage imageData = cacher.get(lkey);
Run Code Online (Sandbox Code Playgroud)
现在,您需要考虑此代码的一些事项。
| 归档时间: |
|
| 查看次数: |
2755 次 |
| 最近记录: |