java - 在 byteArray 中存储图像时的内存问题

标签 java android performance

我有一个需要快速访问大量图像的应用程序,因此我需要以某种方式将这些图像加载到内存中。这样做是因为位图使用了超过 100MB 的 RAM,这是完全不可能的,所以我选择将 jpg 文件读入内存,并将它们存储在 byteArray 中。然后我解码它们并根据需要将它们写入 Canvas 。这非常有效,消除了缓慢的磁盘访问,同时还遵守内存限制。

但是,内存使用对我来说似乎是“关闭”的。我正在存储 450 个 jpg,每个文件大小约为 33kb。这总共有大约 15MB 的数据。但是,根据 Eclipse DDMS 和 Android(在物理设备上)的报告,该应用程序持续运行在 35MB 到 40MB 的 RAM 之间。我已经尝试修改加载了多少 jpg,并且应用程序使用的 RAM 每 jpg 往往会减少大约 60-70kb,这表明每个图像在 RAM 中存储了两次。内存使用量不会波动,这意味着不存在实际的“泄漏”。

相关加载代码如下:

private byte[][] bitmapArray = new byte[totalFrames][];
for (int x=0; x<totalFrames; x++) {
    File file = null;
    if (cWidth <= cHeight){
            file = new File(directory + "/f"+x+".jpg");
    } else {
            file = new File(directory + "/f"+x+"-land.jpg");
    }
    bitmapArray[x] = getBytesFromFile(file);
    imagesLoaded = x + 1;
}


public byte[] getBytesFromFile(File file) {
    byte[] bytes = null;
    try {

        InputStream is = new FileInputStream(file);
        long length = file.length();

        bytes = new byte[(int) length];

        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
            offset += numRead;
        }

        if (offset < bytes.length) {
            throw new IOException("Could not completely read file " + file.getName());
        }

        is.close();
    } catch (IOException e) {
                  //TODO Write your catch method here
    }
    return bytes;
}

最终,它们会像这样写入屏幕:

SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
    c = holder.lockCanvas();
    if (c != null) {            
        int canvasWidth = c.getWidth();
        int canvasHeight = c.getHeight();
        Rect destinationRect = new Rect();
        destinationRect.set(0, 0, canvasWidth, canvasHeight);
        c.drawBitmap(BitmapFactory.decodeByteArray(bitmapArray[bgcycle], 0, bitmapArray[bgcycle].length), null, destinationRect, null);
    }
} finally {
    if (c != null)
    holder.unlockCanvasAndPost(c);
}

我是否正确认为这里发生了某种重复?还是像这样将 jpg 存储在 byteArray 中会涉及很多开销?

最佳答案

在 RAM 中存储字节与在硬盘驱动器上存储数据是非常不同的……它有更多的开销。对对象的引用以及字节数组结构都占用额外的内存。所有额外的内存并没有真正的单一来源,但请记住,将文件加载到 RAM 中通常会占用 2 ~ 3 倍的空间(根据经验,恐怕我不能在这里引用任何文档)。

考虑一下:

File F = //Some file here (Less than 2 GB please)
FileInputStream fIn = new FileInputStream(F);
ByteArrayOutputStream bOut = new ByteArrayOutputStream(((int)F.length()) + 1);

int r;
byte[] buf = new byte[32 * 1000];

while((r = fIn.read(buf) != -1){
    bOut.write(buf, 0, r);
}

//Do a memory measurement at this point. You'll see your using nearly 3x the memory in RAM compared to the file.
//If your actually gonna try this, remember to surround with try-catch and close the streams as appropriate.

另请记住,未使用的内存不会立即清除。方法 getBytesFromFile() 可能会返回一个字节数组的副本,这会导致可能不会立即被垃圾回收的内存重复。如果您想安全起见,请检查方法 getBytesFromFile(file) 是否泄漏了任何应该清理的引用。它不会显示为内存泄漏,因为您只调用它有限次数。

关于java - 在 byteArray 中存储图像时的内存问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18152100/

相关文章:

java - Grpc-java中有没有自动版本的流量控制?

android - Kotlin正则表达式不适用于我在运行时得到的波兰char (“ł”)

python - 优化 CPU 上的 Tensorflow 图像识别

Java super 调整,几个问题

java - 如何使用 Jsoup 抓取 Youtube 视频的观看次数?

java - OBJ 加载 - 奇怪的法线/uv

java - MySQL RDS 和 JDBC SSL 连接给出错误 : unable to find valid certification path to requested target

android - ScrollView 截掉部分布局

android - 无法使用 parse.com 从用户向用户发送推送

performance - 为什么 Scala 在计算机语言基准测试中比竞争对手多消耗 2-3 倍的 RAM?