登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> CLQ工作室开源代码 >> 主题: [android]android largeHeap 解决大图片加载 Throwing OutOfMemoryError 错误     [回主站]     [分站链接]
标题
[android]android largeHeap 解决大图片加载 Throwing OutOfMemoryError 错误
clq
浏览(473) + 2022-01-19 14:44:29 发表 编辑

关键字:

[2022-01-20 09:01:11 最后更新]
[android]android largeHeap 解决大图片加载 Throwing OutOfMemoryError 错误

android:largeHeap="true" 解决大图处加载 “Throwing OutOfMemoryError "Failed to allocate a 8388464 byte allocation with 3689696 free bytes and 3MB until OOM" 错误。

参考
https://blog.csdn.net/mp624183768/article/details/79209751

https://zhuanlan.zhihu.com/p/25186586


clq
2022-01-19 14:45:00 发表 编辑

android:largeHeap="true"的作用
安果移不动 于 2018-01-30 19:26:58 发布 1071 收藏
分类专栏: # 进阶的api
版权
进阶的api
专栏收录该内容
15 篇文章 0 订阅
订阅专栏
AndroidManifest.xml文件中可以设置 android:largeHeap="true"

我使用的测试设备为Nexus5 系统为5.0

安卓设备对应用内存的限制,一般在/system/build.prop文件中可以查看到

dalvik.vm.heapsize=512m(最大内存限制)

dalvik.vm.heapgrowthlimit=192m(普通内存限制)

当设置为android:largeHeap="true" 时

内存溢出

03-03 15:21:51.480: I/art(11679): Clamp target GC heap from 513MB to 512MB

当设置为android:largeHeap="false" 时

内存溢出

03-03 15:29:00.711: I/art(14283): Clamp target GC heap from 205MB to 192MB

测试方法为不断的加载图片到内存,比如

Bitmap bitmap[] = new Bitmap[300];
for (int i=0; i bitmap[i] = BitmapFactory.decodeResource(getResources(), R.drawable.eee);
}

获得最大内存限制:

android3.0及以上可调用此方法

((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE)).getLargeMemoryClass();
————————————————
版权声明:本文为CSDN博主「安果移不动」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/mp624183768/article/details/79209751

clq
2022-01-19 14:45:48 发表 编辑

探究android:largeHeap
技术小黑屋
技术小黑屋
http://droidyue.com/

在日常的Android开发中,我们必然遇到过OutOfMemoryError这样的崩溃,产生的原因无外乎两点,一是内存过小不够用,二是程序设计有误,导致不能释放内存,其中后者情况较多。在解决这个问题时,我们亦或多或少听到android:largeHeap,然而这个概念又是什么呢,它该如何使用,存在哪些问题呢。本文讲比较全面介绍Android中的largeHeap帮助各位全面深入了解这个概念。
磨刀不误砍柴工

为了便于理解,先简单介绍一些和文章相关的基础概念。

通常,一个Android程序在运行时会启动一个Dalvik虚拟机(暂不讨论ART模式)虚拟机的运行时内存一般由堆和栈两大部分构成。栈是存储方法调用的一片内存数据区。堆内存占据了虚拟机的大部分内存空间,程序执行时产生的对象就分配在堆内存上。如果是堆内存没有可用的空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError。

如若具体了解堆和栈,请参考文章Java中的堆和栈的区别和JVM运行时的数据区
largeHeap介绍

一个应用如果使用了largeHeap,会请求系统为Dalvik虚拟机分配更大的内存空间。使用起来也很方便,只需在manifest文件application节点加入android:largeHeap=“true”即可。

android:allowBackup="false"
android:label="@string/app_name"
android:debuggable="true"
android:theme="@android:style/Theme.Black"
android:largeHeap="true"
>

largeHeap有多大

在Android中,有如下两个方法可以帮助我们查看当前内存大小

