您的位置:首页 > Web前端

《Effective Java》避免创建不必要的对象

2018-03-23 14:08 363 查看
经典问题:

这两行创建了几个对象?第一行创建了几个,第二行创建了几个?

第一行创建了一个对象,第二行也是创建了一个对象。引用一共有两个,一个在虚拟机栈的栈帧的局部变量表中,另一个是在全局字符串池中(StringTable,本质上是个哈希表,池的位置视JDK的版本而定,1.6版本在方法区,1.7版本在堆,1.8从堆里面独立,被称为元空间),这个池中只保留被双引号修饰的字符串引用,实际对象还是存储在堆(heap)中,这个池子并不叫常量池。还有一种池子叫做运行时常量池,每个类在运行的时候都有自己独立的池,用来存储每个类的信息。

有人可能会这样说:当创建一个string对象的时候,去字符串常量池看一下是否有这个字符串,如果有就返回,没有就创建一个。

这样的说法是否存在问题呢?

是有问题的,intern()方法在1.7版本之前确实是这样,1.7之后,只是会把首次遇到的字符串实例的引用添加到全局字符串池中(没有复制),并返回此引用。intern()这个方法如果使用不当可能还导致YGC时间变长,原因是StringTable并不会存储这个字符串的引用,实际上对象还是在堆里面,也就是说每一次YGC的时候JVM都会扫描这个StringTable,StringTable过大就会影响YGC的时间,可以使用jmap -histo:live 加pid来强制进行一个full gc,full gc是可以对StringTable进行清理的。

Integer在内部也是有维护缓存池的,由一个名字叫做IntegerCache的缓冲池维护,范围为-128到127,如果使用了这个范围的数字包装类,多次使用同一个数字,会返回相同的对象。除double和float外,其他基本数据类型对应的包装类都有类似机制。



这种情况是不一样的,当对Long类型的数字进行计算时,由于Long是包装类,无法进行基本数据类型的计算,需要先转化为基本数据类型,这个过程叫拆箱,自动拆箱和自动装箱是JDK1.5版本提供的,在提供简便的同时也有这性能的损耗。

总结:在写代码过程中要避免创建不必要的对象,String类型的字符串尽量不要去new,如果有连结字符串的需求的话,可以考虑StringBuilder和线程安全的StringBuffer,二者底层和String一样,都是char数组,StringBuffer在方法上加上了synchornized,所以StringBuilder的性能要好一些,在后续版本,加号会被编译器优化为StringBuilder的append方式,但是由于规范和jdk版本问题,建议连结字符串使用append()方法。Integer下的缓冲池在某些情况是可以被破坏的,如果通过反射获取了缓冲池对象,修改缓冲池中的内容,结合printf()方法,会出现错误。包装类虽然有一点性能损耗,但是如果不是大量计算的话,不是很明显,况且包装的好处就是比如Integer的默认值是null,相比基本数据类型如int的默认是0,这样就很容易判断出有没有获取到值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息