Почему я получаю исключение пространства кучи Java, когда загружаю около 11 тыс. изображений размером около 40 МБ?

У меня есть ситуация в моей программе, когда мне нужно получить доступ к определенному количеству изображений, сохраненных на моем жестком диске. Я мог либо загружать их только тогда, когда они мне действительно нужны, либо загружать их все уже при запуске.

Из любопытства я попытался прочитать все (около 11 тысяч) изображений из нескольких папок и сохранить их в нескольких списках. Мне было интересно, занимает ли это слишком много времени, но я получил OutOfMemoryError после чтения около ~ 9 тыс. изображений.

Моя JRE имеет размер кучи 1g (-Xmx1g).

Почему это происходит для моего кода? Я создаю утечку памяти? Есть ли у вас какие-либо предложения, что изменить, чтобы больше не испытывать этого, или решение просто читать только файлы, как только они мне понадобятся? Общий размер всех файлов составляет всего около 40 МБ, поэтому я подумал, что будет нормально хранить их в памяти (40 МБ, похоже, не слишком много для моего размера кучи в 1 г). Или java выполняет какие-то сумасшедшие волшебные вещи, умножая размер файла на большие числа после загрузки?

Я провел некоторое исследование stackoverflow, но не смог преобразовать данные ответы (например, из здесь или здесь) для моего случая. Так что, если у кого-то из вас есть идеи, я был бы очень рад :)

Мой код здесь, извините, если он немного некрасивый - я просто бездельничал:

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

public class FatfontsManager {

    private List<BufferedImage> until9;
    private List<BufferedImage> until99;
    private List<BufferedImage> until999;
    private List<BufferedImage> until9999;

    // loads and keeps all images 

    public FatfontsManager() {

        until9 = new ArrayList<>();
        until99 = new ArrayList<>();
        until999 = new ArrayList<>();
        until9999 = new ArrayList<>();

        String absolutPathToThisProject = new java.io.File("")
                .getAbsolutePath();

        // load Images
        for (int i = 1; i < 10; i++) {
            until9.add(loadImage(absolutPathToThisProject
                    + "\\latexDir\\0..9\\" + i + ".png"));
        }
        System.out.println("1 - 9 loaded");
        for (int i = 1; i < 100; i++) {
            until99.add(loadImage(absolutPathToThisProject
                    + "\\latexDir\\0..99\\" + i + ".png"));
        }
        System.out.println("1 - 99 loaded");
        for (int i = 1; i < 1000; i++) {
            until999.add(loadImage(absolutPathToThisProject
                    + "\\latexDir\\0..999\\" + i + ".png"));
        }
        System.out.println("1 - 999 loaded");
        for (int i = 1; i < 10000; i++) {
            until9999.add(loadImage(absolutPathToThisProject
                    + "\\latexDir\\0..9999\\" + i + ".png"));
        }
        System.out.println("1 - 9999 loaded");
    }

    public static void main(String[] args) {
        new FatfontsManager();
    }

    public static BufferedImage loadImage(String ref) {
        BufferedImage bimg = null;
        try {

            bimg = ImageIO.read(new File(ref));
        } catch (Exception e) {
            System.err.println("Error loading image file " + ref);
            e.printStackTrace();
        }
        return bimg;
    }

}

Большое спасибо за каждый ответ.


person Waylander    schedule 14.07.2013    source источник


Ответы (1)


Общий размер всех файлов составляет всего около 40 МБ ...

Но когда вы загружаете файлы изображений в память, изображения распаковываются и превращаются в массивы пикселей. В зависимости от исходного формата файла изображения (и параметров сжатия) для этого может потребоваться порядок величины или больше места в куче.

Не совсем понятно, чего вы пытаетесь достичь, но, возможно, вам следует подумать о кэшировании этих изображений в памяти только тогда, когда они действительно будут использоваться.

person Stephen C    schedule 14.07.2013
comment
Да, я думаю, что буду кэшировать только те разы, которые мне действительно нужны. Меня просто смутило, что так много памяти теряется. Изображения в формате .png, только черно-белые и не имеют большой высоты/ширины. Но если я правильно вас понял, говоря, что они распаковываются - не имеет значения, содержат ли они прозрачные области или только черные и белые пиксели. Большое спасибо за ответ! - person Waylander; 14.07.2013