java中的引用和GC

java中的引用和GC

  1. 简介:java中有四种引用:强软弱虚
    1. 强引用: strong Reference,=就是强引用方式,即时出现OOM,GC都不会销毁强引用
    2. 软引用: soft Reference,当Java所管理的内存趋于阈值时,GC将会销毁一部分软引用,释放内存。
    3. 弱引用: weak Reference,就算Java所管理的内存没有趋于阈值,GC都有可能销毁部分弱引用。
    4. 虚引用: 按照GC回收优先度,虚引用肯定是最早被回收的,这个基本开发的时候不会用到
  2. GC:Garbage Collection,是java和C++的主要区别之一,这里简单介绍一下,如果有时间再去写详细介绍,
    1. 垃圾回收机制,java程序员不需要去自己回收内存,不想C和C++的程序员,但是电脑去执行回收机制肯定做不到人那样根据需要去回收,所以会造成一定程序的内存浪费,这就是为什么对安卓里面的OOM很重视。
    2. GC在执行的时候模,所有的线程都会被暂停。当Java所管理内存趋于阈值时,GC的执行频率将会加快,这时候就会出现卡顿的感觉,用户体验杀手。
  3. 下面结合一些代码讲一下这个问题
    1. 早期的时候,图片显示是手机的一个大问题,因为早期手机内存过小,后来出现了高压缩率的图片jpg之类的图片问题得到部分改善,后来内存又扩大,这个问题基本得到了解决,但是随着时间,图片的数量越来越多,再结合服务器,考虑到流量问题。下面具体分析listview结合服务器的图片下载问题
    2. 首先,listview采用了viewholder和adapter进行了优化,但是如果涉及到图片,如果单纯的把图片存在RAM部分,那么加载速度会非常慢,而listview的滑动操作是很快的,这时候就会出现图片没有加载完成所显示的view就不需要了,影响用户体验,之前有写过解决这个问题的方法,是采用了异步任务加中断的方式,但是这种情况适用性有限,而且不好用,性能提升不明显

3. 速度慢的原因是需要去ram中读取的图片的速度过慢导致,所以想到一个办法,将图片存入内存,但当图片过多的时候会出现OOM问题,java程序员是没有办法回收不用的图片内存的,GC这个时候也不会去回收强引用,所以要使用软应用甚至弱引用来解决,这其实是一个二级缓存机制,具体请看下面的代码

    package cn.myapp.musicclient.util;

import java.io.File;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.util.EntityUtils;

import com.tarena.musicclient.R;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListView;

/**
 * 图片异步批量加载的工具类
 * 提供图片的内存缓存与文件缓存
 */
