很多人以为java有虚拟机去管理内存,程序员就可以完全高枕无忧地随便创建对象,不用管它们的销毁或回收。是的,确实在一般的情况下我们不需要去太多地在意对象的创建和它什么时候被销毁,直到。。。。。。。程序曝出了OOM(outofmemory),卧槽内存溢出了。因此,本人觉得搞清楚java的内存管理回收机制,并在此基础上使用一些优化机制就显得很重要了。
一、java内存的分配
寄存器:速度最快的存储区域,位于处理器中,程序无法控制
栈内存:存放对象的引用、基本数据类型,同时值得注意的是栈内存中的数据是共享的。例如int a=3;int b=3;它们指向的都是同一个3的引用地址。
堆内存:用来存放对象的实例,即通过new关键字创建出来的对象和数组。多个变量可以指向同一个对象,例如:People a=new People(); Peeple b=a; 这样变量a和b的引用都是指向同一个堆内存中的实例,如果b改变了里面的成员变量,那么a也会跟着改,原因是它们指向的都是同一个实例。
静态域:在固定的地方存放存续运行时一直存在的数据,例如静态变量和一些特殊的全局的数据。
常量存储:存放常量值的地方
二、垃圾回收机制
我们这里讨论的是如何判定一个对象是否应该被当成垃圾回收。java中使用的是可达性分析算法来判断对象是否可以被回收。这个算法的基本思想就是通过一系列称为“GC Roots”的对象作为起点,从这些起点向下搜索,搜索走过的路劲称为引用链。当一个对象和任何一个“GC Roots“都没有引用链连接时(图论上说从GC Roots 到这个对象不可达),则判定这个对象可以被回收。
在java中可以被当做GC Roots的对象有:
虚拟机栈(栈帧中的本地变量表)引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI(即native方法)引用的对象
三、引用类型
强引用:这种引用类型我们平时用得是最多的,就是常规的引用,例如“People p=new People();”,只要引用还在,虚拟机即使抛出OOM,也不会去回收这个新创建的对象。当然如果变量超出了它的作用范围,也没有其他地方引用它,那么这个变量就会被销毁,因此它指向的对象也会被回收。
如果想中断强引用和对象之间的关联,可以把该引用赋值为NULL。
软引用:软引用是用来描述那些有用但不是必须的对象引用,java中用java.lang.ref.SoftRefrence来使用软引用,虚拟机只有在内存不足的情况下才会去回收软引用的对象。这个特性很适合用来做缓存:比如图片的缓存。
代码中可以如下:
SoftRefrence<People> sr = new SoftRefrence<String>(new People());
//通过sr.get()来获取这个对象
System.out.println(sr.get());
弱引用:也是用来描述非必须的对象,只是在执行GC时,无论内存是否充足都会被回收掉。在java中用java.lang.ref.WeakRefrence来使用弱引用。
代码:
WeakRefrence<People> wr=new WeakRefrence<People>(new People());
System.out.println(wr.get());
System.gc(); //通知虚拟机启动回收
System.out.println(wr.get());
//最后输出为:
People
null
虚引用:和没有引用关联是一样的,不用考虑。
四、通过软引用防止内存溢出
我们在创建一些很费内存、生命周期很长的对象的时候,尽量用软引用或者弱引用。我们就以图片为例,相信很多刚刚开始做Android开发的童鞋都会踩因图片加载导致OOM的坑。当然解决的办法有多种,下面通过软引用技术来解决图片缓存的问题:
首先建立一个HashMap集合来保存软引用对象
private Map<String,SoftRefrence<Bitmap>> mapcache = new HashMap<String,SoftRefrence<Bitmap>>();
再定义一个方法,用来把指定文件路径的图片读进内存,保存在我们的缓存中
public void readIntoCache(String path){
//强引用bitmap对象
Bitmap bitmap=BitmapFactory.decodeFile(path);
//软引用bitmap对象
SoftRefrence<Bitmap> sf = new SoftRefrence<Bitmap>(bitmap);
//添加到缓存Map集合中
mapcache.put(path,sf);
提供一个方法获取缓存中图片的强引用
public Bitmap getCacheBitmap(String path){
SoftRefrence<Bitmap> temp = mapcache .get(path);
//判断是否存在软引用,是否被回收了
if(temp==null){
return null;
}
Bitmap bitmap = temp.get();
return bitmap;
这样我们在获取图片的时候可以现在缓存中去获取,如果缓存中没有,那么我们先把图片读进缓存中后,再去缓存中找到这张图片来使用。