我必须做一个函数来接收 List<PDXObjectImage> list
并为每个元素创建一个小图标,并将它们存储在jtable中。
现在我找到了一种从 PDXObjectImage
不加载整个图像,这样我的程序就不会抛出 OutOfMemoryError: Java heap space
:
for(int k=0;k<list.size();k++)
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
list.get(k).write2OutputStream(output);
ByteArrayInputStream bais = new ByteArrayInputStream(output.toByteArray());
ImageInputStream iis = ImageIO.createImageInputStream(bais);
Iterator iter = ImageIO.getImageReaders(iis);
if (iter.hasNext()) {
ImageReader reader = (ImageReader) iter.next();
reader.setInput(iis, true, true);
ImageReadParam params = reader.getDefaultReadParam();
params.setSourceSubsampling(2.0, 2.0, 0, 0);
BufferedImage img = reader.read(0, params);
ImageIcon imageIcon = new ImageIcon(img);
model.addRow(new Object[]{imageIcon});
}
}
我设法避开了 OutOfMemoryError: Java heap space
对于列表中的少量图片,使用读卡器而不是将图像作为 BufferedImage
每次。不幸的是,当列表中存储了超过84个元素时,我仍然会遇到这个错误。
我使用jvisualvm查看哪些对象占用了所有堆空间,我发现它是 byte[]
对象(约85%)。
问题很明显地存在于我创建所有流以获取 iconImage
. 问题是我不知道如何得到一个 ImageInputStream
而不必每次都创建新的流。
我试图通过在单个函数中生成所有流来避免问题:
private ImageInputStream fct(PDXObjectImage img) throws IOException{
ByteArrayOutputStream output = new ByteArrayOutputStream();
img.write2OutputStream(output);
ByteArrayInputStream bais = new ByteArrayInputStream(output.toByteArray());
return ImageIO.createImageInputStream(bais);
}
认为java到达作用域的末尾时会自动删除对象。
我尝试在每个循环的末尾以任何可能的顺序添加以下内容:
output.reset();
output.flush();
bais.reset();
bais.close();
iis.flush();
output=null;
bais=null;
iis=null;
System.gc();
我还尝试示例化函数作用域之外的流,但是没有办法设置 ByteArrayInputStream
从 byte[]
不使用 new
关键字,从而创建一个新对象。
我还是会犯同样的错误,什么都不管用。
我在网上看了一些帖子 Statements
以及 ResultSets
但我觉得它们不相关(也许我错了)
如果有人知道我怎样才能避免这个错误,我会非常感激的。
谢谢您
编辑:
我修改了我的代码,以便得到以下信息:
for(int k=0;k<list.size();k++)
{
list.get(k).write2OutputStream(cbb.getOutputStream());
ImageInputStream iis = ImageIO.createImageInputStream(cbb.getInputStream());
Iterator iter = ImageIO.getImageReaders(iis);
if (iter.hasNext()) {
ImageReader reader = (ImageReader) iter.next();
reader.setInput(iis, true, true);
BufferedImage img = reader.read(0, null);
ImageIcon imageIcon = new ImageIcon(img);
model.addRow(new Object[]{imageIcon});
}
}
我还为reader添加了一个监听器,以便它打印出完成阅读的百分比。它总是上升到84.2%,然后停止。
有人知道这怎么可能吗?
3条答案
按热度按时间bsxbgnwa1#
使用
PipedInputStream/PipedOutputStream
或者CircularByteBuffer
将直接写入的字节传送到输入流。这样您就不必创建中间流和浪费内存。看看这个帖子:
http://ostermiller.org/convert_java_outputstream_inputstream.html
重写
fct
方法使用CircularByteBuffer
:您还可以使用多线程方法,即在一个线程中写入字节,在另一个线程上读取字节。因此,写/读可以同时发生,从而优化cpu使用和内存使用。
注:
com.Ostermiller.util.CircularByteBuffer
不是标准的java api。但是来源是免费的p8ekf7hl2#
1) 代码中没有任何东西表明加载的图像被压缩为“小图标”,除了在
Iterator iter = ImageIO.getImageReaders(iis);
线路。你能确认图像确实被压缩了吗?否则,可能是没有足够的ram分配给jvm堆的简单情况;2) 即使将映像压缩到更小的字节量,分配给jvm的ram可能仍然不足。或者您的压缩映像可能有对未压缩映像的内部引用(我不熟悉所使用的api,所以我不能确定),从而导致较大的映像保留在jvm堆中。使用MemoryProfiler,查看循环的每次迭代占用了多少内存,以及它是否随着时间的推移由于gc而减少。然后将总堆除以这个数字,您可能知道可以加载多少个图标
OutOfMemory
.3)
Statements
以及ResultSets
与java图像处理无关,这些都是关于使用关系数据库的。envsm3lx3#
您只需在两个维度中将图像大小减半(使用子采样参数)。创建图标应该缩放图像,以便更大尺寸的图像正好与图标的显示大小匹配。
首先需要确定图像的尺寸,然后计算出合适的目标尺寸,然后用子采样读取图像。
如果您的图标大小是(例如)100 x 100像素,那么图标在其较大的尺寸中应该正好是100像素。