ActivityManager.getMemoryClass()获得内用正常情况下内存的大小ActivityManager.getLargeMemoryClass()可以获得开启largeHeap最大的内存大小

然而largeHeap这个最大值是如何决定的呢?想要了解这个问题,我们就需要看一下Android系统中的一个文件。

这个文件路径是/system/build.prop,由于文件比较大,这里我们只截取关于dalvik内存的配置信息,如下。

dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=192m
dalvik.vm.heapsize=512m
dalvik.vm.heaptargetutilization=0.75
dalvik.vm.heapminfree=2m
dalvik.vm.heapmaxfree=8m

上面有诸多配置,但从字面意思也不难理解,为了正确理解,有必要逐一解释一下。

dalvik.vm.heapstartsize=8m

相当于虚拟机的 -Xms配置,该项用来设置堆内存的初始大小。

dalvik.vm.heapgrowthlimit=192m

相当于虚拟机的 -XX:HeapGrowthLimit配置,该项用来设置一个标准的应用的最大堆内存大小。一个标准的应用就是没有使用android:largeHeap的应用。

dalvik.vm.heapsize=512m

相当于虚拟机的 -Xmx配置,该项设置了使用android:largeHeap的应用的最大堆内存大小。

dalvik.vm.heaptargetutilization=0.75

相当于虚拟机的 -XX:HeapTargetUtilization,该项用来设置当前理想的堆内存利用率。其取值位于0与1之间。当GC进行完垃圾回收之后,Dalvik的堆内存会进行相应的调整,通常结果是当前存活的对象的大小与堆内存大小做除法,得到的值为这个选项的设置,即这里的0.75。注意,这只是一个参考值,Dalvik虚拟机也可以忽略此设置。

dalvik.vm.heapminfree=2m与dalvik.vm.heapmaxfree=8m

dalvik.vm.heapminfree对应的是-XX:HeapMinFree配置,用来设置单次堆内存调整的最小值。dalvik.vm.heapmaxfree对应的是-XX:HeapMaxFree配置,用来设置单次堆内存调整的最大值。通常情况下,还需要结合上面的 -XX:HeapTargetUtilization的值,才能确定内存调整时,需要调整的大小。
largeHeap需要权限么

为何有此疑问呢? 原因是这样的。 首先一个设备的内存是固定的,当我们使用了largeHeap之后就可以使我们的程序内存增加,但这部分增加的内存有可能是源自被系统杀掉的后台程序。所以,使用largeHeap理论上是有可能杀掉其他的程序的。

然而,结果就是不需要权限,Google在一开始就是这样,只需要简单在Application元素上加入android:largeHeap=“true”就能正常使用。
largeHeap对GC的影响

拥有了更多的内存,是不是就意味着要花更多的时间遍历对象垃圾回收呢?其实不然。

首先largeHeap自Android 4.0开始支持,而并发的垃圾回收方式从Android 2.3开始引入。

在引入并发垃圾回收之前,系统采用了Stop-the-World回收方式,进行一次垃圾回收通常消耗几百毫秒,这是很影响交互和响应的。

引入并发垃圾回收之后,在GC开始和结束的阶段会有短暂的暂停时间,通常在10毫秒以内。

因此在支持largeHeap的系统上都采用了并发垃圾回收,GC的Pause Time不会很长,对交互响应影响甚微。
慎用largeHeap

对于largeHeap的使用,我们该持有的谨慎的态度,largeHeap可以使用,但是要谨慎。

对于本身对内存要求过大的图片或者视频应用,我们可以使用largeHeap。

除上面的情况,如果仅仅是为了解决OutOfMemoryError这样的问题,而尝试使用largeHeap分配更大内存的这种指标不治本的方法不可取。对待这样的OOM问题,建议阅读以下几篇文章,了解Android中内存泄露和垃圾回收,从代码上去查找问题,从根本上解决问题。
补漏

无论是否开启largeHeap,ActivityManager.getLargeMemoryClass()都可以打印出largeHeap的大小。因为其本身只是读取了配置文件的值而已。即下面的代码无论largeHeap开启与否,打印出来的日志都相同

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);

int largeMemoryClass = activityManager.getLargeMemoryClass();
int memoryClass = activityManager.getMemoryClass();

ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(info);

Log.d(LOGTAG, "largeMemoryClass = " + largeMemoryClass);
Log.d(LOGTAG, "memoryClass = " + memoryClass);

如何验证

关于如何验证,这里设置一个按钮,每次创建100M的内存对象,观察开启largeHeap前后的反应

private ArrayList mLeakyContainer = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.testBtn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
byte[] b = new byte[100 * 1000 * 1000];
mLeakyContainer.add(b);
}
});
testMemoryInfo();
}

以正常情况下可用192M内存为例,点击两次按钮,应用崩溃。然后在manifest开启largeHeap,以最大512M内存可用为例,点击6次应用崩溃

验证源码可以访问github查看largeHeapDemo
推荐扩展文章

Android中Handler引起的内存泄露避免Android中Context引起的内存泄露Google IO:Android内存管理主题演讲记录

clq
2022-01-20 09:01:11 发表 编辑


https://blog.csdn.net/Caster_Saber/article/details/52494984
https://blog.csdn.net/u012301841/article/details/50493214

Android 图片处理以及recycle机制


我们经常会涉及到对相机拍照,然后处理拍照后的图片,最后在显示到UI上。如果处理的不好,就会导致系统卡顿,甚至会出现OOM,程序崩溃。
图片的处理

public static BitmapDrawable getScaledDrawable(Activity a, String path) {
if (a == null) return null;

Display display = a.getWindowManager().getDefaultDisplay();
int destWidth = display.getWidth();
int destHeight = display.getHeight();

// Read in the dimensions of the image on disk
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);

int srcHeight = options.outHeight;
int srcWidth = options.outWidth;

int inSampleSize = 1;
if (srcHeight > destHeight || srcWidth > destWidth) {
if (srcWidth > srcHeight) {
inSampleSize = Math.round(srcHeight / destHeight);
} else {
inSampleSize = Math.round(srcWidth / destWidth);
}
}

options = new BitmapFactory.Options();
options.inSampleSize = inSampleSize;

Bitmap bitmap = BitmapFactory.decodeFile(path, options);
return new BitmapDrawable(a.getResources(), bitmap);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

图片的回收

public static void cleanImageView (ImageView imageView) {
if (!(imageView.getDrawable() instanceof BitmapDrawable))
return;

// clean uo the view's image for the sake of memory
BitmapDrawable b = (BitmapDrawable) imageView.getDrawable();
b.getBitmap().recycle();
imageView.setImageDrawable(null);
}

1
2
3
4
5
6
7
8
9

Bitmap.recycle() 方法的调用需要一些解释。Android开发文档暗示不需要调用Bitmap.recycle()方法,但实际上需要。

Bitmap.recycle()方法释放了Bitmap占用的原始存储空间。这也是Bitmap对象最核心的部分,这取决于具体的安卓版本,原始存储空间可大可小,在Honeycomb之前,它存储了Java Bitmap的所有数据。

如果不主动调用recycle()方法释放内存,占用的存储空间也会被清理。但是,它是在将来的某个时间点在finalizer中清理,而不是在Bitmap自身的垃圾回收中清理。这意味着很可能在finalizer调用之前,应用已经耗尽内存资源了。

finalizer的执行有时不太靠谱,且这类bug很难追踪和重现。因此,如果应用使用的图片文件很大,最好主动调用recycle()方法,以避免可能的内存耗尽问题。

在onStart() 方法中加载图片,在onStop()方法中移除图片是一种好的习惯。这些方法标志着用户可以看到Activity的时间点。


总数:3 页次:1/1 首页 尾页  
总数:3 页次:1/1 首页 尾页  


所在合集/目录



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1