0%

Java性能调优

调优

TODO

jhat
vmstat

测试样例

新docker container,全新的系统,以纯净的系统Centos为例

安装java

1
2
3
4
5
6
# 安装java8
[root@centos ~]# yum install -y java-1.8.0-openjdk*
# 创建deploy用户,用于折腾
[root@centos ~]# useradd deploy
[root@centos ~]# su deploy
[root@centos ~]# cd

创建工作目录

1
2
3
# 创建并切换到工作目录
[deploy@centos ~]$ mkdir -p lk-optimization
[deploy@centos ~]$ cd !$

创建package,用于存放java源文件和编译后的class文件

1
2
[deploy@centos ~]$ mkdir -p src/com/lk/optimization/demo target
[deploy@centos ~]$ vim !$/DemoTest.java

新建Java文件

// Thread thread = Thread.currentThread();
// System.out.println(thread.getName());
// thread.join();
System.out.println(“join over”);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.lk.optimization.demo;
import java.util.List;
import java.util.ArrayList;
public class DemoTest {
public static void main(String[] atgs) throws InterruptedException {
new Handler().doHandle();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.out.println(this.getName() + " is shutting down ....");
}
});
}
}

class Handler {
public void doHandle() {
for (int i = 0; i < 10; i++) {
String workerName="worker-"+(i+1);
new Thread(new Worker(workerName), "handler-" + i).start();
}
}
}