public class ImageLoader {
    //声明工作线程
    private Thread workThread;
    private boolean isLoop=true;
    //存储缓存图片
    private HashMap<String, SoftReference<Bitmap>> cache=new HashMap<String, SoftReference<Bitmap>>();
    private Context context;
    //生成任务集合
    private List<ImageLoadTask> tasks=new ArrayList<ImageLoadTask>();
    private AbsListView listView;
    //声明handler
    private Handler handler=new  Handler(){
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
            case HANDLER_IMAGE_LOAD_SUCCESS:
                //更新ImageView
                ImageLoadTask task=(ImageLoadTask)msg.obj;
                //通过position做的tag 找到相应的ImageView
                ImageView ivAlbum=(ImageView)listView.findViewWithTag(task.position);
                if(ivAlbum!=null){
                    Bitmap bitmap=task.bitmap;
                    if(bitmap!=null){
                        ivAlbum.setImageBitmap(bitmap);
                    }else{
                        ivAlbum.setImageResource(R.drawable.ic_launcher);
                    }
                }
                break;
            }
        }
    };
    public static final int HANDLER_IMAGE_LOAD_SUCCESS=0;



    public ImageLoader(Context context, AbsListView listView) {
        this.listView=listView;
        this.context=context;
        //初始化workThread
        workThread=new Thread(){
            public void run() {
                //不断的查看集合中是否有数据
                while(isLoop){
                    //如果集合中有图片下载任务
                    if(!tasks.isEmpty()){
                        //把第一个图片下载任务获取并执行
                        ImageLoadTask task=tasks.remove(0);
                        //直接下载图片
                        Bitmap bitmap=loadBitmap(task.path);
                        //把bitmap设置到相应的ImageView中
                        //需要在主线程中  (发消息给handler)
                        task.bitmap=bitmap;
                        Message msg=new Message();
                        msg.what=HANDLER_IMAGE_LOAD_SUCCESS;
                        msg.obj=task;
                        handler.sendMessage(msg);
                    }else{
                        //如果任务集合中已经没有任务了 
                        //那么线程等待
                        synchronized (workThread) {
                            try {
                                workThread.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        };
        workThread.start();
    }

    /**
     * 通过path 发送http请求 下载图片
     * @param path 
     * images/junshengjinshi.jpg
     * @return
     * @throws IOException 
     * @throws ClientProtocolException 
     */
    public Bitmap loadBitmap(String path) {
        try {
            String url=GlobalConsts.BASEURL+path;
            HttpEntity entity=HttpUtils.send(HttpUtils.METHOD_GET, url, null);
            //把entity转成Bitmap
            byte[] bytes=EntityUtils.toByteArray(entity);
            Bitmap bitmap=BitmapUtils.loadBitmap(bytes, 50, 50);
            //向内存缓存中 缓存图片
            cache.put(path, new SoftReference<Bitmap>(bitmap));
            //向缓存目录中 保存图片
            File targetFile=new File(context.getCacheDir(), path);
            //targetFile:   /data/data/com.xx.xx/cache/images/jsjs.jpg
            BitmapUtils.save(bitmap, targetFile);

            return bitmap;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 显示图片
     * @param imageView  控件
     * @param path 图片路径
     * @param position 图片的位置
     */
    public void displayImage(ImageView imageView, String path, int position){
        imageView.setTag(position);
        //从缓存中获取  如果有 则直接显示
        //不在向任务集合中添加任务了。
        SoftReference<Bitmap> ref=cache.get(path);
        if(ref!=null){
            Bitmap bitmap=ref.get();
            if(bitmap!=null){
                Log.i("info", "这是从内存缓存中读取的图片..");
                imageView.setImageBitmap(bitmap);
                return;
            }
        }
        //内存缓存中没有图片 则去文件缓存中读取
        String filepath=new File(context.getCacheDir(),path).getAbsolutePath();
        Bitmap bitmap=BitmapUtils.loadBitmap(filepath);
        if(bitmap!=null){
            Log.i("info", "从文件缓存中读取的图片..");
            imageView.setImageBitmap(bitmap);
            //存入内存缓存
            cache.put(path, new SoftReference<Bitmap>(bitmap));
            return;
        }

        //向任务集合中添加任务
        ImageLoadTask task=new ImageLoadTask();
        task.path=path;//保存图片路径
        task.position=position;
        tasks.add(task);
        //唤醒工作线程workThread  起来干活
        synchronized (workThread) {
            workThread.notify();
        }
    }

    class ImageLoadTask{
        String path;
        Bitmap bitmap;
        int position;
    }

    public void stopThread() {
        isLoop=false;
        synchronized (workThread) {
            workThread.notify();
        }       
    }

}

### Java 引用类型详解 Java 中的引用类型是 Java 内存管理的重要组成部分,它决定了对象内存中的生命周期。根据对象的可达性垃圾回收机制的不同,Java 提供了四种引用类型:强引用、软引用、弱引用引用。 #### 1. 强引用(Strong Reference) 强引用是最常见的引用类型,也是默认的引用方式。只要一个对象被强引用所指向,即使 JVM 内存不足,也不会对该对象进行垃圾回收[^4]。 例如: ```java Object obj = new Object(); ``` 在这里,`obj` 是对 `new Object()` 的强引用。只有当 `obj` 被显式赋值为 `null` 或超出作用域时,该对象才可能被垃圾回收器回收。 #### 2. 软引用(Soft Reference) 软引用用于描述那些还有用但非必需的对象。当 JVM 内存不足时,垃圾回收器会回收这些由软引用指向的对象,从而避免内存溢出(OutOfMemoryError)。因此,软引用非常适合用来实现缓存系统[^4]。 创建软引用的示例代码如下: ```java import java.lang.ref.SoftReference; Object obj = new Object(); SoftReference<Object> softRef = new SoftReference<>(obj); ``` 如果 JVM 内存不足,`softRef.get()` 可能返回 `null`,表示对象已被回收。 #### 3. 弱引用(Weak Reference) 弱引用的特点是无论 JVM 内存是否充足,只要发生垃圾回收,被弱引用指向的对象都会被回收[^4]。弱引用通常用于防止内存泄漏,例如在 `WeakHashMap` 中,键值对的键使用弱引用来存储。一旦键被垃圾回收,对应的值也会从哈希表中移除。 以下是一个弱引用的示例: ```java import java.lang.ref.WeakReference; Person person = new Person("Alice"); WeakReference<Person> weakRef = new WeakReference<>(person); System.out.println(weakRef.get()); // 输出: Person{name='Alice'} person = null; System.gc(); // 手动触发垃圾回收 System.out.println(weakRef.get()); // 输出: null ``` #### 4. 虚引用(Phantom Reference) 虚引用是最弱的一种引用类型,它不会影响对象的生命周期。通过虚引用,我们无法访问实际对象,其主要用途是跟踪对象的垃圾回收过程[^4]。虚引用通常与引用队列结合使用,在对象被垃圾回收后,引用会被添加到队列中,从而允许程序执行一些自定义操作。 以下是虚引用的一个简单示例: ```java import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; Object obj = new Object(); ReferenceQueue<Object> queue = new ReferenceQueue<>(); PhantomReference<Object> phantomRef = new PhantomReference<>(obj, queue); obj = null; // 断开强引用 System.gc(); // 手动触发垃圾回收 System.out.println(queue.poll() == phantomRef); // 输出: true ``` #### 引用队列(Reference Queue) 引用队列用于接收那些已经被垃圾回收器回收的对象引用[^5]。通过调用 `poll()` 或 `remove()` 方法,可以从队列中获取这些引用,并执行相应的清理操作。引用队列在软引用、弱引用引用中都可使用。 --- ### 总结 - **强引用**:最常用的引用类型,确保对象不会被垃圾回收。 - **软引用**:适用于缓存场景,仅在内存不足时回收。 - **弱引用**:用于防止内存泄漏,垃圾回收时立即回收。 - **虚引用**:无法访问实际对象,主要用于跟踪垃圾回收过程。 Java引用类型为开发者提供了灵活的内存管理工具,合理使用这些引用类型可以有效避免内存泄漏并优化性能。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值