java深度-----effective java
高性能java1.开发高性能java程序的原则与方法 优化程序代码的注意事项: 1。除非必须优化,否则不要轻易改动 2。改动之后要进行仔细的测试 3。在各个JVM产品中是不存在一劳永逸的成本模型 (在某个虚拟机上做的优化效果非常好,但是到了别的虚拟机上就不一定重现了)2。先把焦点放在设计,数据结构和算法上每一个方法不应该长,可以方法里面引入方法,这样jit所做的工作就会少1。使用StringBuffer来做拼接因为如果用String,那么很多对象将在Eden区域产生,很可能在此时造成垃圾全收集的产生StringBuffer b = new StringBuffer("a"); //这里只产生一个对象,因为他会把a这个二进制的东西(注意这时还不是一个对象),加到b这个对象里面,所以只有一个对象。注意:"a"有两种身份,第一种身份是对象,第二种身份是和int这中东西一样的基础类型所以使用StringBuffer来操作字符串拼接,那么从始至终都只有一个对象哈,虽然在传值给append的时候传递了"a"这种东西,但是这个时候他不是一个对象2。javap -c Test 可以生成字节码文件通过字节码我们知道加入同步控制会导致字节码指令体积会增加同步方法获取的是对象锁Class Test{ public synchronized void m1(){ } public synchronized void m2(){ } public synchronized void m3(){ } public synchronized void m4(){ }} //注意啦,这四个方法居然用了同一把锁,就是这个对象锁(this),那么第一:四个方法只能由一个方法在一个时间内使用第二:如果方法里面有全局变量,对象所还没有用,因为别的线程很可能new一个自己的对象,还是可以进入这个方法来所以千万不要使用方法同步我们一般用字节数组作为锁变量class Test{ private byte[] lock1 = new byte[0]; //技巧哈 private byte[] lock2 = new byte[0]; public void m1(){ synchronized(lock1){ //====================== } }}尽量使用stack变量和基本类型完成必要的任务一个方法的局部变量会存放在这个方法的运行时栈当中stackAccess方法性能要高于staticAccess以及instacevarAccessstaticAccess和instacevarAccess方法的性能大体一致,因为他们都是从常量池中去得到实例变量所以尽可能的使用局部变量class StackVar{ private int instvar; private static int staticvar; void stackAccess(int val){ int j=0; for(int i=0;i<val;i++){ j +=1; //这是对局部变量++ } } void instacevarAccess(int val){ //每次都要访问常量池,性能有问题,第二并发有问题 for(int i=0;i<val;i++){ instvar += 1; //这是对实例变量++ } } void instacevarAccessPer(int val){ int j= instvar; for(int i=0;i<val;i++){ j+=1; } instvar = j; } void staticAccess(int val){ for(int i=0;i<val;i++){ staticvar += 1; //对静态变量++ } } void staticAccessPer(int val){ //性能不错,只需要访问两次常量池, //其他操作都是在本地栈中完成的 int j=staticvar; for(int i=0;i<val;i++){ j+=1; //对局部变量++然后赋值给静态变量 } staticvar = j; }}使用static final private 函数以促成inlining内联函数会使用函数的代码来替换函数的调用,将函数的代码原封不动的拷贝到调用处,省去了函数调用的消耗,以促使性能的提升,但是如果你将一个函数声明为static final private那么java编译器就会把这种函数作为inlining的候选者,为什么是候选者呢?因为如果你的函数写的非常大的话,那么他会使得你函数的体积发生激增,所以Java编译器会先看下代码的长度,代码太长他也不会把他变为内陆函数的实例变量初始化一次就好如果你new一个类,那么java虚拟机如何对他初始化1。在堆中分配内存2。对实例变量进行缺省值的初始化3。执行构造方法的赋值动作但是如果我们private int count=1;这么搞则有四步1。在堆中分配内存2。对实例变量进行缺省值的初始化3。覆盖缺省值4。执行构造方法的赋值动作或者有初始化区段,比如static{}那么1。在堆中分配内存2。对实例变量进行缺省值的初始化3。初始化区段---这个虽然是static的,但确是在实例变量缺省值初始化之后4。执行构造方法的赋值动作class Foo{ private int count; //private int count=1;这样也会被执行两次,先执行缺省值赋值, private boolean done; private Point pt; private Vector ver; public Foo(){ count = 0; //这个东西被初始化了两次哈 done = false; //他的缺省值本来就是false,他又初始化一次 pt = new Point(0,0); vec = new Vector(10); }}class Foo{ private int count; private boolean done; private Point pt; private Vector ver; public Foo(){ pt = new Point(0,0); vec = new Vector(10); }}集群类对象的处理1.数组,HashTable,Vector,Stack等数据结构类用于容纳和处理对象注意:Collection类里面放的都是对象哈。。简单类型也会自动转型才被放入1。权衡使用迭代器:Iterator,ListIterator,Enumerationclass AccessCollection{ public int enumCec(Vector vec){ //比较快,比后面的快10% Enumeration enum = ver.elements(); int toral = 0; while(enum.hasMoreElements()){ //在这里访问了一次容器,就比forVec慢了 total = ((Integer))(enum.nextElement()).iniValue(); } return total; } public int iterVec(Vector vec){ Iterator it = vec.iterator(); int total = 0; while(it.hasNext()){ total +=((Integer))(it.next()).iniValue(); } return total; } public int listiterVec(Vector vec){ ListIterator it = vec.listIterator(); int total = 0; while(it.hasNext()){ total +=((Integer))(it.next()).iniValue(); } return total; } public int forVec(Vector vec){//最快,比前三个方法快35% int size = vec.size(); int total = 0; for(int i=0;i<size;i++){ total +=((Integer)vec.get()).intValue(); } }}如何复制数组元素:使用循环复制;---不好使用System.arraycopy()方法---好,比循环复制快两倍,因为他采用操作系统底层的复制,而不是采用循环的方式System.arraycopy(src,0,des,0,src.length);从原数组第0个元素开始拷贝,拷贝的长度为src.length;把他拷贝到des时,以第0个位置作为起始元素优先数组,然后考虑群积类,尤其是在性能优先的需求中;如果需要群集类功能,但又不考虑同步,可以先选择非同步的集群类如ArrayList;如果需要集群类功能,又要顾及到数据同步保护时,可以选择同步集群类,如Vector;如果选择集群类,要分清集群类使用的目的注意:不要因为有个个数无法确定的数据需要存储,就毫无选择的使用集群类,出现这种情况可以考虑创建一个足以容纳大量数据的array,这可能造成内存浪费,但是在性能上往往会获得更大的收益