你知道数据大小吗?--不要花太多的功夫来隐藏类的成员(三)

原创 2003年05月22日 08:29:00
 

我们能做点什么呢?<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

“这很好,但是我们没有任何选择除了使用String和其它Java提供的类型,是不是这样呢?”我听到你们再问,那么让我们来找找答案吧。

 

l         封装类

封装类比如java.lang.Integer,看起来保存大量的数据在内存中像一个坏的选择。如果你尽力为了内存的经济,那么就要避免这么做。使用你自己的针对int的向量类并不难。当然,如果Java的核心函数库已经包含了这个那就最好不过了。或许这种情况在Java拥有特殊类型的时候将会大大改观。

 

l         多位数组

对于使用多维数组的大型的数据结构,你可以时常的通过简单的索引变换减少额外的维数/例如:转换int[dim1][dim2]的实例到一个int[dim1*dim2]的实例,改变所有形如a[i][j]的表达式为a[i*dim1+j]。这样你就不必花功夫在dim1上的索引检查可以提高效率。

 

l         java.lang.String

你可以使用一些小技巧去减少你的应用中字符串的静态内存大小。

首先,你可以尝试一种很平常的技术,就是当一个应用从一个数据文件或者网络连接中载入或者缓存很多的字符串,并且这种字符串的值是有限变化的。举个例子:如果你想分析一个XML文件,在这个文件中,你经常遇到某种属性,但是这个属性仅仅被限制在两个可能的值。你的目标:通过一个散列映射过滤所有的字符串,减少所有相同的但是明显字符串和目标对象引用一样的。

    public String internString (String s)

    {

        if (s == null) return null;

       

        String is = (String) m_strings.get (s);

        if (is != null)

            return is;

        else

        {

            m_strings.put (s, s);

            return s;

        }

    }

   

    private Map m_strings = new HashMap ();

如果适用成功,这个技巧能够成倍的减少你的静态内存需求。一个富有经验的读者应该能够观察到这个技巧复制java.lang.String.intern()的功能性。有无数的理由存在来让你避免使用String.intern()方法。其中一个就是现在的JVM几乎没有能实现大量数据的保留。

如果你的字符串是完全不同的,会发生什么情况呢?这就是要介绍的第二个技巧,重新收集那些小的字符串空间,这些空间潜在的隐藏于char数组中,因为使用数组大概只占了字符串封装所占用的内存的一半。因此,当我们的应用缓存许多独特的字符传值,我们仅仅只要保持在内存中的数组,在需要的时候转换为字符串。如果这个字符串只是作为暂时的,很快就会抛弃,这将很有效果。一个简单的实验就是从一个字典文件中选出作为缓存的90000个单词,这些数据大约5.6M的大小,如果是char的话,只需要3.4M的空间,只占用了以前的65%

第二个技巧明显的包含一个不利条件,就是你不能支持通过一个构造函数转换一个char[]成为字符串,因为这个构造函数没有复制这个数组而将拥有这个数组。为什么呢?因为这个完全的public的字符串API确保每一个字符串是不可变的,所以每个字符串的构造函数显然要复制输入的数据然后传入它的参数。

 

然后,我们将使用第三个技巧。这个技巧用在当转换一个char数组为一个字符串的代价证实太高的时候。该技巧使用java.lang.String.substr()的功能避免数据复制:这个方法的是显示用了字符串的不变性,并且创建的一个影子字符串对象来共享字符内容,但是它的内部的开始位置和结束位置都是正确的。我们还是写一个例子,new String(“smiles”).substring(1,5)是一个字符串,这个字符串是字符缓冲从位置1到位置4的字符结束,并且这个字符缓冲将共享原来的字符串构造函数指向的字符缓冲。你可以象一下这样使用:给出一个大的字符串集合,你可以合并它的字符内容到一个大的字符数组,在它之上创建一个字符串,并且使用这个主串的子串来重新创建一个原来的字符串。如以下描述:

 

    public static String [] compactStrings (String [] strings)

    {

        String [] result = new String [strings.length];

        int offset = 0;

       

        for (int i = 0; i < strings.length; ++ i)

            offset += strings [i].length ();

       

        // Can't use StringBuffer due to how it manages capacity

        char [] allchars = new char [offset];

       

        offset = 0;

        for (int i = 0; i < strings.length; ++ i)

        {

            strings [i].getChars (0, strings [i].length (), allchars, offset);

            offset += strings [i].length ();

        }

       

        String allstrings = new String (allchars);

       

        offset = 0;

        for (int i = 0; i < strings.length; ++ i)

            result [i] = allstrings.substring (offset,

                                             offset += strings [i].length ());

       

        return result;

    }

以上方法返回一个新的字符串集等同于输入的字符串集,但是在内存中更加得紧凑。重新获得每个字符串数组的16个字节的头部,在方法中被有效的移除。这个存储在缓存压缩大多数短的字符串时比较有效果。当这个方法用于同样的90000个单词的字典时,内存从5.6M节约到4.2M,大概节约了30%

 

l         这些努力是否值得呢?

我这里提到的方法看起来都是很细微的优化,是否值得花时间去实现呢?但是,记住我们脑子里面应该记住:服务端的应用程序能够缓存大量的数据在内存中的话讲能够大大的提高从磁盘和数据库提取数据的性能和效率。在当前32位的JVM中,几百兆的缓存数据代表堆中很引人注意的位置。减少30%或者更多不应该被嘲笑,它能将系统的可测性质中能提高很显著的水平。当然这些技巧不适用于一开始就很好设计的数据结构,事实的决定要由hotspots来决定。无论如何,你现在应该更加了解你的对象消耗了多少内存。

 

关于作者:

Vladimir Roubtsov拥有超过12年的多种语言的编程经验,其中包括从1995年就开始用得Java。目前,它作为资深开发者在Austin, Texas.Trilogy开发企业级软件。平时的业余爱好就是开发一些关于Java字节代码或源程序代码的工具。

你知道数据大小吗?--不要花太多的功夫来隐藏类的成员(二)

 结果:让我们来对一些类使用这个工具,察看是否结果和我们预想的一样。注意:以下的结果都是基于Windows平台的jdk1.3.1版本,并不能保证所有的平台或者jdk版本都得到相同的信息。l      ...
  • copyright
  • copyright
  • 2003年05月22日 08:29
  • 804

你知道数据大小吗?--不要花太多的功夫来隐藏类的成员

  • zgqtxwd
  • zgqtxwd
  • 2008年04月25日 15:57
  • 91

你知道数据大小吗?--不要花太多的功夫来隐藏类的成员(一)

 你知道数据大小吗?--不要花太多的功夫来隐藏类的成员 摘要:过去很多年里面,许多的Java开发人员都一直在问一个问题:“一个Java对象到底耗费多少内存呢?”在本文中,Vladimir Roubts...
  • copyright
  • copyright
  • 2003年05月22日 08:29
  • 1231

c++中类没有数据成员时,类对象的大小

class X { }; class Y : public virtual X { }; class Z : public virtual X { }; class A : ...
  • wly_2014
  • wly_2014
  • 2016年04月08日 09:50
  • 910

你知道数据大小吗?

google_ad_client = "pub-8800625213955058";/* 336x280, 创建于 07-11-21 */google_ad_slot = "0989131976";...
  • java169
  • java169
  • 2008年05月24日 01:12
  • 147

你所知道的设计模式有哪些?我来给你讲常用的

你所知道的设计模式有哪些 Java中一般认为有23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多...
  • LiuHai2014csd
  • LiuHai2014csd
  • 2017年03月05日 15:52
  • 2425

C#中New关键字的三种用法

三种用法如下:   在 C# 中,new 关键字可用作运算符、修饰符或约束。   1)new 运算符:用于创建对象和调用构造函数。这种大家都比较熟悉,没什么好说的了。   2)new...
  • sibaison
  • sibaison
  • 2017年03月26日 17:28
  • 126

C#继承之隐藏基类方法

C#继承之隐藏基类方法或其他信息 当我们在定义一个类并继承了其它类的时候,在派生类中是没有办法删除基类的任何成员,就像我们不能改变父母的基因一样,所能做的只能采用隐藏父类方法,也就像使基因变为隐...
  • giswhw66
  • giswhw66
  • 2017年03月22日 23:10
  • 721

NYOJ 874 签到

 签到 时间限制:1000 ms  |  内存限制:65535 KB 难度:0 描述 你在NYOJ签过到吗?你知道签到可以获得多少OJ币吗?下面就请你来算一下吧。 输入多...
  • zwj1452267376
  • zwj1452267376
  • 2015年10月23日 12:55
  • 346

NYOJ759 你知道这个规律吗

思路: 模拟除法 如1256/9 12%9=3 35%9=8 86%9=5 (吐槽:这次数组开小了是TL 真心无语) 你知道这个规律吗? 时间限制:1000 ms  |  内存...
  • u012349696
  • u012349696
  • 2014年01月09日 09:41
  • 523
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:你知道数据大小吗?--不要花太多的功夫来隐藏类的成员(三)
举报原因:
原因补充:

(最多只允许输入30个字)