Android性能优化—内存优化

news2024/12/25 1:13:48

一、App内存组成以及管理

Android 给每个 App 分配一个 VM ,让App运行在 dalvik 上,这样即使 App 崩溃也不会影响到系统。系统给 VM 分配了一定的内存大小, App 可以申请使用的内存大小不能超过此硬性逻辑限制,就算物理内存富余,如果应用超出 VM 最大内存,就会出现内存溢出 crash,即OOM。 

由程序控制操作的内存空间在 heap 上,分 java heapsize 和 native heapsize 。 

1)Java申请的内存在 vm heap 上,所以如果 java 申请的内存大小超过 VM 的逻辑内存限制,就会出现内存溢出的异常。

2)native层内存申请不受其限制, native 层受 native process 对内存大小的限制。 

二、Android设备对App的内存限制 

如何查看APP的内存大小限制?

1. 主要查看系统配置文件 build.prop ,我们可以通过 adb shell 在 命令行窗口查看 

adb shell cat /system/build.prop

2. 通过代码获取

ActivityManager activityManager =
(ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)
activityManager.getMemoryClass();//以m为单位

3.修改内存限制

修改 \frameworks\base\core\jni\AndroidRuntime.cpp 

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
	/*
	 * The default starting and maximum size of the heap. Larger
	 * values should be specified in a product property override.
	 */
	parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf,
	"-Xms", "4m");
	parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx",
	"16m");//修改这里
}

修改 platform/dalvik/+/eclair-release/vm/Init.c

gDvm.heapSizeStart = 2 * 1024 * 1024; // Spec says 16MB; too big for us.
gDvm.heapSizeMax = 16 * 1024 * 1024; // Spec says 75% physical mem

三、Android内存分配与回收机制

1、内存分配 

Android的Heap空间是一个 Generational Heap Memory 的模型,最近分配的对象会存放在 Young
Generation 区域,当一个对象在这个区域停留的时间达到一定程度,它会被移动到 Old Generation ,最后累积一定时间再移动到 Permanent Generation 区域。

1)Young Generation

由一个Eden区和两个Survivor区组成,程序中生成的大部分新的对象都在Eden区中,当Eden区满时,还存活的对象将被复制到其中一个Survivor区,当次Survivor区满时,此区存活的对象又被复制到另一个Survivor区,当这个Survivor区也满时,会将其中存活的对象复制到年老代。

2)Old Generation

一般情况下,年老代中的对象生命周期都比较长。

3)Permanent Generation

用于存放静态的类和方法,持久代对垃圾回收没有显著影响。

总结:内存对象的处理过程如下:

1. 对象创建后在Eden区。
2. 执行GC后,如果对象仍然存活,则复制到S0区。
3. 当S0区满时,该区域存活对象将复制到S1区,然后S0清空,接下来S0和S1角色互换。
4. 当第3步达到一定次数(系统版本不同会有差异)后,存活对象将被复制到Old Generation。
5. 当这个对象在Young Generation区域停留的时间达到一定程度时,它会被移动到Old
Generation,最后累积一定时间再移动到Permanent Generation区域。

2、Generation GC

系统在Young Generation、Old Generation上采用不同的回收机制。每一个Generation的内存区域都有固定的大小。随着新的对象陆续被分配到此区域,当对象总的大小临近这一级别内存区域的阈值时,会触发GC操作,以便腾出空间来存放其他新的对象。

1)执行GC占用的时间与Generation和Generation中的对象数量有关:

1. Young Generation < Old Generation < Permanent Generation
2. Gener中的对象数量与执行时间成正比。

2)Young Generation GC

由于其对象存活时间短,因此基于Copying算法(扫描出存活的对象,并复制到一块新的完全未使用的控件中)来回收。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在YoungGeneration区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。

3)Old Generation GC

由于其对象存活时间较长,比较稳定,因此采用Mark(标记)算法(扫描出存活的对象,然后再回收未被标记的对象,回收后对空出的空间要么合并,要么标记出来便于下次分配,以减少内存碎片带来的效率损耗)来回收。 

3、Java内存分配模型 

4、可达性分析与GCRoots 

通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所有的引用链,当一个对象到GC Roots没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的。 

GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区、栈和本地方法区不被GC所管理,因而选择这些区域内的对象作为GC roots,被GC roots引用的对象不被GC回收。 

5、GC 回收算法

1)标记清除算法:

1 位置不连续,产生碎片;
2 效率略低;
3 扫描两遍。

2)复制算法:

1 实现简单、运行高效;
2 没有内存碎片;
3 利用率只有一半 。

3)标记整理算法:

1 没有内存碎片;
2 效率偏低;
3 扫描两遍、指针需要调整 。

四、内存三大问题 

1、内存抖动

通过Memory Profiler检测,内存波动图形呈锯齿张,频繁GC容易导致页面卡顿。常出现于:循环里创建对象,自定义onDraw创建对象等。

2、内存泄漏

在当前应用周期内不再使用的对象被GC Roots引用,导致不能回收,使实际可使用内存变小;Android内存泄漏分析工具:Memory Profiler和LeakCanary(查看文章,LeakCanary内存泄漏检测框架分析:http://t.csdn.cn/SEpWC);

Android内存泄漏常见场景以及解决方案:

1. 资源性对象未关闭
对于资源性对象不再使用时,应该立即调用它的close()函数,将其关闭,然后再置为null。例如文件、数据库和Bitmap等资源未关闭会造成内存泄漏,此时我们应该在Activity销毁时及时关闭。

2. 注册对象未注销
例如BraodcastReceiver、EventBus未注销造成的内存泄漏,我们应该在Activity销毁时及时注销。

3 .类的静态变量持有大数据对象
尽量避免使用静态变量存储数据,特别是大数据对象,建议使用数据库存储。

4 .单例造成的内存泄漏
优先使用Application的Context,如需使用Activity的Context,可以在传入Context时使用弱引用进行封装,然后在使用到的地方从弱引用中获取Context,如果获取不到,则直接return即可。

5. 非静态内部类的静态实例
该实例的生命周期和应用一样长,这就导致该静态实例一直持有该Activity的引用,Activity的内存资源不能正常回收。此时,我们可以将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,尽量使用Application Context,如果需要使用Activity Context,就记得用完后置空让GC可以回收,否则还是会内存泄漏。

6. Handler临时性内存泄漏
Message发出之后存储在MessageQueue中,在Message中存在一个target,它是Handler的一个引用,Message在Queue中存在的时间过长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity或者Service不会被回收。并且消息队列是在一个Looper线程中不断地轮询处理消息,当这个Activity退出时,消息队列中还有未处理的消息或者正在处理的消息,并且消息队列中的Message持有Handler实例的引用,Handler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。解决方案如下所示:

    1)使用一个静态Handler内部类,然后对Handler持有的对象(一般是Activity)使用弱引用,这样在回收时,也可以回收Handler持有的对象。
    2)在Activity的Destroy或者Stop时,应该移除消息队列中的消息,避免Looper线程的消息队列中有待处理的消息需要处理。需要注意的是,AsyncTask内部也是Handler机制,同样存在内存泄漏风险,但其一般是临时性的。对于类似AsyncTask或是线程造成的内存泄漏,我们也可以将AsyncTask和Runnable类独立出来或者使用静态内部类。

7、容器中的对象没清理造成的内存泄漏
在退出程序之前,将集合里的东西clear,然后置为null,再退出程序。

8、WebView
WebView都存在内存泄漏的问题,在应用中只要使用一次WebView,内存就不会被释放掉。我们可以为WebView开启一个独立的进程,使用AIDL与应用的主进程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,达到正常释放内存的目的。

9、使用ListView时造成的内存泄漏
在构造Adapter时,使用缓存的convertView。

3、内存溢出

即OOM,OOM时会导致程序异常。Android设备出厂以后,java虚拟机对单个应用的最大内存分配就确定下来了,超出这个值就会OOM。

1)OOM原因分类

2)OOM代码分析

Android 虚拟机最终抛出OutOfMemoryError的地方:/art/runtime/thread.cc

void Thread::ThrowOutOfMemoryError(const char* msg) {
	LOG(WARNING) << StringPrintf("Throwing OutOfMemoryError \"%s\"%s",
	msg, (tls32_.throwing_OutOfMemoryError ? " (recursive case)" : ""));
	if (!tls32_.throwing_OutOfMemoryError) {
		tls32_.throwing_OutOfMemoryError = true;
		ThrowNewException("Ljava/lang/OutOfMemoryError;", msg);
		tls32_.throwing_OutOfMemoryError = false;
	} else {
		Dump(LOG_STREAM(WARNING)); // The pre-allocated OOME has no stack, so
		help out and log one.
		SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError());
	}
}

堆内存分配失败:/art/runtime/gc/heap.cc

