【问题标题】:Big buffer causes Android to get out of memory大缓冲区导致 Android 内存不足
【发布时间】:2012-06-16 04:14:34
【问题描述】:

我在这个问题上被困了 3 天。我得到了一个 Dicom 文件,我需要使用缓冲阅读器对其进行解析,该阅读器从文档的标题和图像的原始数据返回一些信息。之后,我在 raw 上应用 LUT 将字节转换为灰度,然后将其放入 Bitmap.create 中。它非常适合小图像,但现在,我必须加载 13Mo 图像,不仅需要很长时间才能打开它(大约 20 秒),而且在应用 LUT int 位图方法时,Android 会抛出错误 About Bitmap @ 987654322@java.lang.OutOfMemoryError: bitmap size exceeds VM budget。我知道有很多关于这个错误的线索,但就我而言,它有点原始,因为我只想打开一个图像(所以它不是关于堆叠很多位图)。我可以给你看一些代码:

刷新Bmp:

private void refreshBmp(int windowWidth, int windowCentre) {
    int[] colorArray = process.transformBuffer(myDicomObject.getRawData(),
            myDicomObject.isInverted(), windowWidth, windowCentre,
            myDicomObject.getnBits());

    Bitmap bmp = Bitmap.createBitmap(colorArray,
            myDicomObject.getColumns(), myDicomObject.getRows(),
            Bitmap.Config.ARGB_8888);
    dicomImageView.setImageBitmap(bmp);
}

这叫我的LUT:

public int[] transformBuffer(int[] rawData, boolean inverted,
        int windowWidth, int windowCenter, int nBits) {
    System.gc();

    int min = windowCenter - (windowWidth/2);       
    int max = windowCenter + (windowWidth/2);       
    int intGrayscale = (int) Math.pow(2, nBits);    
    int intDivisionFactor = nBits-8;                

    double dmin = (double) min;                     
    double dmax = (double) max;                     
    double doubleGrayScale = (double) intGrayscale; 

    int rawDataLength = rawData.length;             

    int[] resultBuffer = new int[rawDataLength];    
    lutBuffer = new int[intGrayscale];              

    if(inverted){

        for(int i = 0 ; i < min ; i++){
            lutBuffer[i] = 255;
        }

        for(int i = min ; i < max ; i++){
            double value = doubleGrayScale * ((i - dmin + 1) / (dmax - dmin + 1));
            lutBuffer[i] = (int) (doubleGrayScale - value) >> intDivisionFactor;
        }

        for(int i = max ; i < intGrayscale ; i++){
            lutBuffer[i] = 0;
        }

    }else{
        for(int i = 0 ; i < min ; i++){
            lutBuffer[i] = 0;
        }

        for(int i = min ; i < max ; i++){
            double value = ((i - dmin + 1) / (dmax - dmin + 1));
            lutBuffer[i] = (int) (value) << intDivisionFactor;
        }

        for(int i = max ; i < intGrayscale ; i++){
            lutBuffer[i] = 255;
        }
    }

    for(int i = 0 ; i < rawDataLength ; i++){
        int colorValue = lutBuffer[rawData[i]];
        resultBuffer[i] = Color.argb(255, colorValue, colorValue, colorValue);
    }

    System.out.println(resultBuffer.length);
    return resultBuffer;
}

希望有人知道一种节省内存分配的方法,尤其是 LUT 方法。

【问题讨论】:

  • 崩溃时rawDataLengthintGrayScale的值是多少?
  • @David intGrayscale 是一个常数,它的值为 2^16 = 65536。rawDataLength = resultBuffer.length = 7263120
  • 好的。你知道分配一个 int[7263120] 需要 28MB 的内存吗?大多数 Android 手机的最大进程堆大小为 16MB、24MB 或 32MB。您可以使用ActivityManager.getMemoryClass() 检查设备的最大内存。无论如何,您不能在内存中执行此操作。这是不可能的。
  • 需要分配一个int数组吗?每个 int 需要 4 个字节。如果您输入的值只有 0-255 之间的值,那么您可以使用字节数组。这将使您的内存需求减少 75%
  • @david 感谢您的回答,我尝试制作一个 byte[] 但创建位图的方法需要一个 int 数组。

标签: java android optimization memory-leaks bitmap


【解决方案1】:

如果您不再需要它,您应该尝试直接修改rawData。像这样的:

public void transformBuffer(int[] rawData, boolean inverted,
        int windowWidth, int windowCenter, int nBits) {
/*
change rowData instead resultBuffer
*/
}

public void methodThatCallsTransformBuffer(...) {
    transformBuffer(data, inverted, ...);
    //now data is transformed
}

此外,您可以为每个像素计算 colorValue,而不是使用 lutBuffer。这会有点慢,但你会节省一些内存:

int colorValue = 0;
if (inverted) {
   if (rawData[i] < min) {
      colorValue = 255;
   } else {
      if (rawData[i] < max) {
         double value = doubleGrayScale * ((i - dmin + 1) / (dmax - dmin + 1));
         colorValue  = (int) (doubleGrayScale - value) >> intDivisionFactor;         
      }
   }
} else {
   if (rawData[i] >= min && rowData[i] < max) {
      double value = doubleGrayScale * ((i - dmin + 1) / (dmax - dmin + 1));
      colorValue  = (int) (doubleGrayScale - value) >> intDivisionFactor;      
   } else if (rowData[i] > max) {
      colorValue = 255;
   }
}

【讨论】:

  • 感谢您的回答!但我实际上稍后需要它,所以它不会在我的情况下工作。
【解决方案2】:

当我最终找到解决方案时,我决定回答自己。使用这些大的位图数据避免 OutOfMemory 错误的唯一方法是后分箱(我不确定它是否是实际名称)。它包括获取每个交替的像素。您可以通过读取一个像素、跳过一个像素、读取另一个像素、跳过一个像素并继续执行此操作直到到达行尾,然后跳过一行并继续此过程直到数据缓冲区的末尾。

我希望它可以帮助别人。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多