[英]Memory leak when the same BufferedImage object is loading multiple images
[英]Possible memory leak when caching 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;
}
}
用法:
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[]
而不是對象本身。 內存使用情況仍然正常。 但是我發現BufferedImage
的Serialization
和Deserialization
Serialization
Deserialization
將花費太多時間。
所以我想知道你們有沒有圖像緩存的經驗?
更新:
既然有這么多人說沒有內存泄漏,但是我的cacher使用了太多的內存,我不確定,但我試圖直接緩存byte[]
而不是BufferedImage
,內存使用看起來不錯。 而我無法想象322圖像會占用1.5G +內存,事件如@BrettOkken所說,總大小應為(256 * 256 * 4byte) * 322 / 1024 / 1024 = 80M
322/1024/1024 (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);
和內存使用情況:
相同的代碼,相同的操作。
從兩個VisualVM屏幕截圖中注意到,4,313個int []實例消耗了97.5%的內存(我假設是通過高速緩存的緩存圖像),非高速緩存版本不會消耗這些內存。
雖然您有一個小於1K的PNG圖像(根據PNG格式壓縮),但這個單個圖像是由多個緩沖圖像實例(未壓縮)生成的。 因此,您無法直接將瀏覽器的圖像大小與服務器上占用的內存聯系起來。 所以這里的問題不是內存泄漏,而是緩存這些未壓縮的緩沖圖像層所需的內存量。
解決此問題的策略是調整您的緩存機制:
不確定您使用的是哪種緩存API,或者您的請求中的實際值是什么。 但是基於visualvm,我認為String對象正在泄漏。 正如您所提到的,如果您關閉緩存,問題就解決了。
考慮下面代碼片段的摘錄。
String lkey = layer + "-" + coordinate;
BufferedImage imageData = cacher.get(lkey);
現在,您可以在此處為此代碼考慮一些事項。
VisualVM是一個開始,但它沒有提供完整的圖片。
您需要在應用程序使用大量內存時觸發堆轉儲。 您可以從VisualVM觸發堆轉儲。 如果將此vmarg添加到java進程,它也可以在OOME上自動完成:
-XX:+HeapDumpOnOutOfMemoryError
使用Memory Analyzer Tool打開並檢查堆轉儲。
該工具非常強大,可以幫助您遍歷對象引用以發現:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.