论坛首页 Java企业应用论坛

【JDK优化】 Integer 自动打包机制的优化

浏览 13823 次
该帖已经被评为良好帖
作者 正文
   发表时间:2010-03-12   最后修改:2010-06-18

我们首先来看一段代码:

Integer i=100;
Integer j=100;
System.out.println(i==j);  //true
Integer i=200;
Integer j=200;
System.out.println(i==j);  //false

 

差不多的两段代码,怎么结果完全不同呢。我们分两步来说明这个问题:

 

首先 Integer i=100; 编译器会自动将int类型常数100包装成Interger,采用的是Integer.valueOf(100); 这个方法。

 

然后我们看看valueOf(int)这个方法的源代码:

 /*
  * 返回一个表示指定的 int 值的 Integer 实例。如果不需要新的 Integer 实例,则
  * 通常应优先使用该方法,而不是构造方法 Integer(int),因为该方法有可能通过
  * 缓存经常请求的值而显著提高空间和时间性能。 
  * @param  i an <code>int</code> value.
  * @return a <tt>Integer</tt> instance representing <tt>i</tt>.
  * @since  1.5
  */
public static Integer valueOf(int i) {
      final int offset = 128;
      if (i >= -128 && i <= 127) { // must cache 
	    return IntegerCache.cache[i + offset];
     }
     return new Integer(i);
}

 /*
  * IntegerCache内部类
  * 其中cache[]数组用于存放从-128到127一共256个整数
  */
private static class IntegerCache {
	private IntegerCache(){}

	static final Integer cache[] = new Integer[-(-128) + 127 + 1];

	static {
	    for(int i = 0; i < cache.length; i++)
		cache[i] = new Integer(i - 128);
	}
}

 

原来如此,当-128=<i<=127的时候,返回的是IntegerCache中的数组的值;当 i>127 或 i<-128 时,返回的是Integer类对象。 这就好解释:

 

Integer i=100;
Integer j=100;
System.out.println(i==j);  //(1)

此时的 i=IntegerCache.cache[228],因此 Integer引用i中存储的是cache数组第228号元素的地址。同理j也是同一个cache数组的第228号元素的地址(因为cache是Integer的静态数组,只有一个)。i==j比较的是引用地址,因此相等。

Integer i=200;
Integer j=200;
System.out.println(i==j);  //(2)

此时的 i=new Integer(200);  同样j=new Integer(200) 。两次都在堆中开辟了Integer的对象。i 和 j 中存储的堆得对象地址是完全不同的。i==j 自然不相等了。

 

那么这样做有什么意义呢? 我们来看看API的解释:

返回一个表示指定的 int 值的 Integer 实例。如果不需要新的 Integer 实例,
则通常应优先使用该方法,而不是构造方法 Integer(int),因为该方法有可能
通过缓存经常请求的值而显著提高空间和时间性能。 

假如我们在编程时大量需要值为100的Integer对象,如果只能通过new来创建的话。是不是需要在堆中开辟大量值一样的Integer对象呢!这是相当不划算的。既然如此,Java中的字符串常量池的应用是不是可以提醒我们点什么呢?是的,IntegerCache.cache就相当于这样一个字符串常量池。 当我们需要Integer i=100的时候,直接从cache中取出第[100+128]号元素的地址赋值引用i,再次需要Integer j=100时,还是直接去这个地址赋给j ..... 是不是省去了再堆中不停的创建对象的代价了(空间,时间上的消耗都很大)。

 

 

 

 

 

   发表时间:2010-03-13  
这个东西为了性能考虑,但是这种性能提升也有问题~
0 请登录后投票
   发表时间:2010-03-13  
因为实际上我们的编码会比较  i.equals(j),而非i==j,这种性能提升未尝不可?
0 请登录后投票
   发表时间:2010-03-13  
mercyblitz 写道
这个东西为了性能考虑,但是这种性能提升也有问题~



不是很理解,能不能详细一点。为什么这种性能提升是有问题的??
0 请登录后投票
   发表时间:2010-03-13  
Heart.X.Raid 写道
mercyblitz 写道
这个东西为了性能考虑,但是这种性能提升也有问题~



不是很理解,能不能详细一点。为什么这种性能提升是有问题的??



为了性能提升,在JVM实施优化的话,那么应该保存多少个这样实例呢,1万还是?
目前SUN JDK的实现是256个这样的缓存,这是一种折中的方法,实际上,很多实体ID很有可能超过127,并且重复出现。

3 请登录后投票
   发表时间:2010-03-13  
mercyblitz 写道


为了性能提升,在JVM实施优化的话,那么应该保存多少个这样实例呢,1万还是?
目前SUN JDK的实现是256个这样的缓存,这是一种折中的方法,实际上,很多实体ID很有可能超过127,并且重复出现。



说的对,我也对256的缓存的设置有异议。设置256个有什么依据呢?这个问题确实有待Java的设计者来解释。但我觉得,如果需要创建的整数对象较少的时候。最好不要用valueOf这种方法,毕竟要创建256个Integer对象的缓存空间。但如果数据量大,而且重复较多的情况下,还是可以考虑的,毕竟优化一点是一点吗? 至于如何选择要根据程序员自己的经验了。
1 请登录后投票
   发表时间:2010-03-13  
除了考虑性能外还要考虑空间啊,建议看下Integer类的源码你就知道了
0 请登录后投票
   发表时间:2010-03-13  
对于==和Object的equals
对于对象而言 一个是引用比较,一个是对象(逻辑)比较
反正比较对象我都是使用:equals不会用==的
0 请登录后投票
   发表时间:2010-03-13  
不错。不过,比较对象还是推荐用equals
0 请登录后投票
   发表时间:2010-03-13  
Integer i=100;  
Integer j=100;  
System.out.println(i.intValue()==j.intValue() );  //(1)  
 
Integer i=200;  
Integer j=200;  
System.out.println(i.intValue() ==j.intValue() );  //(2)

如果比较值是否相等请用:Integer的intValue()
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics