android native区的大小,Android性能优化----内存篇

作为运行在Linux内核上的移动设备,手机受限于内存的大小,给每一个app都只能分配有限的可用内存,如果开发者的代码编写不当,可能导致应用的内存泄漏,进而导致OOM内存溢出(程序申请内存,但是系统无更多可用空间)。

为了避免内存泄漏,需要我们在平时的代码编写上多加注意,避免产生内存泄漏的情况。可用LeakCanary监控你的应用,当发生内存泄漏的时候他会在适当的时间给你发出警告。当然,这只是一种监控工具,我们还是需要知道如何去处理这个泄漏,并且要知道为什么会产生泄漏。

在java里,内存的释放虽然不需要我们手动去管理,但是对于存在引用的对象,java并不会去释放它,在这块java有两种机制去判断对象是否存在引用:

1.计数器,对象每引用一次就+1,当对象的引用计数为0时,判断该对象为垃辣鸡,赶快把它解决掉吧。但是这种方法对应对象间的相互引用可不好用了。

2.可达性判断,从GCRoot根部判断是否有能到达该对象的路径,如果有,则判断为该对象有引用,不能回收了。

可作为GC Roots的对象有:

1.虚拟机栈(栈帧中的本地变量表)中引用的对象;

2.方法区中的类静态属性引用的对象;

3.方法区中常量引用的对象;

4.本地方法栈中JNI(即一般说的Native方法)中引用的对象

Java内存分配策略

对象优先在Eden分配

大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次 Minor GC

大对象直接进入老年代

所谓大对象是指,需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组。

长期存活的对象将进入老年代

既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代。为做到这一点,虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。对象在Survivor区中每熬过一次Minor GC,年龄就增加1岁,当它的年龄增加到一定程度(默认15岁),就会被晋升到老年代中。对象晋升老年代的年龄阈值,可通过参数-XX:MaxTenuringThreshold设置

动态对象年龄判定

为了能更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

Java 程序运行时的内存分配策略有三种,分别是静态存储区(也称方法区)、栈区和堆区。

静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。

栈区 :当方法被执行时,方法体内的局部变量(其中包括基础数据类型、对象的引用)都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

堆区 : 又称动态内存分配,通常就是指在程序运行时直接 new 出来的内存,也就是对象的实例。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。

Java垃圾回收算法

Mark-Sweep Collector(标记-清除收集器)

标记清除收集器停止所有的工作,从根扫描每个活跃的对象,然后标记扫描过的对象,标记完成以后,清除那些没有被标记的对象。

缺点是耗时长,内存碎片多

Copying Collector(复制收集器)

将内存分为两块,标记完成开始回收时,将一块内存中保留的对象全部复制到另一块空闲内存中。

缺点是需要额外的空间消耗

Mark-Compact Collector(标记-整理收集器)

标记整理收集器汲取了标记清除和复制收集器的优点,它分两个阶段执行,在第一个阶段,首先扫描所有活跃的对象,并标记所有活跃的对象,第二个阶段首先清除未标记的对象,然后将活跃的对象复制到堆的底部。

分代收集算法

当代商业虚拟机的垃圾收集都采用“分代收集(Generational Collection)”算法,该算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适合的收集算法。

Java四种引用

强引用(Strong Reference)最常用的引用类型,如Object obj = new Object();。只要强引用存在则GC时必定不被回收。

软引用(Soft Reference)用于描述还有用但非必需对象,当堆将发生OOM(Out Of Memory)时则会回收软引用所指向的内存空间,若回收后依然空间不足才会抛出OOM。

弱引用(Weak Reference)发生GC时必定回收弱引用指向的内存空间。和软引用加入队列的时机相同

虚引用(Phantom Reference)又称为幽灵引用或幻影引用,虚引用既不会影响对象的生命周期,也无法通过虚引用来获取对象实例,仅用于在发生GC时接收一个系统通知。

GC的各种触发

Minor GC触发条件:当Eden区满时,触发Minor GC。

Full GC触发条件:

(1)调用System.gc时,系统建议执行Full GC,但是不必然执行

(2)老年代空间不足

(3)方法去空间不足

(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存

(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

一般来说,我们需要关注的只是堆这块空间,因为堆存放的都是对象的引用,这一块是很容易造成对象释放不及时而导致的泄漏。

Android常见的内存泄漏

1.匿名类跟非静态内部类默认持有外部类的引用,导致无法释放。解决办法是把它改为静态内部类,并使用弱引用,比如Handler内部类。

2.Handler因为其Message会默认持有Handler的引用,如果在对象释放的时候,MessageQueue还在不断的循环使用,那么将造成无法释放对象。

3.各种资源使用后未释放,比如IO的操作。

4.static使用不当,造成其生命周期伴随整个Application。常见的为单例模式中的Context,最好将其改为Application的Context。

另外,内存抖动也是影响性能的重要因数,在短时间内大量的创建和销毁对象就会导致这种情况的出现。所以在Ondraw方法中不要创建对象,因为android系统在每隔16ms会发出一次刷新界面的信号。对于线程的使用最好通过线程池来进行管理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值