class Worker implements Runnable {
private String workerName;
private List<String> list;
public Worker(String workerName){
this.workerName=workerName;
this.list=new ArrayList<>(1000);
}
@Override
public void run() {
System.out.println(workerName+" start!");
while (true) {
for(int i=0;i<100;i++){
list.add(""+i);
}
if(list.size()>1000){
list.clear();
}
try {
Thread.sleep(100);
System.out.println(workerName+": sleep over");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[deploy@centos ~]$ find src -name "*.java" | xargs javac -d target

[deploy@centos ~]$ tree
.
├── src
│ └── com
│ └── lk
│ └── optimization
│ └── demo
│ └── DemoTest.java
└── target
└── com
└── lk
└── optimization
└── demo
├── DemoTest$1.class
├── DemoTest.class
├── Handler.class
└── Worker.class

10 directories, 5 files

运行

1
2
[deploy@centos ~]$ nohup java -cp target  -Xcomp -XX:MaxNewSize=1000000  -XX:MaxHeapSize=2000000 com.lk.optimization.demo.DemoTest &
main

ps 查看进程和线程信息

ps命令选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
********* simple selection *********  ********* selection by list *********
-A all processes -C by command name
-N negate selection -G by real group ID (supports names)
-a all w/ tty except session leaders -U by real user ID (supports names)
-d all except session leaders -g by session OR by effective group name
-e all processes -p by process ID
T all processes on this terminal -s processes in the sessions given
a all w/ tty, including other users -t by tty
g OBSOLETE -- DO NOT USE -u by effective user ID (supports names)
r only running processes U processes for specified users
x processes w/o controlling ttys t by tty
*********** output format ********** *********** long options ***********
-o,o user-defined -f full --Group --User --pid --cols --ppid
-j,j job control s signal --group --user --sid --rows --info
-O,O preloaded -o v virtual memory --cumulative --format --deselect
-l,l long u user-oriented --sort --tty --forest --version
-F extra full X registers --heading --no-heading --context
********* misc options *********
-V,V show version L list format codes f ASCII art forest
-m,m,-L,-T,H threads S children in sum -y change -l format
-M,Z security data c true command name -c scheduling class
-w,w wide output n numeric WCHAN,UID -H process hierarchy

root用户查看进程信息

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@centos ~]# ps -ef  | grep -v ps
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 13:16 ? 00:00:00 /usr/sbin/sshd -D
root 14 0 0 13:23 pts/0 00:00:00 /bin/bash
root 421 1 0 13:58 ? 00:00:00 sshd: root@pts/1
root 423 421 0 13:58 pts/1 00:00:00 -bash
root 461 1 0 14:15 ? 00:00:00 sshd: root@pts/2
root 463 461 0 14:15 pts/2 00:00:00 -bash
root 519 1 0 14:16 ? 00:00:00 sshd: root@pts/3
root 521 519 0 14:16 pts/3 00:00:00 -bash
root 785 423 0 14:59 pts/1 00:00:00 su deploy
deploy 786 785 0 14:59 pts/1 00:00:00 bash
deploy 1129 786 0 15:32 pts/1 00:00:02 java -cp target com.lk.optimization.demo.DemoTest

ps 命令

  • -e 显示所有进程信息
  • -f 显示所有的字段
  • -l long format
  • -L 显示NLWP (number of threads) and LWP (thread ID) 显示线程信息
  • -p 只显示指定的PID

字段的解释可以在man页的STANDARD FORMAT SPECIFIERS中查找

默认情况下,ps显示与当前用户相同EUID以及调用了同一个终端的进程。

  • PID是进程ID
  • TTY为与进程关联的终端名称
  • TIME为以[dd-]hh:mm:ss格式显示的CPU时间
  • CMD为执行的名称, 默认不排序
  • PPID为父进程ID,根据ID可以找到进程的调用关系

样例中的进程信息:

进程id 解释
0 0号进程是根进程
1 通过sshd发起的ssh连接
421 ssh连接,用户为root,终端为pts/1,子进程全部这个终端
423 bash shell
785 切换deploy账户
786 deploy账户的bash shell
1129 java启动进程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@centos ~]# ps -flL -p 1129
F S UID PID PPID LWP C NLWP PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
0 S deploy 1129 786 1129 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
5 S deploy 1129 786 1130 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
1 S deploy 1129 786 1131 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
1 S deploy 1129 786 1132 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
1 S deploy 1129 786 1133 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
1 S deploy 1129 786 1134 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
1 S deploy 1129 786 1135 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
1 S deploy 1129 786 1136 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
1 S deploy 1129 786 1137 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
1 S deploy 1129 786 1138 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
1 S deploy 1129 786 1139 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest
1 S deploy 1129 786 1140 0 27 80 0 - 934507 - 15:32 pts/1 00:00:00 java -cp target com.lk.optimization.demo.DemoTest

Process Format

  1. F PROCESS FLAGS 1 forked但没有执行 5超级用户权限
  2. S PROCESS STATE CODES
    D Uninterruptible sleep (usually IO)
    R Running or runnable (on run queue)
    S Interruptible sleep (waiting for an event to complete)
    T Stopped, either by a job control signal or because it is being traced.
    W paging (not valid since the 2.6.xx kernel)
    X dead (should never be seen)
    Z Defunct (“zombie”) process, terminated but not reaped by its parent.
  3. lwp (light weight process, or thread) ID 线程ID
  4. C processor utilization. Currently, this is the integer value of the percent usage over the lifetime of the process.
  5. NLWP 轻量级进程的个数
  6. ni NI nice value. This ranges from 19 (nicest) to -20 (not nice to others)
  7. size SZ approximate amount of swap space that would be required if the process were to dirty all writable pages and then be swapped out. This number is very rough!
  8. wchan WCHAN name of the kernel function in which the process is sleeping, a “-“ if the process is running, or a “*” if the process is multi-threaded and ps is not displaying threads.

线程信息

线程线程的选项:

  • H Show threads as if they were processes 显示正在处理的线程
  • -L Show threads, possibly with LWP and NLWP columns 显示线程
  • -T Show threads, possibly with SPID column 显示线程
  • m Show threads after processes
  • -m Show threads after processes

通过上面的例子,可以看出

线程id是1129~1154, 共27个线程
实际handler线程和main线程加起来是11个,为什么是27个线程呢

top 查看资源

以线程模式查看下进程31951的所有线程情况

1
top -Hp 31951

top-thread

1
2
3
4
5
6
-H : Threads toggle
Starts top with the last remembered ’H’ state reversed. When this toggle is On, all individual threads will be displayed. Otherwise, top displays a summation of all threads in a process. 当此开关打开时,将显示所有单个线程。否则,top显示进程中所有线程的总和。

-p : Monitor PIDs as: -pN1 -pN2 ... or -pN1, N2 [,...]
Monitor only processes with specified process IDs. This option can be given up to 20 times, or you can provide a comma delimited list with up to 20 pids. Co-mingling both approaches is permitted. This is a command-line option only. And should you wish to return to normal operation, it is not necessary to quit and and restart top -- just issue the ’=’ interactive command.
打开top后,= 可以切换回全部进程

jstack Java线程栈信息

jstack中的线程id为16进制,需要将从top或ps获取的线程id转换为16进制,

1
printf %x <tid>

注意:

  1. jstack必须和运行的JVM进程是同一个用户
1
2
3
4
5
Options:
-F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message

虚拟机信息flag

打印虚拟机所有的参数

java 命令文档

1
2
3
4
5
6
7
8
9
java -XX:+PrintFlagsFinal -version | grep :
java -XX:+PrintFlagsInitial -version


java -XX:+PrintFlagsFinal -version | grep -v :=


The -XX:+PrintFlagsFinal (emphasis on "Final") option displays what options HotSpot ended up using for running Java code while
-XX:+PrintFlagsInitial (emphasis on "Initial") displays what options were provided to HotSpot initially, before HotSpot has made its own tweaks.

jinfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
jinfo -flags $PID

Attaching to process ID 29893, please wait...
Debugger attached successfully.
Server compiler detected. # 服务器版本的编译器
JVM version is 25.232-b09 # jvm版本
Non-default VM flags:
-XX:CICompilerCount=2
-XX:InitialHeapSize=33554432 # 初始堆大小 33M
-XX:MaxHeapSize=524288000 # 最大堆大小 524M
-XX:MaxNewSize=174718976 #年轻代最大大小 174M
-XX:MinHeapDeltaBytes=196608 #堆自动增加步长最小196K
-XX:NewSize=11141120 #年轻代大小11M
-XX:OldSize=22413312 #老年代大小22M
-XX:+UseCompressedClassPointers #压缩类指针
-XX:+UseCompressedOops #压缩
-XX:+UseParallelGC # 使用并行回收器

Command line:

jstat查看jvm统计信息和垃圾回收信息

JVM statistics

jstat docs

jstat 命令使用样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
➜ jstat -options
-class # classLoader行为的统计信息
-compiler # Java HotSpot VM Just-in-Time compiler行为的统计信息
-gc # 垃圾回收堆的统计信息
-gccapacity # 每代的大小和容量
-gccause # 垃圾收集统计及回收原因
-gcmetacapacity
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcutil # gc统计信息汇总,展示gc次数、耗时和各个分区的大小
-printcompilation


class: Displays statistics about the behavior of the class loader.
compiler: Displays statistics about the behavior of the Java HotSpot VM Just-in-Time compiler.
gc: Displays statistics about the behavior of the garbage collected heap.
gccapacity: Displays statistics about the capacities of the generations and their corresponding spaces.
gccause: Displays a summary about garbage collection statistics (same as -gcutil), with the cause of the last and current (when applicable) garbage collection events.
gcnew: Displays statistics of the behavior of the new generation.
gcnewcapacity: Displays statistics about the sizes of the new generations and its corresponding spaces.
gcold: Displays statistics about the behavior of the old generation and metaspace statistics.
gcoldcapacity: Displays statistics about the sizes of the old generation.
gcmetacapacity: Displays statistics about the sizes of the metaspace.
gcutil: Displays a summary about garbage collection statistics.
printcompilation: Displays Java HotSpot VM compilation method statistics.

GC堆统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
jstat -gc 12196
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
512.0 1024.0 496.0 0.0 67584.0 38337.9 437248.0 75268.8 83992.0 80411.0 9496.0 8854.4 388 5.643 3 0.390 6.033



-gc option
Garbage-collected heap statistics.

S0C: Current survivor space 0 capacity (kB). #s0区当前容量
S1C: Current survivor space 1 capacity (kB). #s1区当前容量
S0U: Survivor space 0 utilization (kB). #s0区使用量
S1U: Survivor space 1 utilization (kB). #s1区使用量
EC: Current eden space capacity (kB). #eden区容量
EU: Eden space utilization (kB). #eden区使用量
OC: Current old space capacity (kB). #old区容量
OU: Old space utilization (kB). #old区使用量
MC: Metaspace capacity (kB). #metaspace容量
MU: Metacspace utilization (kB). #metaspace使用量
CCSC: Compressed class space capacity (kB). #压缩类空间容量
CCSU: Compressed class space used (kB). #压缩类空间使用量
YGC: Number of young generation garbage collection events. # YGC次数
YGCT: Young generation garbage collection time. #YGC耗时
FGC: Number of full GC events. # Full GC次数
FGCT: Full garbage collection time. # Full GC耗时
GCT: Total garbage collection time. # GC总耗时

GC统计

1
2
3
jstat -gcutil 12196
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
96.88 0.00 89.96 17.21 95.74 93.24 388 5.643 3 0.390 6.033
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-gcutil option
Summary of garbage collection statistics.

S0: Survivor space 0 utilization as a percentage of the space's current capacity. # s0区使用率
S1: Survivor space 1 utilization as a percentage of the space's current capacity. # S1区使用率
E: Eden space utilization as a percentage of the space's current capacity. # eden区使用率
O: Old space utilization as a percentage of the space's current capacity. # old区使用率
M: Metaspace utilization as a percentage of the space's current capacity. # Metaspace使用率
CCS: Compressed class space utilization as a percentage. # 压缩类空间使用率
YGC: Number of young generation GC events. # YGC次数
YGCT: Young generation garbage collection time. # YGC耗时
FGC: Number of full GC events. # Full GC次数
FGCT: Full garbage collection time. # Full GC耗时
GCT: Total garbage collection time. # GC总耗时

堆dump

full gc前后dump

1
java -XX:HeapDumpPath=./heap/ -XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpAfterFullGC

HeapDumpPath: dump path
HeapDumpOnOutOfMemoryError 内存溢出dump
HeapDumpAfterFullGCHeapDumpBeforeFullGC :Full GC前后dump

jmap

Prints shared object memory maps or heap memory details for a process, core file, or remote debug server. This command is experimental and unsupported.
打印进程、核心文件或远程调试服务器的共享对象内存映射或堆内存详细信息

jmap docs

jmap -heap <pid>

堆的各个分区大小

堆内存分析-GC日志解读

展示堆满的情况下的各个分区大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
> jmap -heap 16186
Attaching to process ID 16186, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.242-b08

using thread-local object allocation.
Parallel GC with 6 thread(s)

Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 522190848 (498.0MB)
NewSize = 11010048 (10.5MB)
MaxNewSize = 174063616 (166.0MB)
OldSize = 22544384 (21.5MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
capacity = 58720256 (56.0MB)
used = 49193976 (46.91503143310547MB)
free = 9526280 (9.084968566894531MB)
83.7768418448312% used
From Space:
capacity = 57147392 (54.5MB)
used = 0 (0.0MB)
free = 57147392 (54.5MB)
0.0% used
To Space:
capacity = 57671680 (55.0MB)
used = 0 (0.0MB)
free = 57671680 (55.0MB)
0.0% used
PS Old Generation
capacity = 348127232 (332.0MB)
used = 347644232 (331.5393753051758MB)
free = 483000 (0.46062469482421875MB)
99.8612576220409% used

10381 interned Strings occupying 935984 bytes.

远程监控

JVM开启远程JMX

1
2
3
4
5
6
7
8
9
# 开启远程调试端口
-Dcom.sun.management.jmxremote.port=8777
# 开启本地调试端口
-Dcom.sun.management.jmxremote.rmi.port=8777
# jmx远程服务默认是开启ssl和认证功能功能的,也可以通过jvm选项把这两个功能关闭
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
# jmx默认是通过localhost的ip地址提供RMI服务的,如果要明确指定RMI服务地址或主机名(比如主机有多个接口,想使用非hostname关联的接口)
-Djava.rmi.server.hostname=10.242.93.40

例子:

1
2
3
4
5
6
7
8
java \
-Djava.rmi.server.hostname=0.0.0.0 \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.rmi.port=8000 \
-Dcom.sun.management.jmxremote.port=8001 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-jar demo-0.0.1-SNAPSHOT.jar

Tomcat 开启远程

修改catalina.sh,

1
2
3
4
5
6
JAVA_OPTS="$JAVA_OPTS -Djava.rmi.server.hostname=0.0.0.0 
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.rmi.port=8000
-Dcom.sun.management.jmxremote.port=8001
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false "

jvisualvm

插件中心

  • 新建远程主机0.0.0.0
  • 新建JMX链接: 端口是8001

中文文档

开启jstatd连接

BTrace

官网

JDWP与tomcat远程调试

Tomcat 监控

JDWP

Tomcat-manager

psi-probe监控

Nginx

ngx_http_stub_status监控连接信息

ngxtop监控请求信息

字节码

常量池官方文档

深入解析String#intern

深入解析String.intern
原文出处: 深入解析String.intern

字符串的常量池

常量池在哪里?

  • Jdk1.6及之前: 有永久代, 常量池在方法区
  • Jdk1.7: 有永久代,但已经逐步“去永久代”,常量池在堆
  • Jdk1.8及之后: 无永久代,常量池在元空间

String去重

String Deduplication in G1

-XX:+UseStringDeduplication 开启String去重
-XX:+PrintStringDeduplicationStatistics 打印详细的去重统计
-XX:StringDeduplicationAgeThreshold=15 达到这个年龄的String对象才会去重

重用代码优化方法

  • 尽量重用对象,不要循环创建对象,比如:for循环字符串拼接
  • 容器类初始化的时候指定长度
  • ArrayList随机遍历快,LinkedList添加删除快
  • 集合遍历尽量减少重复计算
    1
    for(int i=0;i<collection.size();i++) // collection.size() 提取成常量
  • 使用Entry遍历Map
    1
    2
    3
    4
    for(Map.Entry<String,String> entry:map.entrySet()){
    String key=entry.getKey();
    String value=entry.getValue();
    }
  • 大数组复制用System.arrayCopy()
  • 尽量使用基本类型而不是包装类型
    1
    2
    Integer i=100;
    System.out.println(i);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Code:
    stack=2, locals=2, args_size=1
    0: bipush 100 // 将100压入栈
    2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    5: astore_1
    6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
    9: aload_1
    10: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
    13: return
    1
    2
    3
    Integer i1=100;
    Integer i2=100;
    System.out.println(i1==i2); // true
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Code:
    stack=3, locals=3, args_size=1
    0: bipush 100
    2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    5: astore_1
    6: bipush 100
    8: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    11: astore_2
    12: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
    15: aload_1
    16: aload_2
    17: if_acmpne 24
    20: iconst_1
    21: goto 25
    24: iconst_0
    25: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V
    28: return
    1
    2
    3
    Integer i1=1000;
    Integer i2=1000;
    System.out.println(i1==i2); // false
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Code:
    stack=3, locals=3, args_size=1
    0: sipush 1000
    3: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    6: astore_1
    7: sipush 1000
    10: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    13: astore_2
    14: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
    17: aload_1
    18: aload_2
    19: if_acmpne 26
    22: iconst_1
    23: goto 27
    26: iconst_0
    27: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V
    30: return
    Integer.valueOf 内部有缓存,如果大于-128且小于java.lang.Integer.IntegerCache.high(默认值是127),则返回缓存
  • 尽量使用非同步的容器,vector和ArrayList
  • 尽量减少同步作用范围,synchronized方法vs代码块
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public synchronized void f1() {
    System.out.println("f1");
    }

    public void f2() {
    synchronized (this) {
    System.out.println("f4");
    }

    }

    public static synchronized void f3() {
    System.out.println("f3");
    }

    public static void f4() {
    synchronized (TestPrimaryType.class) {
    System.out.println("f4");
    }
    }
  • 使用ThreadLocal缓存线程不安全对象,simpleDateFormat
  • 尽量使用延迟加载,懒惰单例模式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class TestPrimaryType{
    private TestPrimaryType() {
    }

    private static class TestPrimaryTypeHolder {
    private static TestPrimaryType instance = new TestPrimaryType();
    }
    /**
    ** 调用这个方法时,发生对TestPrimaryTypeHolder的引用,才创建instance对象
    */
    public static TestPrimaryType getInstance() {
    return TestPrimaryTypeHolder.instance;
    }
    }
  • 尽量减少使用反射,加缓存
  • 尽量使用连接池、线程池、对象池、缓存
  • 及时释放资源,I/O流、socket、数据库连接
  • 慎用异常,不要用抛异常来标示正常的业务逻辑
  • String操作尽量少用正则表达式
    1
    2
    replace VS replaceAll: 尽量用replace
    split
  • 日志输出注意使用不同的级别
  • 日志中参数拼接使用占位符
    1
    2
    log.info(“orderId:”+orgerId); // 不推荐
    log.info(“orderId:{}”,orgerId); //推荐