Java性能优化要点

  • JIT / Inlining内联
  • GC垃圾回收
  • False-sharing伪共享
  •  Unsafe
  •  Queues队列

 From:http://www.jdon.com/performance/java-performance-optimizations.html

JITJava即时编译器

当Java执行runtime环境时,每遇到一个新的类,JIT编译器在此时就会针对这个类别进行编译(compile)被优化成相当精简的原生型指令码(native code),会做一下工作:

  • 展开循环loop-unrolling
  • 重新安排代码
  • 移除同步synchronized
  • 优化锁
  • 内联热点方法

首先,JIT会展开我们代码中的循环语句,所以,我们编码时尽量注意不要在关键热点部分编写让JIT难于展开的循环语句。


JIT比较难以展开的循环语句如下:

int i = 0;
for (;;) {
  if (array.length == i) {
    break;
  }
  doSomething(array[i++]);
}

这种for循环虽然编写方便,但是JIT不喜欢,下面循环则易于JIT展开:

int i = 0;
for (int i = 0; i < array.length; i++) {
  doSomething(array[i]);
}

其次,JIT会内联一些热点小方法代码,这些小方法缺省差不多是325字节。比如下面是普通代码:

public void methodA() {
  ... // Do some work A
  methodB();
}
 
private void methodB() {
  ... // Do some more work B
}

JIT会将methodB内联合并到methodA中

//采取methodB内联到到methodA
public void methodA() {
  ... // Do some work A
  ... // Do some more work B
}

可以通过下面的Java运行配置记录检测内联:

java 
-XX:+PrintCompilation 
-XX:+UnlockDiagnosticVMOptions 
-XX:+PrintInlining 
.... > inline.log

PrintCompilation:当JIT编译发生输出打印
UnlockDiagnosticVMOptions:这是标识 -XX:+PrintInlining需要的
-XX:+PrintInlining :当方法被内联后打印出来

内联日志inline.log效果如下:

@ 42 io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe::read (191 bytes) inline (hot) (这表示方法hot被内联了)
@ 42 io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe::read (327 bytes) hot method too big (但是方法hot用于内联太大了)
@ 4 io.netty.channel.socket.nio.NioSocketChannel::config (5 bytes) inline (hot)
@ 1 io.netty.channel.socket.nio.NioSocketChannel::config (5 bytes) inline (hot)
@ 12 io.netty.channel.AbstractChannel::pipeline (5 bytes) inline (hot)

我们编码时对于热点方法不要编写对内联太大的方法,如下面read方法:

private final class NioMessageUnsafe extends AbstractNioUnsafe {
  public void read() {
    final SelectionKey key = selectionKey();
    if (!config().isAutoRead()) {
      int interestOps = key.interestOps();
      if ((interestOps & readInterestOp) != 0) {
        // only remove readInterestOp if needed
        key.interestOps(interestOps & ~readInterestOp);
      }
    }
    ... // rest of the method
  }
  ...
}

分解出read() 方法一部分代码到新的方法中:

private final class NioMessageUnsafe extends AbstractNioUnsafe {
  public void read() {
    if (!config().isAutoRead()) {
      removeReadOp();
    }
 
  private void removeReadOp() {
    SelectionKey key = selectionKey();
    int interestOps = key.interestOps();
    if ((interestOps & readInterestOp) != 0) {
      // only remove readInterestOp if needed
      key.interestOps(interestOps & ~readInterestOp);
    }
  }
 
    ... // rest of the method
  }
  ...

注意到read方法从原来多行已经变成了简单几行,这时我们再看看JIT的内联日志:

@ 42 io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe::read (288 bytes) inline (hot)

只有一行输出,说明read方法已经小到适合内联了。

你可能感兴趣的:(java,优化,编码,性能优化)