java特种兵读书笔记(1-1)——开篇

编译器优化


java编译器在对String a = “a”+“b”这种操作的时候,会直接在编译器优化为String a=“ab”;而不会等到运行时才做。同理,int i=1+2*3;这种操作也是在编译时就算好了结果是7.

编译器会把能做的工作提前做好,提高效率和速度。

对于String a = "a"; String c = "b"+a;这种的,编译器不会做优化,因为a的引用可能修改。

但是对于finial String a = "a"; String c = "b"+a;这种的,编译器会做优化,因为a是final的,它不可变了,所以编译器认为它的结果不变,所以会做优化。

编译器优化的底层操作是这样的:

StringBuilder temp = new StringBuilder();

temp.append("b").append(a);

String c = temp.toString();

String常量池位置


JDK1.6中,存放在Perm Gen中,JDK1.7中会存放在堆中。永久带也会注销,在FULLGC的时候操作。

JVM要点


让这个线程内所使用的内存尽快结束,以便让JVM认为它是垃圾,在YOUNG空间就尽量释放掉,尽量不要让它进入OLD区域。

要做到这一点,首先代码跑的足够快,其次分配的空间要足够的小。

String比较


对于String这种对象引用,==比较的是字符串引用对象的地址。

String对equals的实现,是比较字符串内容是否相同。

可以intern()将字符串放入到常量池中,然后用==比较来代替equals。

但是因为intern在去常量池中寻找的过程中(期间可能还有加锁)要对比不止一个字符串,也需要调用equals来进行对比。所以intern不是说对比地址比equals慢,而是输在了对比地址之前先要找到地址上。

String和StringBuilder


String str = a+b+c;

一句+操作(不管加了几次,都是在一行中加和的)java编译的时候会申请一个StringBuilder,如果多个+操作在多条语句中进行的,比如

String str1 = a+b;String str2 = c+d;

这样就会申请多个。

扩容的时候,会按照max(count+newlength, count*2)

效率运算


最低级的系统运算,比如与,或,异或等等操作。

交换两个数字的算法:用异或,a=a^b,b=a^b,a=a^b。

将数字分散到不同的堆中(类似hash),可以用a&4999将待选取的数据均匀分布在5000个堆中(这样的问题是,会有几个下标永远没有数据,因为做完&操作后都会使0)。

Bits类


看看这个类的源码。

Integer


Integer由四个字节组成,最大0x7fffffff,十进制是2147483647,2的31次方-1,即2G-1(一个f是4位,这里7个f,最左面的7是3位,最高位是标志位,表示正负)。

2的10次方是1K,2的20次方是1M,2的30次方是1G,2的31次方就是2G。

最小是0x80000000,十进制是-2147483648,最高位是1代表是负数,这是绝对值最大的负数。

0xffffffff,十进制是-1,是绝对值最小的负数。

原生类型(Primitive)


boolean,byte,short,char,int,float,long,double。

包装之后的类会按照对象的规则存储在堆中,而线程栈中只存储引用的地址。

对象会占据更大的空间,对于原生态类型,栈上直接保存了它们的值,而不是引用。

IntegerCache的设计


Integer有一个缓存,如果是-128~127之间的数字,都会从这个缓存数组中取值,所以如果有

Integer a = 1;Integer b = 1;

那么用a==b做判断,返回是true,因为都同一个对象的引用,都是Integer缓存中的1。

Integer a = 200;Integer b = 200;

此时用a==b做判断,返回是false。

默认缓存的数字是-128~127的,可以通过-Djava.lang.Integer.IntegerCache.high=200;来修改,这样第二组判断的结果就会返回true了。注意:可以修改缓存大小是JDK1.7引入的。

这样做的目的是:更加节约空间,对于-128~127之间的数字,不用对每个数字都new一个对象了。

其他对象也有cache,比如Boolean,Double,Byte等等,理念类似。

装箱拆箱


HashMap和List这种结合在填充int类型数据的时候,会发生装箱。假设有如下操作:

List<Integer> list1 = Lists.newArrayList();list1.add(1);//这时候会装箱

int temp = list1.get(0);//这时候拆箱

List<Integer> list2 = Lists.newArrayList();list2.add(temp);//这时候又装箱

将一个list中的一个数据放到另一个list中就发生了三字装箱/拆箱操作,会浪费大量的空间

语法糖


JDK1.7支持对String的swith case操作,但是这是语法糖,编译后的字节码还是if else的嵌套实现。

集合类与OOM


集合类都是存储对象的引用,32位和64位的压缩模式,一个对象占用4个字节,64位非压缩模式一个对象占用8个字节。而相比之下,StringBuilder存储的是char数组,每个位只占两个字节。

问题一般都发生在扩容时候,空间不够,会创建一份新的空间,然后拷贝数据,会产生内存碎片,即多次扩容->多次拷贝->越来越多的内存碎片。

经常修改List的话,尝试LinkedList,减少移动数据带来的开销。

如果事先知道长度,用new ArrayList(int size)或者guava的带大小的Lists构造函数,可以降低内存碎片和内存拷贝次数。

List太大的话尝试分段,不要一次性的加载的内存当中,防止OOM。

注意HashMap的0.75因子,会导致扩容,浪费空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值