void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count,
AllocatorType allocator_type) {
	// If we're in a stack overflow, do not create a new exception. It would
	require running the
	// constructor, which will of course still be in a stack overflow.
	if (self->IsHandlingStackOverflow()) {
		self->SetException(
		Runtime::Current()-
		>GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
		return;
	}
	std::ostringstream oss;
	size_t total_bytes_free = GetFreeMemory();
	//为对象分配内存时达到进程的内存上限
	oss << "Failed to allocate a " << byte_count << " byte allocation with "
	<< total_bytes_free
	<< " free bytes and " << PrettySize(GetFreeMemoryUntilOOME()) << "
	until OOM,"
	<< " target footprint " <<
	target_footprint_.load(std::memory_order_relaxed)
	<< ", growth limit "
	<< growth_limit_;
	//没有足够大小的连续地址空间
	// There is no fragmentation info to log for large-object space.
	if (allocator_type != kAllocatorTypeLOS) {
		CHECK(space != nullptr) << "allocator_type:" << allocator_type
		<< " byte_count:" << byte_count
		<< " total_bytes_free:" << total_bytes_free;
		space->LogFragmentationAllocFailure(oss, byte_count);
	}
}

创建线程失败:/art/runtime/thread.cc

void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t
stack_size, bool is_daemon) {
	CHECK(java_peer != nullptr);
	Thread* self = static_cast<JNIEnvExt*>(env)->GetSelf();
	// TODO: remove from thread group?
	env->SetLongField(java_peer,
	WellKnownClasses::java_lang_Thread_nativePeer, 0);
	{
		std::string msg(child_jni_env_ext.get() == nullptr ?
		StringPrintf("Could not allocate JNI Env: %s", error_msg.c_str()) :
		StringPrintf("pthread_create (%s stack) failed: %s",
		PrettySize(stack_size).c_str(),
		strerror(pthread_create_result)));
		ScopedObjectAccess soa(env);
		soa.Self()->ThrowOutOfMemoryError(msg.c_str());
	}
}

OOM问题比较常出现在:Bitmap没有进行任何压缩处理,直接加载原图到APP上面,而导致OOM;长时间的内存泄漏也是导致OOM问题的原因之一。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/833038.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WPF中自定义Loading图

纯前端方式&#xff0c;通过动画实现Loading样式&#xff0c;如图所示 <Grid Width"35" Height"35" HorizontalAlignment"Center" VerticalAlignment"Center" Name"Loading"><Grid.Resources><DrawingBrus…

react icon ant简单使用

refer&#xff1a; 文字提示 Tooltip - Ant Design 1.首先保证已经引入了Ant 2.在组件&#xff08;页面&#xff09;引入tooltip import { Form, Tooltip } from antd; 3.在合适的位置使用tooltip&#xff1a; <span>寿命 <Tooltip title"这是寿命的说明&quo…

静/动态网页、Web1.0/2.0、安装tomcat8

一、简述静态网页和动态网页的区别。 静态页面&#xff1a;请求响应信息&#xff0c;发给客户端进行处理&#xff0c;由浏览器进行解析&#xff0c;显示的页面。在网站设计中&#xff0c;纯粹HTML格式的网页&#xff08;可以包含图片、视频JS (前端功能实现&#xff09;、CSS …

app自动化测试

在实习过程中&#xff0c;我接触到了一些SDL安全提测的工作。原来我是学web端渗透比较多的&#xff0c;移动端这块基本没怎么试过手&#xff0c;结果刚开始一直踩坑&#xff0c;连抓包都抓不到(&#xff34;▽&#xff34;)。 下面记录下我遇到的部分问题和解决方法&#xff0c…

uni.chooseLocation 安卓端 无法显示地址列表?

uni.chooseLocation 安卓端 无法显示地址列表&#xff1f; 高德官方申请Android服务平台时&#xff0c;提示填写SHA1值&#xff0c;填写之后发现还是不生效&#xff1f; 这里有个巨坑&#xff0c;如果你真的去填写SHA1值&#xff0c;你会发现地址列表还是不会展示&#xff0c;需…

编写一个最简单的Linux服务端和客户端程序

2023年8月3日&#xff0c;周四下午 这篇文章我从下午开始写了几个小时&#xff0c; 这篇文件基本总结了我今天学到的知识&#xff0c; 在写这篇文章的过程中灵感不断涌现、想明白了很多知识点&#xff0c;非常酣畅淋漓。 什么叫做深度学习&#xff1f;这就是深度学习&#…

仅一个月作品获推荐550.51w,视频号内容创作出现新趋势

7月初&#xff0c;视频号上线直播切片授权功能&#xff0c;创作者经授权可对直播内容进行二次加工上传至短视频平台&#xff0c;对新进入的普通创作者来说&#xff0c;切片授权无疑是一种很好的起号策略&#xff0c;吸引了大量用户。 视频号不断丰富内容生态&#xff0c;大批创…

Flink作业调度的9种状态

1.什么是作业调度 Flink 通过 Task Slots 来定义执行资源。每个 TaskManager 有一到多个 task slot&#xff0c;每个 task slot 可以运行一条由多个并行 task 组成的流水线。 这样一条流水线由多个连续的 task 组成&#xff0c;比如并行度为 n 的 MapFunction 和 并行度为 n 的…

WPF上位机8——C#与MySQL

ADO.NET 数据库连接 数据插入、删除、更改 数据查询 带单个参数 带多个参数 基于特性反射DAO通用操作库 ORM对象关系映射 数据库中表映射为实体类 调用ORM框架中提供的新增方法&#xff1a;构建sql语句 创建枚举 根据枚举类型创建sql语句 调用 实体限定 生成参数列表 生成插入…

【项目 进程13】2.28共享内存(1) 2.29共享内存(2)

文章目录 2.28共享内存&#xff08;1&#xff09;共享内存&#xff08;效率最高&#xff0c;比内存映射更高。因为内存映射还需一个文件做载体&#xff09;共享内存使用步骤共享内存操作函数头文件 2.29共享内存&#xff08;2&#xff09;共享内存相关问题共享内存和内存映射的…

MongoDB文档-基础使用-在客户端(dos窗口)/可视化工具中使用MongoDB基础语句

阿丹&#xff1a; 本文章将描述以及研究mongodb在客户端的基础应用以及在spring-boot中整合使用mongodb来完成基本的数据增删改查。 先放官方的文章 MongoDB CRUD操作 - MongoDB-CN-Manual 本文章分为&#xff1a; 在客户端&#xff08;dos窗口&#xff09;/可视化工具中使用…

工作流管理系统如何提升生产力

工作流管理工具通过提供无缝沟通和简化流程来提高团队的生产力。如果部署得当&#xff0c;工作流管理可以长期获得回报。 以下是工作场所管理系统如何提高员工的生产力&#xff1a; 1、无需更多的状态更新会议 工作流管理系统使您的员工无需跟踪电子邮件和讨论线程来评估项目…

【Pytorch】下载CIFAR10数据集报错: urllib.error.URLError: <urlopen error name: https>

在使用Pytorch 下载CIFAR10的时候&#xff0c;遇到一个报错&#xff0c; 可能是网络特别慢导致的&#xff0c;一般情况下都会遇到这个报错。 解决办法&#xff1a; 1、到官网直接下载这个压缩包&#xff0c;解压。 http://www.cs.utoronto.ca/~kriz/cifar.html 解压后&#x…

一套ai绘图软件教程帮你打开创作新篇章

在一个寂静的艺术工作室里&#xff0c;住着一个渴望成为画家的年轻人&#xff0c;名叫艾米。她梦想能够运用自己的创造力和手艺&#xff0c;创作出令人叹为观止的艺术作品。然而&#xff0c;面对空白的画布&#xff0c;她感到无从下手&#xff0c;不知道该从何处开始。每次她试…

数据可视化(七)常用图表的绘制

1. #seaborn绘制常用图表 #折线图 #replot&#xff08;x&#xff0c;y&#xff0c;kind&#xff0c;data&#xff09; #lineplot&#xff08;x&#xff0c;y&#xff0c;data&#xff09; #直方图 #displot&#xff08;data&#xff0c;rug&#xff09; #条形图 #barplot&…

MONAI的测试与使用(一)

MONAI的测试与使用&#xff08;一&#xff09; 一.Transform 的分类二 普通变换和字典变换的联系与区别三 加载与显示图像 一.Transform 的分类 具体API函数请参考文档&#xff1a;https://docs.monai.io/en/latest/transforms.html 二 普通变换和字典变换的联系与区别 普通变…

DAY2,C高级(shell脚本的使用)

1.今日思维导图&#xff1b; 2.递归实现&#xff0c;输入一个数&#xff0c;输出这个数的每一位&#xff1b; #include<my_head.h>void Output(int num) {if(num 0)return;Output(num/10);printf("%d ",num%10);}int main(int argc, const char *argv[]) {in…

软件测试自动化selenuim的常用方法和属性总结

selenuim其实主要就是使用webdriver实例对象的方法和属性。 常用属性 1 driver.current_url 当前网页的请求地址 2 driver.current_window_handle 句柄&#xff0c;用于页面切换 3 driver.page_source 网页源代码 4 driver.title 网站的title&#xff0c;tab栏上显示的内容…

『Linux学习笔记』Linux服务器硬件配置查看

Linux服务器硬件配置查看 文章目录 一. 操作系统1.1. 发行版本1.2. 查看内核版本 二. CPU2.1. CPU统计信息2.2. CPU型号2.3. 查看物理CPU数目2.4. 每个物理CPU的核数2.5. 查看逻辑CPU的个数 三. GPU四. 内存4.1. 内存使用情况4.2. 内存硬件情况4.3. 查看内存使用信息 五. 硬盘…

幂等性问题 —— 如何防止重复创建订单

幂等性&#xff1a;对接口的多余重复调用的结果和单次调用的结果一致。 一、需要幂等性的场景 二、幂等性实现方案 核心思想&#xff1a;首先要根据业务判断什么是重复相同的请求&#xff0c;然后相同的请求都携带或生成一个唯一的识别码&#xff0c;这样在服务端就可以进行判…