• android开发:一例dv虚拟机资源回收引起的有趣现象,附上一份可用的zip操作的工具类


    自从进入android,接触java以来,已与jvm及其gc机制有过多次“奇妙”接触,jvm的gc实现有很多种,策略也不一样,据我自己亲身体会,即使是android手机的各个厂商的实现(当然,android上面的严格说来并不是jvm)甚至都不一样,让你遇到的话真是欲哭无泪。比如说涉及到softreference或者weakreference的时候,你发现程序的行为和文档说明不一致的时候,请千万不要惊奇。

     

    话说android的文档很多地方也不完善,比如说Bitmap,文档上明明说一般不需要recycle,可是用过的同志应该明白,你敢不recycle吗?(咱不较真儿啊。。。)当然,话题扯远了,发个牢骚,这不,前两天又遇到了一次问题。这个问题之前遇到过一次,百思不得其解,稀里糊涂蒙混过关了。这次再一次遇见,经过仔细观察,认真思考,做实验,查数据,好好纠结,终于寻得要领,并且经过了验证。

     

    假设有一个class名为PublicArea,另外有一个Class名为ZipUtil,前者主要用于数据交换,里面有一些全局数据,后者提供了一个zip相关的static方法,用于从byte[]或者Inputstrem对象中提取需要的指定文件名的文件数据.

    PublicArea类定义了两个全局变量如下:

     

    public static byte[] mZipdata=null;
    public static InputStream mZipIS=null;

     

    ZipUtil类定义如下:

    public class ZipUtil{

    public static Map<String,byte[]> unzipFilesFromStream(InputStream is,String type) throws Exception
    {
    Map
    <String, byte[]> result = new HashMap<String, byte[]>();
    ZipInputStream zipStream
    =new ZipInputStream(is);
    ZipEntry zipEntry;
    while((zipEntry=zipStream.getNextEntry())!=null){
    if (zipEntry.getName().endsWith("."+type))
    {
    int zipsize=(int)zipEntry.getSize();
    byte[] temp=new byte[zipsize];
    byte[] beRead=new byte[zipsize];
    int nowpos=0;
    int beReadThisTime=0;
    while(-1!=(beReadThisTime=zipStream.read(temp)))
    {
    System.arraycopy(temp,
    0,beRead,nowpos,beReadThisTime);
    nowpos
    +=beReadThisTime;
    temp
    =new byte[zipsize];
    }
    result.put(zipEntry.getName(), beRead);
    }
    zipStream.closeEntry();
    }
    zipStream.close();
    return result;
    }


    public static byte[] unzipFileFromStream(InputStream is,String filename) throws Exception
    {
    byte[] result =null;
    ZipInputStream zipStream
    =new ZipInputStream(is);
    ZipEntry zipEntry;
    while((zipEntry=zipStream.getNextEntry())!=null){
    if (zipEntry.getName().equals(filename))
    {
    int zipsize=(int)zipEntry.getSize();
    byte[] temp=new byte[zipsize];
    byte[] beRead=new byte[zipsize];
    int nowpos=0;
    int beReadThisTime=0;
    while(-1!=(beReadThisTime=zipStream.read(temp)))
    {
    System.arraycopy(temp,
    0,beRead,nowpos,beReadThisTime);
    nowpos
    +=beReadThisTime;
    temp
    =new byte[zipsize];
    }
    result
    =beRead;
    }
    zipStream.closeEntry();
    }
    zipStream.close();
    return result;
    }

    }

     

    我在PublicArea中通过一系列动作获取了一份zip文件的二进制数据(比如file读取或者从网络传输)并将对象引用交给mZipdata,然后使用如下代码:

    mZipIS = new ByteArrayInputStream(mZipdata);

    创建字节流并将引用赋值给mZipIS 。

    至此,mZipdata与mZipIS已创建相应对象,同时因为二者均为全局变量,所以除非我手动放弃引用,否则这两个对象将一直存活。

    ZipUtil中的两个方法分别为:

    public static byte[] unzipFileFromStream(InputStream is,String filename)

    public static Map<String,byte[]> unzipFilesFromStream(InputStream is,String type)

    前者根据给定的InputStream获取zip包中名为filename的文件的二进制字对象。

    后者是遍历zip包中的文件并将后缀名为type的文件的二进制字节对象存入一个HashMap中,key为文件名。

    在这里说一下,zip用来做遍历速度很快,它的结构适合提取文件名。

     

    问题来了,我需要分别使用ZipUtil中的这两个方法,先使用unzipFileFromStream,再使用unzipFilesFromStream,InputStream 参数当然就是PublicArea中的mZipIS了。但是在第二次调用ZipUtil的方法的时候,会爆出“NullPointerException”,着实蛋疼。mZipIS的对象是在第一次调用unzipFileFromStream的时候创建的,然后方法跑完,其他线程过一会儿会来调用unzipFilesFromStream方法,仍然想当然地使用这个mZipIS作为参数,然后就可耻地异常。ZipUtil被回收了。其实在开始的时候我还刻意没有去在ZipUtil的实现中调用zipStream.close(); 这样表面上看应该不会存在这样的问题啊,那么这个问题是怎样产生的呢?这就要从多个方面说起了。

     

    首先我们要复习一下java的io类的一个小特点,java的io类实现堪称经典,所有的流式类的实现分为节点流和过滤流两种,用了一种“装饰设计模式”,一环套一环,形成一种链式结构。这里要说一下它的资源释放,当你将各种过滤流串起来之后,关闭的时候,你只需要对最后一个过滤流(就是串的尾端末梢)的引用调用close()方法即可释放整个链式结构里的流对象。

     

     

    而我遇到的问题是因为在ZipUtil的方法实现中,在mZipIS尾端又加入了一个ZipInputStream过滤流,而它是局部变量,所以。。。方法执行完它就被华丽丽地释放了,不光它被释放,整个链式结构都被释放,连累到了mZipIS。虽然mZipIS的引用仍然存在,但是dv虚拟机的gc依然将它回收了,一方面dv虚拟机的gc策略是尽量回收,另一方面我们也要认识到,gc的智能毕竟是有限的,同时程序员良好的编码习惯很重要,肯定不存在完美的jvm和gc,不要将自动回收的机制掺和到太过于复杂的程序流程,否则真的是自讨苦吃,对于简单的程序流程来说,大部分的jvm的gc行为应该基本一致,因为没什么好选择的,所以相对安全。虽然我们可以把很多问题怪罪于jvm和他gc不够完善,但是。。。你能和机器较什么劲儿呢 - -#

     

     

    这里我们也看到,对于mZipdata这种全局数据存储的引用的使用,在哪里产生流就在哪里结束,不要依赖于一个流对象幻想用个千八百遍,你组合的一连串的面向需求的链式结构用完了就整体销毁掉,养成良好的习惯,你自己手工销毁那你自然该知道在用的时候创建。至于对数据引用的保持,还是使用byte[]引用吧,用的时候创建流,这样条理清晰,也不麻烦,不能太懒啊。当然了,极端情况你看着办吧,比如神马网络不顺畅并且没有存储空间并且内存不够。。。。那就让程序自杀好了(开玩笑的),跑题了,哈哈。

     

    对了,那个zip工具类可以直接拿去用哦,有问题欢迎探讨指教。

  • 相关阅读:
    湘潭大学 Hurry Up 三分,求凹函数的最小值问题
    hdu 1166 线段树 单点修改 + 询问区间求和 (线段树模板)
    hdu 1166 树状数组(模板) 更改点值+求区间和
    getline
    poj 1873 The Fortified Forest 凸包+位运算枚举 world final 水题
    C# 代码操作XML(增、删、改)
    C# Socket服务端与客户端通信(包含大文件的断点传输)
    MD5 十六进制加密
    C# 面向对象——多态
    C# 面向对象——继承
  • 原文地址:https://www.cnblogs.com/elanp/p/2008114.html
Copyright © 2020-2023  润新知