简体   繁体   English

在循环中实例化对象而不会出现 OutOfMemoryError:Java 堆空间

[英]Instantiate objects in loop without getting OutOfMemoryError: Java heap space

I have to make a function that receives as en entry a List<PDXObjectImage> list and creates a small icon for each of these elements and store them in a JTable.我必须创建一个函数,它接收一个List<PDXObjectImage> list作为条目,并为每个元素创建一个小图标并将它们存储在 JTable 中。

Now I found a way to create icons from a PDXObjectImage without loading the whole Image so that my program does not throw OutOfMemoryError: Java heap space :现在我找到了一种从PDXObjectImage创建图标而不加载整个 Image 的方法,这样我的程序就不会抛出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});
    }
}

I managed to avoid the OutOfMemoryError: Java heap space for a small amount of pictures in the list using readers instead of loading the images as BufferedImage each time.我设法避免了OutOfMemoryError: Java heap space使用读取器而不是每次都将图像加载为BufferedImage列表中少量图片的OutOfMemoryError: Java heap space Unfortunately, I still get this error when more than 84 elements are stored in the list.不幸的是,当列表中存储的元素超过 84 个时,我仍然收到此错误。

I used jvisualvm to see what objects took all the heap space and I found out it was byte[] objects(around 85%).我使用 jvisualvm 来查看哪些对象占用了所有堆空间,我发现它是byte[]对象(大约 85%)。

The problem is clearly located where I create all the streams to get the iconImage .问题显然位于我创建所有流以获取iconImage The thing is I don't know any ways of getting a ImageInputStream without having to create new streams each time.问题是我不知道无需每次都创建新流即可获得ImageInputStream任何方法。

I tried to avoid the problem by generating all the streams in a single function :我试图通过在单个函数中生成所有流来避免这个问题:

private ImageInputStream fct(PDXObjectImage img) throws IOException{
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    img.write2OutputStream(output);
    ByteArrayInputStream bais = new ByteArrayInputStream(output.toByteArray());
    return ImageIO.createImageInputStream(bais);
}

thinking that java would automatically delete objects when it reach the end of the scope.认为java会在到达范围末尾时自动删除对象。

I tried adding the following in any order possible at the end of each loop :我尝试在每个循环结束时以任何可能的顺序添加以下内容:

output.reset();
output.flush();
bais.reset();
bais.close();
iis.flush();
output=null;
bais=null;
iis=null;
System.gc();

I also tried to instantiate the streams outside the function's scope, but there is no way of setting a ByteArrayInputStream from a byte[] without using the new keyword, thus creating a new object.我还尝试在函数作用域之外实例化流,但无法在不使用new关键字的情况下从byte[]设置ByteArrayInputStream ,从而创建一个新对象。

I still get the same error, nothing works.我仍然遇到同样的错误,没有任何效果。

I read some posts on Statements and ResultSets but I not found them relevant.我阅读了一些关于StatementsResultSets帖子,但我发现它们不相关。 (Maybe I am wrong) (也许我错了)

If someone has any idea of how I could avoid this error, I would be very gratefull.如果有人对我如何避免此错误有任何想法,我将不胜感激。

Thank you谢谢

EDIT:编辑:

I have modified my code so that I get the following :我修改了我的代码,以便得到以下内容:

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});
    }
}

I have also added a listener to the reader so that it prints out the percentage of the reading done.我还为阅读器添加了一个监听器,以便它打印出阅读完成的百分比。 It always goes up to 84.2% and stops.它总是上升到 84.2% 并停止。

Does anyone knows how can this be possible?有谁知道这怎么可能?

Use PipedInputStream/PipedOutputStream or CircularByteBuffer to channel the bytes written directly to the input stream.使用PipedInputStream/PipedOutputStreamCircularByteBuffer将字节直接写入输入流。 This way you will not have to create intermediate streams and waste memory.这样您就不必创建中间流和浪费内存。

Take a look at this post :看看这个帖子:

http://ostermiller.org/convert_java_outputstream_inputstream.html http://ostermiller.org/convert_java_outputstream_inputstream.html

Rewriting fct method using CircularByteBuffer :使用CircularByteBuffer重写fct方法:

private ImageInputStream fct(PDXObjectImage img) throws IOException{
CircularByteBuffer cbb = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE);
img.write2OutputStream(cbb.getOutputStream());
return ImageIO.createImageInputStream(cbb.getInputStream());
}

You can also use a multi-threaded approach where you are writing bytes in one thread and reading on another.您还可以使用多线程方法,在一个线程中写入字节并在另一个线程中读取。 Thus, writing/reading can occur concurrently optimizing the CPU usage and memory utilization.因此,写入/读取可以同时发生以优化 CPU 使用率和内存使用率。

Note: com.Ostermiller.util.CircularByteBuffer is not a standard Java API.注意: com.Ostermiller.util.CircularByteBuffer不是标准的 Java API。 But the source is freely available但源是 免费提供的

1) There's nothing in your code that suggests that loaded image is compressed anyhow to "small icon", except in Iterator iter = ImageIO.getImageReaders(iis); 1)您的代码中没有任何内容表明加载的图像无论如何都被压缩为“小图标”,除了Iterator iter = ImageIO.getImageReaders(iis); line.线。 Can you confirm image is compressed indeed?你能确认图像确实被压缩了吗? Otherwise, it may be the simple case of not enough RAM allocated to JVM heap;否则,可能是没有足够的 RAM 分配给 JVM 堆的简单情况;

2) Even if image is compressed to smaller byte volume, you may still have insufficient RAM allocated to JVM. 2) 即使图像被压缩到较小的字节量,您可能仍然没有足够的 RAM 分配给 JVM。 Or your compressed image may have internal reference to uncompressed one (I'm not familiar with API used, so I can't tell for sure), resulting in larger image being retained in JVM heap.或者您的压缩图像可能具有对未压缩图像的内部引用(我不熟悉所使用的 API,所以我不能确定),导致较大的图像保留在 JVM 堆中。 Using memory profiler, take a look how much memory is occupied on each iteration of loop and if it decreseas over the time due to GC.使用内存分析器,查看每次循环迭代占用了多少内存,以及它是否因 GC 而随着时间的推移而减少。 Then divide total heap by this figure, and you may have an idea how many icons you can load w/o getting OutOfMemory .然后将总堆除以这个数字,您可能知道可以加载多少个图标而OutOfMemory获取OutOfMemory

3) Statements and ResultSets have nothing to do with Java image processing, these are about working with relational databases. 3) StatementsResultSets与 Java 图像处理无关,这些与关系数据库有关。

You are only halving the image size in both dimensions (with the subsampling parameters).您只是将两个维度的图像大小减半(使用二次采样参数)。 Creating an icon should scale the image so that the images larger dimension just matches the display size of the icon.创建图标应该缩放图像,以便图像较大的尺寸与图标的显示尺寸相匹配。

You need first to determine the image dimensions, then calculate the proper target size, then read the image with subsampling.您需要首先确定图像尺寸,然后计算合适的目标尺寸,然后通过二次采样读取图像。

If your icon size is (for example) 100 by 100 pixels, the icon should be exactly 100 pixels in its larger dimension.如果您的图标大小(例如)是 100 x 100 像素,则图标的较大尺寸应该正好是 100 像素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM