我怎么做 Java 应用性能优化

2016/10/12 技术

工欲善其事,必先利其器

做应用性能优化或者找出性能瓶颈如果没有好用的工具是无法想象的

这里汇总记录了工作中常用的性能优化工具,持续更新

Java 应用常见的性能优化包括了如下几大方面

  • JVM
  • Linux
  • 数据库
  • 中间件

所以下面会对这几类分开进行讲解,最后还列举了常见的一些错误供参考

JVM 篇

jps

jps 用于查看系统中的 Java 进程,作用等同于 ps -ef | grep java

jps -h

查看帮助信息

jmap

jmap 用于查看 JVM 堆内存的情况,包括了堆内存汇总、对象大小、类加载、永久代(方法区,常量池)等相关信息,jmap 还可以 dump 堆快照到一个二进制文件进行离线分析。

jmap -heap pid

打印进程堆内存的汇总信息,这是一个概要的输出,可以看到当前的 JVM 用的是哪个 GC 算法和一些简单的配置比例参数,还可以看到堆内存的分区和使用情况,一个典型的 JVM6 的输出是这样的

注意:这个命令可能会导致 Java 进程挂起

Attaching to process ID 193350, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.0-b12-internal

using parallel threads in the new generation.
using thread-local object allocation.
Concurrent Mark-Sweep GC

Heap Configuration:
   MinHeapFreeRatio = 40
   MaxHeapFreeRatio = 70
   MaxHeapSize      = 4294967296 (4096.0MB)
   NewSize          = 2147483648 (2048.0MB)
   MaxNewSize       = 2147483648 (2048.0MB)
   OldSize          = 5439488 (5.1875MB)
   NewRatio         = 2
   SurvivorRatio    = 10
   PermSize         = 100663296 (96.0MB)
   MaxPermSize      = 402653184 (384.0MB)

Heap Usage:
New Generation (Eden + 1 Survivor Space):
   capacity = 1968570368 (1877.375MB)
   used     = 1301671664 (1241.370834350586MB)
   free     = 666898704 (636.0041656494141MB)
   66.12268909251407% used
Eden Space:
   capacity = 1789657088 (1706.75MB)
   used     = 1219023832 (1162.5517196655273MB)
   free     = 570633256 (544.1982803344727MB)
   68.11493889940104% used
From Space:
   capacity = 178913280 (170.625MB)
   used     = 82647832 (78.8191146850586MB)
   free     = 96265448 (91.8058853149414MB)
   46.19435292897207% used
To Space:
   capacity = 178913280 (170.625MB)
   used     = 0 (0.0MB)
   free     = 178913280 (170.625MB)
   0.0% used
concurrent mark-sweep generation:
   capacity = 2147483648 (2048.0MB)
   used     = 1620385664 (1545.3201904296875MB)
   free     = 527097984 (502.6798095703125MB)
   75.45508742332458% used
Perm Generation:
   capacity = 399908864 (381.3828125MB)
   used     = 244258792 (232.9433364868164MB)
   free     = 155650072 (148.4394760131836MB)
   61.078614151448264% used

MinHeapFreeRatio 和 MaxHeapFreeRatio 这两个参数指出了堆的剩余比例,超过这个比例,内存会自动设定为 Xmx 和 Xms 的大小,如果一开始就将 Xmx 和 Xms 设置成一样,这 2 个参数是无效的。

MaxHeapSize MaxNewSize NewSize 见名知义,为年轻代和老年代的大小

OldSize 这个参数好像没什么用,指的是老年代默认大小,如果设置堆空间后,参数应该是无效的

NewRatio 这个参数指定了老年代与年轻代的比例,但是如果大小固定的情况下,参数是无效的

SurvivorRatio 这个参数指出 Eden 区是存活区的几倍,一般设置为10,意思是 1/10 的对象会存活下来

PermSize MaxPermSize 持久代的大小

jmap -histo pid

打印 Java 对象占用的堆内存详细列表,会按占用大小降序排列

num     #instances         #bytes  class name
----------------------------------------------
   1:       2057349      166008824  [C
   2:        464806      113522760  [B
   3:        171908       91584528  [I
   4:       2412542       77201344  java.lang.String
   5:       1979521       63344672  java.util.HashMap$Entry
   6:       1550946       62037840  java.util.concurrent.ConcurrentHashMap$Segment
   7:        868145       57148928  [Ljava.lang.Object;
   8:        365993       55979392  <constMethodKlass>
   9:       1636340       52362880  java.util.concurrent.locks.ReentrantLock$NonfairSync
  10:        365993       43930904  <methodKlass>
  11:       1551930       38532624  [Ljava.util.concurrent.ConcurrentHashMap$HashEntry;
  12:         30900       37707912  <constantPoolKlass>
  13:        328579       34937280  [Ljava.util.HashMap$Entry;
  14:         30900       24044144  <instanceKlassKlass>
  15:        385746       23722992  <symbolKlass>
  16:         22361       19328328  <constantPoolCacheKlass>
  17:        796899       19125576  java.lang.Long
  18:        637196       15292704  java.util.ArrayList
  19:        287337       13792176  java.util.HashMap
  20:        219462       12289872  net.sf.cglib.asm.Item
  21:        175085       11205440  net.sf.cglib.asm.Label
  22:        366987        8807688  net.sf.cglib.asm.Edge
  23:        204301        8172040  java.util.HashMap$KeyIterator
  24:         97944        7770912  [Ljava.util.concurrent.ConcurrentHashMap$Segment;
  25:        304873        7316952  org.h2.value.ValueLong
  26:        142069        6928760  [Lorg.h2.value.Value;
  27:        168645        6745800  java.util.LinkedHashMap$Entry

jmap -permstat pid

用来搜集持久代的统计信息,执行时间很慢

jmap -dump pid

jmap -dump:format=b,file=heap.bin pid

导出堆内存的快照,便于对内存进行离线分析,但是需要借助一些第三方的工作,比如:MAT, Jprofiler

  • 堆内存概要汇总信息

堆内存概要汇总信息

  • 堆内存详细信息

堆内存详细信息

这里有 2 个概念简单解释一下

  • Shallow Heap 对象本身占用的内存大小,对象引用的别的对象不包含在内
  • Retained Heap 对象被回收可以释放掉的内存大小,包括了自身的大小和它引用的可释放的空间大小

jhat

对导出的 Java 堆文件进行在线的分析,功能类似 MAT

jhat -J-mx512m -port 7000 <file>

这样开启服务后,可以在本地通过浏览器进行访问

jstat

查看 JVM 运行时的一些信息,可通过 -options 查看所有功能,如下

./jstat -options
-class
-compiler
-gc
-gccapacity
-gccause
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcpermcapacity
-gcutil
-printcompilation

jstat -gcutil pid 1000

每秒打印出 GC 情况,如下

./jstat -gcutil 193350 1000
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT
 57.64   0.00  26.82  75.46  61.13   1202   63.121     8    2.563   65.684
 57.64   0.00  26.82  75.46  61.13   1202   63.121     8    2.563   65.684
 57.64   0.00  26.82  75.46  61.13   1202   63.121     8    2.563   65.684
 57.64   0.00  26.82  75.46  61.13   1202   63.121     8    2.563   65.684
 57.64   0.00  26.82  75.46  61.13   1202   63.121     8    2.563   65.684
 57.64   0.00  26.82  75.46  61.13   1202   63.121     8    2.563   65.684

jstack

查看线程相关的信息

jstack -l pid

分析死锁

$./jstack -l 213676 | head
2017-10-13 10:04:18
Full thread dump OpenJDK (Taobao) 64-Bit Server VM (20.0-b12-internal mixed mode):

"http-bio-7001-exec-11" daemon prio=10 tid=0x00002aaab759d000 nid=0x1bd3e waiting on condition [0x0000000056254000]
   java.lang.Thread.State: TIMED_WAITING (parking)
  at sun.misc.Unsafe.park(Native Method)
  - parking to wait for  <0x000000079180e650> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
  at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196)
  at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2025)
  at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:424)

分析线程负载

找到负载最高的 Java 进程编号

top
  • P 按 CPU 排序
  • M 按内存排序
  • T 按运行时间排序

找到这个进程下的所有的线程

top -H -p 213676

将负载高的或者运行时间最长的线程号转换成 16 进制

printf '%x\n' 215414
34976

根据线程号找到对应的代码

./jstack 213676 | fgrep -A 30 34976

得到的输出如下

"http-bio-7001-exec-1" daemon prio=10 tid=0x00002aaaccfba000 nid=0x34976 waiting on condition [0x00000000505f8000]
   java.lang.Thread.State: TIMED_WAITING (parking)
  at sun.misc.Unsafe.park(Native Method)
  - parking to wait for  <0x000000079180e650> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
  at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:196)
  at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2025)
  at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:424)
  at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:86)
  at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:32)
  at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:945)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
  at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
  at java.lang.Thread.run(Thread.java:662)

"http-bio-7001-AsyncTimeout" daemon prio=10 tid=0x00002aaab8080000 nid=0x34975 waiting on condition [0x00000000504f7000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
  at java.lang.Thread.sleep(Native Method)
  at org.apache.tomcat.util.net.JIoEndpoint$AsyncTimeout.run(JIoEndpoint.java:152)
  at java.lang.Thread.run(Thread.java:662)

然后根据这个堆栈找到对应的代码分析问题吧!

jinfo

打印系统配置信息,如下

VM Flags:

-Djava.util.logging.config.file=/home/admin/ma/.default/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Dcatalina.vendor=alibaba -Djava.securi
ty.egd=file:/dev/./urandom -Dlog4j.defaultInitOverride=true -Dorg.apache.tomcat.util.http.ServerCookie.ALLOW_EQUALS_IN_VALUE=true -Dorg.apache.tomcat.util.http.ServerCookie.ALLOW_HTTP_S
EPARATORS_IN_V0=true -Xms4g -Xmx4g -XX:PermSize=96m -XX:MaxPermSize=384m -Xmn2g -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSMaxAbortablePrecleanTime=5000 -XX:+CMSC
lassUnloadingEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=80 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/logs/java.hprof -Xloggc:/home/
admin/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Djava.awt.headless=true -Dsun.net.client.defaultConnectTimeout=10000 -Dsun.net.client.defaultReadTimeout=30000 -XX:MaxDirec
tMemorySize=1g -XX:SurvivorRatio=10 -XX:+ExplicitGCInvokesConcurrent -Dsun.rmi.dgc.server.gcInterval=2592000000 -Dsun.rmi.dgc.client.gcInterval=2592000000 -XX:ParallelGCThreads=4 -DJM.L
OG.PATH=/home/admin/logs -Dfile.encoding=GB18030 -Dhsf.publish.delayed=true -Dproject.name=ma -Djava.endorsed.dirs=/opt/taobao/tomcat/endorsed -Dcatalina.logs=/home/admin/ma/logs/catali
na -Dcatalina.base=/home/admin/ma/.default -Dcatalina.home=/opt/taobao/tomcat -Djava.io.tmpdir=/home/admin/ma/.default/temp

Linux 篇

vmstat

查看系统内存、IO、CPU信息

vmstat 1
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 2616048      0 1149900    0    0   699 28014    6    0  8  1 87  0  5
 0  0      0 2616124      0 1149904    0    0     0  3540 163918 3226  1  0 93  0  5
 0  0      0 2616312      0 1150044    0    0     0     0 175265 3826  3  1 93  0  3
 0  0      0 2612704      0 1150076    0    0     0     0 203592 3892 15  3 75  0  6
 1  0      0 2612732      0 1150120    0    0     4  4268 190790 3580  4  2 89  0  4
 2  0      0 2612552      0 1150168    0    0     0   164 174234 3658  3  1 92  0  4
 0  0      0 2612244      0 1150204    0    0     0  5144 180386 3511  2  1 93  0  4
 1  0      0 2612224      0 1150224    0    0     4     0 178876 3687  3  2 88  0  7
 0  0      0 2612204      0 1150244    0    0     0  3760 179695 3635  2  0 92  0  6
 0  0      0 2612192      0 1150256    0    0     0  8268 179635 3518  2  1 93  0  4
 1  0      0 2612140      0 1150308    0    0     0   140 183020 3535  3  0 92  0  4
 1  0      0 2612020      0 1150424    0    0     0     0 163945 3867 21  1 72  0  5

详细说明参考这里

iostat

查看系统 IO 信息

$iostat
Linux 2.6.32-358.23.2.ali1172.el5.x86_64 (ma011250055040.eu13)  10/12/2017

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           7.95    0.01    0.90    0.00    4.61   86.52

Device:            tps   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn
sda             215.36        24.71      2722.61   29741664 3276937160
sda1              0.06         0.00         0.12       2492     144094
sda2            215.24        24.70      2722.09   29733498 3276311048
sda3              0.00         0.00         0.00       2168          2
sda4              0.00         0.00         0.00          8          0
sda5              0.05         0.00         0.40       2066     482016
sdb            3994.04      5627.35    223680.10 6773098528 269222012744
sdb1           3764.68      5627.35    223680.10 6773097376 269222012744

详细说明参考这里

dmesge

用来查看系统消息,最典型的场景就是发现 Java 进程无帮消失时,查看是否是系统 oom-killer 所为,尤其是在 Docker 环境,因为存在内存超卖的情况,应用无故消失,很有可能是 oom-killer 所为

dmesg | grep java
[34341062.176755] [50398]   697 50398  4086406  1956111  60       0             0 java
[34341062.240302] [256082]   698 256082   117782    19857  62       0             0 java
[34341062.257468] Memory cgroup out of memory: Kill process 50398 (java) score 936 or sacrifice child
[34341062.268023] Killed process 50398, UID 697, (java) total-vm:16345624kB, anon-rss:7822592kB, file-rss:1852kB
[34341065.188952] [50416]   697 50398  4086406  1956099  63       0             0 java
[34341065.252456] [256082]   698 256082   117782    19857  62       0             0 java
[34341065.269762] Memory cgroup out of memory: Kill process 50416 (java) score 936 or sacrifice child
[34341065.279322] Killed process 50416, UID 697, (java) total-vm:16345624kB, anon-rss:7822592kB, file-rss:1804kB

分析这个日志,发现 Java 进程被系统杀掉,但是前面的时间参数看不清楚,使用命令转换一下

date -d "1970-01-01 UTC `echo "$(date +%s)-$(cat /proc/uptime|cut -f 1 -d ' ')+34341065.279322"|bc ` seconds"

free

查看系统内存信息

$free -m
             total       used       free     shared    buffers     cached
Mem:          8192       5440       2751          0          0       1107
-/+ buffers/cache:       4333       3858
Swap:         2047          0       2047

uptime

查看系统 CPU 负载情况

$uptime
 11:35:50 up 13 days, 22:12,  3 users,  load average: 0.31, 0.41, 0.25

top

查看系统进程信息

top - 11:37:11 up 13 days, 22:13,  3 users,  load average: 0.12, 0.33, 0.23
Tasks:  46 total,   1 running,  42 sleeping,   0 stopped,   3 zombie
Cpu(s):  2.1%us,  0.9%sy,  0.0%ni, 92.1%id,  0.0%wa,  0.0%hi,  0.0%si,  4.9%st
Mem:   8388608k total,  5639588k used,  2749020k free,        0k buffers
Swap:  2097144k total,        0k used,  2097144k free,  1137888k cached

   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
213676 admin     20   0 6502m 4.1g  43m S  9.3 51.0   3:28.50 java
   295 root      20   0  721m  19m 7056 S  0.7  0.2 111:14.49 staragent-core
   297 root      20   0  333m 8872 5632 S  0.3  0.1  44:10.78 staragent-ppf
   410 root      20   0  195m 5428 2792 S  0.3  0.1 159:10.20 alisentry_cli
215495 admin     20   0  202m  66m 1164 S  0.3  0.8   0:00.18 nginx-proxy
     1 root      20   0 10360  724  608 S  0.0  0.0   0:05.74 init
   294 root      20   0 29760 2424 1276 S  0.0  0.0   0:23.86 staragentd
   544 root      20   0 69708 1228  644 S  0.0  0.0   0:07.19 sshd
   554 root      20   0 81908 1316  600 S  0.0  0.0   0:12.92 crond
   618 root      20   0 12044  600  484 S  0.0  0.0   0:00.00 mingetty
   619 root      20   0 16952 1208 1008 S  0.0  0.0   0:00.00 svscanboot

df / du

查看磁盘空间和文件大小,当系统运行一段时间后,日志文件大量堆积有可能会撑爆磁盘,可以通过这个命令查看

$df
Filesystem           1K-blocks      Used Available Use% Mounted on
/                     62914560  14369060  48545500  23% /
/dev/v01d             62914560  14369060  48545500  23% /home/admin/cai/alivmcommon
/dev/v02d             62914560  14369060  48545500  23% /home/admin/cai/logs
/dev/v03d             62914560  14369060  48545500  23% /home/admin/cai/top_foot_vm
/dev/v04d             62914560  14369060  48545500  23% /home/admin/localDatas
/dev/v05d             62914560  14369060  48545500  23% /home/admin/logs
/dev/v06d             62914560  14369060  48545500  23% /home/admin/ma/logs
/dev/v07d             62914560  14369060  48545500  23% /home/admin/snapshots/diamond
/dev/v08d             62914560  14369060  48545500  23% /home/admin/tms
/dev/v09d             62914560  14369060  48545500  23% /home/admin/vmcommon
/dev/v10d             62914560  14369060  48545500  23% /home/staragent/plugins
$du * | head
52  appletviewer
52  apt
0 ControlPanel
52  extcheck
4 HtmlConverter
52  idlj
52  jar
52  jarsigner
52  java
52  javac

ulimit

将系统设置成不限制

ulimit -c unlimited

查看文件句柄设置

$ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 16384
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65535
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) unlimited
cpu time               (seconds, -t) unlimited
max user processes              (-u) 16384
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

通过 ulimit 改善系统性能

数据库篇

Mysql 索引优化

left-most 最左前缀原则

如果存在联合索引 (A, B, C),那么查询能够使用索引的情况有这几种

  • A, B, C
  • A, B
  • A

执行计划

explain select * from xxx;

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |   

select_type

select查询的类型,主要是区别普通查询和联合查询、子查询之类的复杂查询。

  • SIMPLE:查询中不包含子查询或者UNION
  • 查询中若包含任何复杂的子部分,最外层查询则被标记为:PRIMARY
  • 在SELECT或WHERE列表中包含了子查询,该子查询被标记为:SUBQUERY
  • 在FROM列表中包含的子查询被标记为:DERIVED(衍生)
  • 若第二个SELECT出现在UNION之后,则被标记为UNION;若UNION包含在 FROM子句的子查询中,外层SELECT将被标记为:DERIVED
  • 从UNION表获取结果的SELECT被标记为:UNION RESULT

type

联合查询所使用的类型,表示MySQL在表中找到所需行的方式,又称“访问类型”。

type显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是: 
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,一般来说,得保证查询至少达到range级别,最好能达到ref。

  • ALL: 扫描全表
  • index: 扫描全部索引树
  • range: 扫描部分索引,索引范围扫描,对索引的扫描开始于某一点,返回匹配值域的行,常见于between、<、>等的查询
  • ref: 非唯一性索引扫描,返回匹配某个单独值的所有行。常见于使用非唯一索引即唯一索引的非唯一前缀进行的查找
  • eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
  • const, system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量。system是const类型的特例,当查询的表只有一行的情况下, 使用system。
  • NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引。

possible_keys

指出MySQL能使用哪个索引在该表中找到行。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用。如果是空的,没有相关的索引。这时要提高性能,可通过检验WHERE子句,看是否引用某些字段,或者检查字段不是适合索引。 

key

显示MySQL实际决定使用的键。如果没有索引被选择,键是NULL。  

key_len

显示MySQL决定使用的键长度。表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。如果键是NULL,长度就是NULL。文档提示特别注意这个值可以得出一个多重主键里mysql实际使用了哪一部分。  注:key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。  

ref

显示哪个字段或常数与key一起被使用。  

rows

这个数表示mysql要遍历多少数据才能找到,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数,在innodb上可能是不准确的。

Extra

包含不适合在其他列中显示但十分重要的额外信息

  • Only index,这意味着信息只用索引树中的信息检索出的,这比扫描整个表要快。 
  • using where是使用上了where限制,表示MySQL服务器在存储引擎受到记录后进行“后过滤”(Post-filter),如果查询未能使用索引,Using where的作用只是提醒我们MySQL将用where子句来过滤结果集。
  • impossible where 表示用不着where,一般就是没查出来啥。 
  • Using filesort(MySQL中无法利用索引完成的排序操作称为“文件排序”)当我们试图对一个没有索引的字段进行排序时,就是filesoft。它跟文件没有任何关系,实际上是内部的一个快速排序。 
  • Using temporary(表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询),使用filesort和temporary的话会很吃力,WHERE和ORDER BY的索引经常无法兼顾,如果按照WHERE来确定索引,那么在ORDER BY时,就必然会引起Using filesort,这就要看是先过滤再排序划算,还是先排序再过滤划算。

中间件篇

DAL

分布式数据层,对 SQL 执行进行打点,然后统计慢 SQL,方便监控和性能优化

RPC

服务调用中间件,对服务进行埋点,方便跟踪分布式调用链,便于系统的优化和限流分析

其他工具

shell 循环工具

工作中常常需要去遍历一个文件,针对每一行记录做一些数据订正或者查询的处理。比如说根据一个包含了订单号列表的文件批量执行快速退款操作。

for i in `cat oid.txt`; do curl -d "outerId=$i&bizType=3001" localhost:7001/tool/query/simple3 >> result.txt;echo " " >> result.txt;done

典型错误

OOM

OutOfMemoryError 异常除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生 OutOfMemoryError(OOM) 异常的可能

Java Heap 溢出

一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess

java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。 出现这种异常,一般手段是先通过内存映像分析工具(如Eclipse Memory Analyzer)对dump出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。 如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链。于是就能找到泄漏对象时通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收。 如果不存在泄漏,那就应该检查虚拟机的参数(-Xmx与-Xms)的设置是否适当。

虚拟机栈和本地方法栈溢出

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常 这里需要注意当栈的大小越大可分配的线程数就越少。

运行时常量池溢出

异常信息:java.lang.OutOfMemoryError:PermGen space

如果要向运行时常量池中添加内容,最简单的做法就是使用String.intern()这个Native方法。该方法的作用是:如果池中已经包含一个等于此String的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。由于常量池分配在方法区内,我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量池的容量。

方法区溢出

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。

异常信息:java.lang.OutOfMemoryError:PermGen space

方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收,判定条件是很苛刻的。在经常动态生成大量Class的应用中,要特别注意这点。

堆内存溢出

OutOfMemoryError: Java heap space

这种是java堆内存不够,一个原因是真不够,另一个原因是程序中有死循环; 如果是java堆内存不够的话,可以通过调整JVM下面的配置来解决:

-Xms4096M
-Xmx4096M

OutOfMemoryError: GC overhead limit exceeded

JDK6新增错误类型,当GC为释放很小空间占用大量时间时抛出;一般是因为堆太小,导致异常的原因,没有足够的内存。

  • 查看系统是否有使用大内存的代码或死循环
  • 通过添加JVM配置,来限制使用内存
-XX:-UseGCOverheadLimit

OutOfMemoryError: PermGen space

这种是P区内存不够,可通过调整JVM的配置

-XX:MaxPermSize=128m
-XXermSize=128m

JVM的Perm区主要用于存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space,这个区域成为年老代,GC在主程序运行期间不会对年老区进行清理,默认是64M大小,当程序需要加载的对象比较多时,超过64M就会报这部分内存溢出了,需要加大内存分配,一般 128m 足够。 

OutOfMemoryError: Direct buffer memory

调整 -XX:MaxDirectMemorySize= 参数,如添加JVM配置

-XX:MaxDirectMemorySize=128m

OutOfMemoryError: unable to create new native thread

Stack空间不足以创建额外的线程,要么是创建的线程过多,要么是Stack空间确实小了。 

由于JVM没有提供参数设置总的stack空间大小,但可以设置单个线程栈的大小;而系统的用户空间一共是3G,除了Text/Data/BSS /MemoryMapping几个段之外,Heap和Stack空间的总量有限,是此消彼长的。因此遇到这个错误,可以通过两个途径解决: 

  • 通过 -Xss启动参数减少单个线程栈大小,这样便能开更多线程(当然不能太小,太小会出现StackOverflowError); 
  • 通过-Xms -Xmx 两参数减少Heap大小,将内存让给Stack(前提是保证Heap空间够用)。 

StackOverflowError 

这也内存溢出错误的一种,即线程栈的溢出,要么是方法调用层次过多(比如存在无限递归调用),要么是线程栈太小。 

优化程序设计,减少方法调用层次;调整 -Xss 参数增加线程栈大小。

PS

本文部分内容来源于网络,无法找到出处,原作者可联系邮箱 yunchow.zy@gmail.com 协商解决

打赏作者一杯

渣渣程序狗

文章内容导航