一、概述
在前面一篇博客整理了Android性能优化之内存优化,这篇就整理整理内存优化方面的知识
1.简介
RAM(random access memory)随机存取存储器。分为下面5种
- 寄存器存储:速度最快。因为寄存器位于处理器内部,我们程序无法控制。
- 栈(stack):存放的是基本数据类型(注意String不是基本数据类型)和引用。对象本身不存储在栈中,而是存储在堆中。
- 堆(Heap):存放的是new产生的对象。在堆中的内存,由Java虚拟机自动回收。
- 静态域:静态域中存放的是程序运行时一直存在的数据,如由static修饰的变量。
- 常量池:常量池中除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值还包含一些以文本形式出现的符号引用。
2.常量池
###1.概念:
java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个)。这个可以和线程池相似。其实说白了常量池也是一个内存空间。但是和new创建出来的空间不一样。下面通过代码演示两者的区别
###2.代码演示
public static void main(String[] args) {
Integer a1 = new Integer(127) ;
Integer a2 = new Integer(127) ;
System.out.println(a1==a2); //false
Integer a3 = Integer.valueOf(127) ;
Integer a4 = Integer.valueOf(127) ;
System.out.println(a3 == a4); //true
System.out.println(a1 == a3 ); //false
Integer a5 = Integer.valueOf(128) ;
Integer a6 = Integer.valueOf(128) ;
System.out.println(a5 == a6); //false
}
由上述的代码可知:每次new的都是一个新的空间,而第二个方式为什么返回true呢?这是因为大部分的基本数据类型都实现了常量池技术。这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种double、float则没有实现。而且 Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,另外注意一点:String也实现了线程线程池技术。
2.分析Integer类型valueOf()方法
下面就Integer型的为什么127可以而128不可以,看源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
那low和high是什么呢?
static final int low = -128;
static final int high;
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
看了上面的源码是不是明白了如果不是在-128到127之间,则会重新new的。
栈和堆的比较
- 栈:存取速度比堆要快,仅次于寄存器。当定义一个变量时,(注意:这里不是申明,申明一个变量是不会分配内存的)。Java就在栈中为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。
- 堆:存取速度较慢。当堆中的new产生数组和对象超出其作用域后,它们不会被释放,只有在没有引用变量指向它们的时候才变成垃圾,不能再被使用。即使这样,所占内存也不会立即释放,而是等待被垃圾回收器gc收走。这也是Java比较占内存的原因。
int a = 1;
int b = 1 ;
分析:
编译器先处理int a = 1;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向1。接着处理int b =1;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向1。这样,就出现了a与b同时均指向3的情况。这时,如果再让a=2;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并让a指向1;如果已经有了,则直接将a指向这个地址。
二、OOM
1.为什么会出现OOM
oom(Out Of Memory)在我们写代码的时候肯定经常遇到,那么为什么会出现这种问题呢?
- 有限的堆内存,一般默认是16M。
- 运行在虚拟机之上
- 支持后台多任务处理
2.Bitmap
图片是最容易造成oom的,尤其是在ListView中或者瀑布流中上下滑动,不断加载,程序很容易崩溃。
解决方案
使用BitmapFactory.Options设置inSampleSize, 这样做可以减少对系统资源的要求。比如
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true ;
options.inSampleSize = 3 ;
上面2行代码就设置缩略图为原来的图的1/9。
图片像素:
- ALPHA_8:每个像素占用1byte内存
- ARGB_4444:每个像素占用2byte内存
- ARGB_8888:每个像素占用4byte内存
- RGB_565:每个像素占用2byte内存
图片默认是ARGB_8888,由此可以看出占用的内存比较大,再对图片要求不高的话,我们可以使用ARGB_4444或者RGB_565。
回收:
在我们不需要用某张图片的时候,可以将其回收
// 先判断是否已经回收
if(bitmap != null && !bitmap.isRecycled()){
// 回收并且置为null
bitmap.recycle();
bitmap = null;
}
4中引用类型
- 强引用:基本不会被释放。
- 软引用:SoftReference,对Bitmap来说,就是用SoftReference包装下Bitmap
- 弱引用:WeakReference。
- 虚引用:PhantomReference
不过现在已经不推荐用软(弱)应用了,推荐使用LruCache(最近最少使用),关于4种种引用的具体用法会在下篇和LruCache一起介绍。
三、使用static修饰的变量
private static Context context ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
context = this ;
}
上面会导致Activity无法销毁,因为静态变量引用了当前Activity。
四、属性动画造成的内存泄漏
属性动画中有一类无限循环的动画,如果没有在onDestory()方法停止,就会无限循环下去,并且Activity的view将会被动画持有。而view又持有了Activity,导致Activity无法销毁。
OK,这篇关于内存优化的就结束了。