├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── imooc │ └── monitor_tuning │ ├── MonitorTuningApplication.java │ ├── chapter2 │ ├── CpuController.java │ ├── MemoryController.java │ ├── Metaspace.java │ └── User.java │ ├── chapter4 │ ├── Ch4Controller.java │ ├── PrintArgComplex.java │ ├── PrintArgSimple.java │ ├── PrintConstructor.java │ ├── PrintJinfo.java │ ├── PrintLine.java │ ├── PrintOnThrow.java │ ├── PrintRegex.java │ ├── PrintReturn.java │ └── PrintSame.java │ ├── chapter5 │ └── Ch5Controller.java │ └── chapter8 │ ├── Constant.java │ ├── SelfAdd.java │ ├── SimpleDateFormatUtil.java │ ├── Singleton.java │ ├── Stack.java │ ├── StringAdd.java │ ├── StringConstant.java │ ├── SynchronizedTest.java │ ├── Test1.java │ └── TryFinally.java └── resources └── application.properties /README.md: -------------------------------------------------------------------------------- 1 | 2 | ### 主要内容包括 3 | | :blue_heart:|:blue_heart:|:blue_heart: |:blue_heart:|:blue_heart:|:blue_heart:| 4 | | -------- | :----: | :----: |:----: |:----: |:----: | 5 | | jvm参数类型:alien:|jinfo & jps(参数和进程查看):alien:|jstat(类加载、垃圾收集、JIT 编译):alien: |jmap+MAT(内存溢出):alien:|jstack(线程、死循环、死锁):alien:|JVisualVM(本地和远程可视化监控:alien:| 6 | |使用 BTrace进行拦截调试:alien:|Tomcat 性能监控与调优:alien:| Nginx 性能监控与调优:alien:|JVM 层 GC 调优:alien:|JAVA代码层调优:alien: |:alien:| 7 | #### 1.JVM的参数类型 8 | ``` 9 | 标准参数(各版本中保持稳定) 10 | -help 11 | -server -client 12 | -version -showversion 13 | -cp -classpath 14 | ``` 15 | ``` 16 | X 参数(非标准化参数) 17 | -Xint:解释执行 18 | -Xcomp:第一次使用就编译成本地代码 19 | -Xmixed:混合模式,JVM 自己决定是否编译成本地代码 20 | ``` 21 | 示例: 22 | ``` 23 | java -version(默认是混合模式) 24 | Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode) 25 | java -Xint -version 26 | Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, interpreted mode) 27 | ``` 28 | ``` 29 | XX 参数(非标准化参数) 30 | 主要用于 JVM调优和 debug 31 | Boolean类型(+-) 32 | 格式:-XX:[+-]表示启用或禁用 name 属性 33 | 如:-XX:+UseConcMarkSweepGC(启用cms垃圾收集器) 34 | -XX:+UseG1GC(启用G1垃圾收集器) 35 | 非Boolean类型(key-value)(带=的) 36 | 格式:-XX:=表示 name 属性的值是 value 37 | 如:-XX:MaxGCPauseMillis=500(GC最大停用时间) 38 | -xx:GCTimeRatio=19 39 | -Xmx -Xms属于 XX 参数 40 | -Xms 等价于-XX:InitialHeapSize(初始化堆大小) 41 | -Xmx 等价于-XX:MaxHeapSize (最大堆大小) 42 | -xss 等价于-XX:ThreadStackSize(线程堆栈) 43 | 查看 44 | jinfo -flag MaxHeapSize (查看最大内存) 45 | -XX:+PrintFlagsInitial 46 | -XX:+PrintFlagsFinal 47 | -XX:+UnlockExperimentalVMOptions 解锁实验参数 48 | -XX:+UnlockDiagnosticVMOptions 解锁诊断参数 49 | -XX:+PrintCommandLineFlags 打印命令行参数 50 | 输出结果中=表示默认值,:=表示被用户或 JVM 修改后的值 51 | 示例:java -XX:+PrintFlagsFinal -version 52 | ``` 53 | 54 | #### 2.jinfo & jps(参数和进程查看) 55 | ``` 56 | pid 可通过类似 ps -ef|grep tomcat或 jps来进行查看 57 | jps 58 | [详情参考 jps官方文档](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jps.html) 59 | -l 60 | jinfo 61 | jinfo -flag MaxHeapSize 62 | jinfo -flags 63 | 64 | jstat 65 | [详情参考 jps官方文档](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html#BEHBBBDJ) 66 | jstat 使用示例 67 | ``` 68 | ###### 3.jstat(类加载、垃圾收集、JIT 编译) 69 | ``` 70 | 类加载 71 | 每隔1000ms 即1秒,共输出10次 72 | jstat -class 1000 10 73 | loaded 加载类的个数 74 | [root@localhost java]# jps 75 | 4167 Jps 76 | 3370 Bootstrap 77 | [root@localhost java]# jstat -class 3370 78 | Loaded Bytes Unloaded Bytes Time 79 | 5990 12028.7 0 0.0 15.50 80 | ``` 81 | ``` 82 | 垃圾收集 83 | [root@localhost java]# jstat -gc 3370 84 | Warning: Unresolved Symbol: sun.gc.metaspace.capacity substituted NaN 85 | Warning: Unresolved Symbol: sun.gc.metaspace.used substituted NaN 86 | Warning: Unresolved Symbol: sun.gc.compressedclassspace.capacity substituted NaN 87 | Warning: Unresolved Symbol: sun.gc.compressedclassspace.used substituted NaN 88 | S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 89 | 1600.0 1600.0 0.0 1600.0 13184.0 7779.2 32696.0 22669.8 - - - - 117 1.056 5 0.386 1.441 90 | -gc, -gcutil, -gccause, -gcnew, -gcold 91 | jstat -gc 1000 10 92 | 以下大小的单位均为 KB 93 | S0C, S1C, S0U, S1U: S0和 S1的总量和使用量 94 | EC, EU: Eden区总量与使用量 95 | OC, OU: Old区总量与使用量 96 | MC, MU: Metacspace区(jdk1.8前为 PermGen)总量与使用量 97 | CCSC, CCSU: 压缩类区总量与使用量 98 | YGC, YGCT: YoungGC 的次数与时间 99 | FGC, FGCT: FullGC 的次数与时间 100 | GCT: 总的 GC 时间 101 | ``` 102 | ``` 103 | JIT 编译 104 | -compiler, -printcompilation 105 | [root@localhost java]# jstat -compiler 3370 106 | Compiled Failed Invalid Time FailedType FailedMethod 107 | 833 0 0 26.04 0 108 | 花费26.04s编译了833个方法 109 | ``` 110 | 111 | #### 4.jmap+MAT(内存溢出) 112 | 113 | 内存溢出演示: 114 | [生成springboot初始化代码](https://start.spring.io/) 115 | [最终代码](https://start.spring.io/) 116 | ``` 117 | 为快速产生内存溢出,右击 Run As>Run Configurations, Arguments 标签VM arguments 中填入 118 | -Xmx32M -Xms32M 119 | 访问 http://localhost:8080/heap 120 | Exception in thread "http-nio-8080-ClientPoller-0" java.lang.OutOfMemoryError: GC overhead limit exceeded 121 | at java.util.HashMap$KeySet.iterator(HashMap.java:916) 122 | at java.util.HashSet.iterator(HashSet.java:172) 123 | at java.util.Collections$UnmodifiableCollection$1.(Collections.java:1039) 124 | Exception in thread "http-nio-8080-exec-1" java.lang.OutOfMemoryError: GC overhead limit exceeded 125 | -XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M(同时在 pom.xml 中加入 asm 的依赖) 126 | 访问 http://localhost:8080/nonheap 127 | Exception in thread "main" java.lang.OutOfMemoryError: Metaspace 128 | Exception in thread "ContainerBackgroundProcessor[StandardEngine[Tomcat]]" java.lang.OutOfMemoryError: Metaspace 129 | 内存溢出自动导出 130 | -XX:+HeapDumpOnOutOfMemoryError 131 | -XX:HeapDumpPath=./ 132 | 右击 Run As>Run Configurations, Arguments 标签VM arguments 中填入 133 | -Xmx32M -Xms32M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./ 134 | 可以看到自动在当前目录中生成了一个java_pid660.hprof文件 135 | java.lang.OutOfMemoryError: GC overhead limit exceeded 136 | Dumping heap to ./java_pid660.hprof ... 137 | 另一种导出溢出也更推荐的方式是jmap 138 | option: -heap, -clstats, -dump:, -F 139 | jmap -dump:format=b,file=heap.hprof 140 | C:\Users\Mr Chen>jps -l 141 | 10476 sun.tools.jps.Jps 142 | 6744 143 | 14980 com.imooc.monitor_tuning.MonitorTuningApplication 144 | C:\Users\Mr Chen>cd desktop 145 | C:\Users\Mr Chen\Desktop>jmap -dump:format=b,file=heap.hprof 14980 146 | Dumping heap to C:\Users\Mr Chen\Desktop\heap.hprof ... 147 | Heap dump file created 148 | ``` 149 | jmap 导出溢出文件 150 | [MAT下载地址](http://www.eclipse.org/mat/) 151 | 找开上述导出的内存溢出文件即可进行分析,如下图的溢出源头分析: 152 | Memory Analyzer 内存溢出分析 153 | #### 5.jstack(线程、死循环、死锁) 154 | ##### 1.线程状态 155 | ``` 156 | jstack: 打印jvm内部所有线程 157 | C:\Users\Mr Chen\Desktop>jps -l 158 | 15260 com.imooc.monitor_tuning.MonitorTuningApplication 159 | 10836 sun.tools.jps.Jps 160 | 6744 161 | C:\Users\Mr Chen\Desktop>jstack 15260> 15260.txt 162 | ``` 163 | 可查看其中包含java.lang.Thread.State: WAITING (parking),JAVA 线程包含的状态有: 164 | NEW:线程尚未启动 165 | RUNNABLE:线程正在 JVM 中执行 166 | BLOCKED:线程在等待监控锁(monitor lock) 167 | WAITING:线程在等待另一个线程进行特定操作(时间不确定) 168 | TIMED_WAITING:线程等待另一个线程进行限时操作 169 | TERMINATED:线程已退出 170 | ##### 2.死循环导致cpu飙高 171 | ``` 172 | monitor_tuning中新增CpuController.java 173 | 直接 maven clean、 maven install 174 | 此时会生成一个monitor_tuning-0.0.1-SNAPSHOT.jar的 jar包,为避免本地的 CPU 消耗过多导致死机,建议上传上传到虚拟机进行测试 175 | nohup java -jar monitor_tuning-0.0.1-SNAPSHOT.jar & 176 | 访问 http://xx.xx.xx.xx:8080/loop(端口8080在application.properties文件中定义) 177 | top -p -H可以查看线程及 CPU 消耗情况 178 | top 命令打出线程 CPU 消耗 179 | 180 | ``` 181 | 使用 jstack 可以导出追踪文件,文件中 PID 在 jstack 中显示的对应 nid 为十六进制(命令行可执行 print '%x' 可以进行转化,如4008对应的十六进制为1007) 182 | ``` 183 | [root@localhost java]# jstack 4008 > 4008.txt 184 | [root@localhost java]# printf "%x" 4103 185 | 1007[root@localhost java]# jstack 4103 > 4103.txt 186 | ``` 187 | ``` 188 | "http-nio-8080-exec-7" #22 daemon prio=5 os_prio=0 tid=0x00007f6378be7000 nid=0x1026 runnable [0x00007f63556d1000] 189 | java.lang.Thread.State: RUNNABLE 190 | at java.lang.String.indexOf(String.java:1769) 191 | at java.lang.String.indexOf(String.java:1718) 192 | at com.imooc.monitor_tuning.chapter2.CpuController.getPartneridsFromJson(CpuController.java:73) 193 | ``` 194 | **说明getPartneridsFromJson这里导致的cpu飙升** 195 | 196 | ##### 3.死锁 197 | 198 | 访问http://xx.xx.xx.xx:8080/deadlock(如上jstack 导出追踪记录会发现如下这样的记录) 199 | ``` 200 | [root@localhost java]# ps -ef |grep java 201 | root 4357 3145 23 02:00 pts/0 00:01:22 java -jar monitor_tuning-0.0.1-SNAPSHOT.jar 202 | root 4474 3707 0 02:06 pts/3 00:00:00 grep --color=auto java 203 | [root@localhost java]# jstack 4357 > 4357.txt 204 | 205 | ``` 206 | 将文件拉到最后会发现 207 | ``` 208 | "Thread-5": 209 | at com.imooc.monitor_tuning.chapter2.CpuController.lambda$deadlock$1(CpuController.java:41) 210 | - waiting to lock <0x00000000f6b383e0> (a java.lang.Object) 211 | - locked <0x00000000f6b383f0> (a java.lang.Object) 212 | at com.imooc.monitor_tuning.chapter2.CpuController$$Lambda$337/678439847.run(Unknown Source) 213 | at java.lang.Thread.run(Thread.java:748) 214 | "Thread-4": 215 | at com.imooc.monitor_tuning.chapter2.CpuController.lambda$deadlock$0(CpuController.java:33) 216 | - waiting to lock <0x00000000f6b383f0> (a java.lang.Object) 217 | - locked <0x00000000f6b383e0> (a java.lang.Object) 218 | at com.imooc.monitor_tuning.chapter2.CpuController$$Lambda$336/498593401.run(Unknown Source) 219 | at java.lang.Thread.run(Thread.java:748) 220 | Found 1 deadlock. 221 | ``` 222 | **线程4在等待线程5,同时线程5也在等待线程4,此时死锁** 223 | 224 | #### 6.JVisualVM(本地和远程可视化监控) 225 | ##### 1.监控本地java进程 226 | 详情参考官方文档 227 | Mac命令行直接输入jvisualvm命令,Windows 找到对应的 exe 文件双击即可打开 228 | 插件安装Tools>Plugins>Settings根据自身版本(java -version)更新插件中心地址,各版本查询地址: 229 | [jvisualvm插件](https://visualvm.github.io/pluginscenters.html) 230 | 建议安装:Visual GC, BTrace Workbench 231 | ##### 2.监控远程java进程 232 | 以上是本地的JAVA进程监控,还可以进行远程的监控,在上图左侧导航的 Applications 下的 Remote 处右击Add Remote Host...,输入主机 IP 即可添加,在 IP 上右击会发现有两种连接 JAVA 进程进行监控的方式:JMX, jstatd 233 | ``` 234 | vi bin/catalina.sh(以192.168.73.0为例) 235 | :/JAVA_OPTS 按两个n 添加 236 | ``` 237 | ``` 238 | JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.net.preferIPv4Stack=true -Djava.rmi.server.hostname=192.168.73.0" 239 | ``` 240 | **报错** 241 | 异常情况:VisualVM 无法使用 service:jmx:rmi:///jndi/rmi:///jmxrmi 连接到 242 | 原因: 243 | 除了JMX server指定的监听端口号外,JMXserver还会监听一到两个随机端口号, 244 | 可以通过命grep 来查看当前java进程需要监听的随机端口号 245 | ``` 246 | [root@localhost bin]# jps 247 | 4216 Jps 248 | 4031 Bootstrap 249 | [root@localhost bin]# lsof -i|grep java | grep 4031 250 | java 4031 root 19u IPv4 32304 0t0 TCP *:33707 (LISTEN) 251 | java 4031 root 20u IPv4 32310 0t0 TCP *:9004 (LISTEN) 252 | java 4031 root 21u IPv4 32322 0t0 TCP *:50022 (LISTEN) 253 | java 4031 root 53u IPv4 32323 0t0 TCP *:webcache (LISTEN) 254 | java 4031 root 57u IPv4 32325 0t0 TCP *:8009 (LISTEN) 255 | java 4031 root 69u IPv4 32338 0t0 TCP localhost:mxi (LISTEN) 256 | 将监听的端口去掉防火墙当然9004端口也是需要去掉的 257 | [root@localhost bin]# iptables -I INPUT -p tcp --dport 33707 -j ACCEPT 258 | [root@localhost bin]# iptables -I INPUT -p tcp --dport 50022 -j ACCEPT 259 | [root@localhost bin]# iptables -I INPUT -p tcp --dport 8009 -j ACCEPT 260 | 261 | ``` 262 | 263 | 启动tomcat,以 JMX 为例,在 IP 上右击点击Add JMX Connection...,输入 192.168.73.0:9004 264 | Add JMX Connection 265 | 以上为 Tomcat,其它 JAVA 进程也是类似的,如: 266 | ``` 267 | nohup java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9005 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.net.preferIPv4Stack=true -Djava.rmi.server.hostname=192.168.73.0 -jar monitor_tuning-0.0.1-SNAPSHOT.jar & 268 | ``` 269 | #### 6.使用 BTrace 进行拦截调试 270 | [下载BTrace ](https://github.com/btraceio/btrace/releases/tag/v1.3.11.1) 271 | 可以动态地向目标应用程序的字节码注入追踪代码,使用的技术有 JavaCompilerApi, JVMTI, Agent, Instrumentation+ASM 272 | 使用方法:JVisualVM中添加 BTrace 插件 273 | 方法二:btrace 274 | monitor_tuning中新增包org.alanhou.monitor_tuning.chapter4 275 | 安装BTrace 要记得配置环境变量,以 windos 为例 276 | ``` 277 | BTRACE_HOME D:\Program Files\btrace-bin-1.3.11.1 278 | path 添加 %BTRACE_HOME%\bin 279 | 添加btrace相关依赖btrace-agent, btrace-boot, btrace-client 280 | ``` 281 | 访问:http://localhost:8080/ch4/arg1?name=belong 282 | ``` 283 | d:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>jps -l 284 | 3932 com.imooc.monitor_tuning.MonitorTuningApplication 285 | 1108 sun.tools.jps.Jps 286 | 7852 org/netbeans/Main 287 | 15456 288 | 289 | d:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>btrace 3932 PrintArgSimple.java 290 | [belong, ] 291 | com.imooc.monitor_tuning.chapter4.Ch4Controller,arg1 292 | 293 | ``` 294 | ##### 1.拦截方法 295 | 普通方法:@OnMethod( clazz="", method=""),如上例(PrintArgSimple.java) 296 | 构造函数:@OnMethod( clazz="", method=" ") 297 | 访问 http://localhost:8080/ch4/constructor?id=1&name=belong 298 | ``` 299 | d:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>jps -l 300 | 3296 sun.tools.jps.Jps 301 | 7852 org/netbeans/Main 302 | 15592 com.imooc.monitor_tuning.MonitorTuningApplication 303 | 15456 304 | 305 | d:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>btrace 15592 PrintConstructor.java 306 | com.imooc.monitor_tuning.chapter2.User, 307 | [1, belong, ] 308 | ``` 309 | ##### 2.拦截同名函数:用参数区分(PrintSame.java) 310 | 如下例中虽然方法名相同,但分别有一个和两个参数 311 | ``` 312 | @RequestMapping("/same1") 313 | public String same(@RequestParam("name")String name) { 314 | // 访问地址: http://localhost:8080/ch4/same1?name=Java 315 | return "Hello, "+name; 316 | } 317 | 318 | @RequestMapping("/same2") 319 | public String same(@RequestParam("name")String name, @RequestParam("id")int id) { 320 | // 访问地址: http://localhost:8080/ch4/same2?name=Java&id=1 321 | return "Hello, "+name+", "+id; 322 | } 323 | 324 | import com.sun.btrace.BTraceUtils; 325 | import com.sun.btrace.annotations.BTrace; 326 | import com.sun.btrace.annotations.OnMethod; 327 | import com.sun.btrace.annotations.ProbeClassName; 328 | import com.sun.btrace.annotations.ProbeMethodName; 329 | 330 | @BTrace 331 | public class PrintSame { 332 | 333 | @OnMethod( 334 | clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller", 335 | method="same" 336 | ) 337 | public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, String name,int id) { 338 | BTraceUtils.println(pcn+","+pmn + "," + name+ "," + id); 339 | BTraceUtils.println(); 340 | } 341 | } 342 | ``` 343 | ##### 3.拦截时机 344 | Kind.ENTRY: 入口,默认值(上述例子均为这种情况) 345 | Kind.RETURN: 返回(PrintReturn.java) 346 | ``` 347 | d:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>btrace 16268 PrintReturn.java 348 | com.imooc.monitor_tuning.chapter4.Ch4Controller,arg1,hello,belong 349 | ``` 350 | Kind.THROW: 异常(PrintOnThrow.java) 351 | ``` 352 | d:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>jps -l 353 | 12836 sun.tools.jps.Jps 354 | 16268 com.imooc.monitor_tuning.MonitorTuningApplication 355 | 7852 org/netbeans/Main 356 | 15456 357 | 358 | d:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>btrace 16268 PrintOnThrow.java 359 | java.lang.ArithmeticException: / by zero 360 | com.imooc.monitor_tuning.chapter4.Ch4Controller.exception(Ch4Controller.java:41) 361 | sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 362 | sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 363 | sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 364 | java.lang.reflect.Method.invoke(Method.java:498) 365 | org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209) 366 | org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) 367 | org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) 368 | ``` 369 | 打印指定行号是否执行 370 | ``` 371 | d:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>btrace 16268 PrintLine.java 372 | com.imooc.monitor_tuning.chapter4.Ch4Controller,exception,41 373 | ``` 374 | ##### 3.拦截 this、入参、返回值 375 | this:@self 376 | 入参:可以用 AnyType,也可以用真实类型,同名的用真实的 377 | 返回:@Return 378 | 获取对象的值 379 | 简单类型:直接获取 380 | 复杂类型:反射,类名+属性名(PrintArgComplex.java) 381 | ``` 382 | D:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>jps -l 383 | 2896 sun.tools.jps.Jps 384 | 10568 com.imooc.monitor_tuning.MonitorTuningApplication 385 | 7004 386 | 387 | D:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>btrace -cp "D:\workspace\work-app\project\o435au\target\classes" 10568 PrintArgComplex.java 388 | {id=1, name=belong, } 389 | belong 390 | com.imooc.monitor_tuning.chapter4.Ch4Controller,arg2 391 | ``` 392 | ###### 1.拦截函数中还可以使用正则表达式,如method="/.*/"匹配指定类下的所有方法(PrintRegex.java) 393 | ``` 394 | import com.sun.btrace.BTraceUtils; 395 | import com.sun.btrace.annotations.BTrace; 396 | import com.sun.btrace.annotations.OnMethod; 397 | import com.sun.btrace.annotations.ProbeClassName; 398 | import com.sun.btrace.annotations.ProbeMethodName; 399 | 400 | @BTrace 401 | public class PrintRegex { 402 | 403 | @OnMethod( 404 | clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller", 405 | method="/.*/" ) 406 | public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn) { 407 | BTraceUtils.println(pcn+","+pmn); 408 | BTraceUtils.println(); 409 | } 410 | } 411 | ``` 412 | ``` 413 | D:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>btrace 10568 PrintRegex.java 414 | com.imooc.monitor_tuning.chapter4.Ch4Controller,arg2 415 | ``` 416 | ###### 2.打印环境变量(PrintJinfo.java)和 jinfo -help一样 417 | ``` 418 | D:\workspace\work-app\project\o435au\src\main\java\com\imooc\monitor_tuning\chapter4>btrace 10568 PrintJinfo.java 419 | System Properties: 420 | java.vm.version = 25.131-b11 421 | sun.jnu.encoding = GBK 422 | java.vendor.url = http://java.oracle.com/ 423 | java.vm.info = mixed mode 424 | java.awt.headless = true 425 | user.dir = D:\workspace\work-app\project\o435au 426 | sun.cpu.isalist = amd64 427 | java.awt.graphicsenv = sun.awt.Win32GraphicsEnvironment 428 | sun.os.patch.level = 429 | catalina.useNaming = false 430 | user.home = C:\Users\Mr Chen 431 | java.io.tmpdir = C:\Users\MRCHEN~1\AppData\Local\Temp\ 432 | java.awt.printerjob = sun.awt.windows.WPrinterJob 433 | java.version = 1.8.0_131 434 | file.encoding.pkg = sun.io 435 | java.vendor.url.bug = http://bugreport.sun.com/bugreport/ 436 | file.encoding = UTF-8 437 | ``` 438 | 注意事项 439 | 默认只能本地运行 440 | 生产环境下可以使用,但是被修改的字节码不会被还原 441 | #### 7.Tomcat 性能监控与调优 442 | ##### 1.Tomcat 远程 Debug 443 | [JDWP](https://www.ibm.com/developerworks/cn/java/j-lo-jpda3/) 444 | bin/startup.sh 修改最后一行(添加 jpda) 445 | ``` 446 | exec "$PRGDIR"/"$EXECUTABLE" jpda start "$@" 447 | 448 | ``` 449 | bin/catalina.sh 为便于远程调试进行如下修改 450 | JPDA_ADDRESS="localhost:8000" 451 | 修改为 452 | ``` 453 | if [ -z "$JPDA_ADDRESS" ]; then 454 | JPDA_ADDRESS="54321" 455 | fi 456 | if [ -z "$JPDA_SUSPEND" ]; then 457 | JPDA_SUSPEND="n" 458 | 459 | ``` 460 | 查看日志发现已经启动起来 461 | ``` 462 | tail -f ./logs/catalina.out 463 | ``` 464 | 再查看54321端口是否被监听 465 | ``` 466 | [root@localhost apache-tomcat-8.5.34]# netstat -nap | grep 54321 467 | tcp 0 0 0.0.0.0:54321 0.0.0.0:* LISTEN 3314/java 468 | ``` 469 | 打开54321端口防火墙 470 | ``` 471 | [root@localhost apache-tomcat-8.5.34]# iptables -I INPUT -p tcp --dport 54321 -j ACCEPT 472 | ``` 473 | ``` 474 | 若发现54321端口启动存在问题可尝试bin/catalina.sh jpda start 475 | 本地添加包org.alanhou.monitor_tuning.chapter5,修改打包方式为 war,并重写configure,进入monitor_tuning文件夹,执行mvn clean package 进行打包,target 目录下默认生成的包名为monitor_tuning-0.0.1-SNAPSHOT.war,为便于访问修改为monitor_tuning.war再上传到服务器的webapps目录下 476 | http://192.168.0.5:8080/monitor_tuning/ch5/hello 477 | 使用 Eclipse 远程调试,右击 Debug As > Debug Configurations... > Remote Java Application > 右击 New 新建 478 | ``` 479 | ##### 2.tomcat-manager 监控 480 | ``` 481 | 1.conf/tomcat-users.xml添加用户 482 | 483 | 484 | 485 | 486 | 2.新建conf/Catalina/localhost/manager.xml配置允许的远程连接 487 | 488 | 490 | 492 | 493 | 远程连接将allow="127.0.0.1"修改为allow="^.*$",浏览器中输入http://127.0.0.1:8080/manage或对应的 IP,用户名密码为tomcat-users.xml中所设置的 494 | 3.重启 Tomcat 服务 495 | ``` 496 | ##### 3.psi-probe 监控 497 | [psi-probe下载地址](https://github.com/psi-probe/psi-probe) 498 | ``` 499 | 下载后进入psi-probe-master目录,执行: 500 | mvn clean package -Dmaven.test.skip 501 | 将 web/target/probe.war放到 Tomcat 的 webapps 目录下,同样需要conf/tomcat-users.xml和conf/Catalina/localhost/manager.xml中的配置(可保持不变),启动 Tomcat 服务 502 | 浏览器中输入http://127.0.0.1:8080/probe或对应的 IP,用户名密码为tomcat-users.xml中所设置的 503 | ``` 504 | 505 | ##### 4.Tomcat 调优 506 | ###### 1.线程优化(webapps/docs/config/http.html): 507 | ``` 508 | maxConnections: 最大链接数现在nio 1w,之前apr8192 509 | acceptCount :最大排队数,超过最大连接数后的队列 默认100 510 | maxThreads : 工作线程,最大并发 511 | minSpareThreads :最小空闲线程,不能设置太小 512 | ``` 513 | ###### 2.配置优化(webapps/docs/config/host.html): 514 | ``` 515 | autoDeploy: 周期性检查是否有新应用部署,默认true,生产千万不能true 516 | enableLookups(http.html):默认false,千万不能true 517 | reloadable(context.html):默认false,千万不要true 518 | protocol="org.apache.coyote.http11.Http11AprProtocol" 8.5以后nio 519 | apr用于大并发 比较合适 520 | Session 优化: 521 | 如果是 JSP, 可以禁用原生 Session=false 522 | session放在redis 523 | ``` 524 | ###### 3.补充:APR 配置 525 | ``` 526 | yum install -y apr-devel openssl-devel 527 | cd tomcat/bin 528 | tar -zxvf tomcat-native.tar.gz 529 | cd tomcat-native-1.2.17-src/native/ 530 | ./configure --with-apr=/usr/bin/apr-1-config --with-java-home=/usr/lib/jvm/java-1.8.0 --with-ssl=yes 531 | make && make install 532 | 533 | vi tomcat/bin/setenv.sh 534 | export CATALINA_OPTS=”$CATALINA_OPTS -Djava.library.path=/usr/local/apr/lib” 535 | #vi tomcat/conf/server.xml 536 | 539 | 修改为 540 | 543 | 544 | 545 | 546 | ``` 547 | 548 | ##### 5.Nginx 性能监控与调优 549 | ###### 1.Nginx 安装 550 | 添加 yum 源(/etc/yum.repos.d/nginx.repo) 551 | ``` 552 | [root@localhost Desktop]# vi /etc/yum.repos.d/nginx.repo 553 | [nginx] 554 | name=nginx repo 555 | baseurl=http://nginx.org/packages/centos/7/x86_64/ #7为当前centos的版本号 556 | gpgcheck=0 557 | enabled=1 558 | [root@localhost Desktop]# yum install -y nginx 559 | ``` 560 | 关闭80端口防火墙,打开nginx 561 | ``` 562 | [root@localhost nginx]# iptables -I INPUT -p tcp --dport 80 -j ACCEPT 563 | [root@localhost nginx]# nginx 564 | [root@localhost nginx]# ps -ef | grep nginx 565 | root 12178 1 0 01:25 ? 00:00:00 nginx: master process nginx 566 | nginx 12180 12178 0 01:25 ? 00:00:00 nginx: worker process 567 | root 12192 11673 0 01:25 pts/0 00:00:00 grep --color=auto nginx 568 | 569 | ``` 570 | 开启 571 | ``` 572 | [root@localhost nginx]# nginx 573 | ``` 574 | 重启 575 | ``` 576 | [root@localhost nginx]# nginx -s reload 577 | ``` 578 | 停止 579 | ``` 580 | [root@localhost nginx]# nginx -s stop 581 | ``` 582 | 查看编译参数 583 | ``` 584 | nginx -v 585 | ``` 586 | 默认配置文件 587 | ``` 588 | /etc/nginx/nginx.conf 589 | 注意:配置反向代理要关闭selinux,setenforce 0 590 | ``` 591 | 取消注释 592 | ``` 593 | cat default.conf | grep -v "#' 594 | ``` 595 | ###### 2.ngx_http_stub_status 监控连接信息 596 | ``` 597 | [root@localhost Desktop]# cd /etc/nginx 598 | [root@localhost nginx]# cd conf.d/ 599 | [root@localhost conf.d]# ll 600 | total 4 601 | -rw-r--r--. 1 root root 1093 Nov 6 05:54 default.conf 602 | [root@localhost conf.d]# vi default.conf 603 | 添加 604 | location = /nginx_status { 605 | stub_status on; 606 | access_log off; 607 | allow 127.0.0.1; 608 | deny all; 609 | } 610 | 重启 611 | [root@localhost conf.d]# nginx -s reload 612 | 查看连接信息 613 | [root@localhost conf.d]# wget http://127.0.0.1/nginx_status 614 | --2018-11-12 19:46:04-- http://127.0.0.1/nginx_status 615 | Connecting to 127.0.0.1:80... connected. 616 | HTTP request sent, awaiting response... 200 OK 617 | Length: 97 [text/plain] 618 | Saving to: ‘nginx_status’ 619 | 620 | 100%[======================================>] 97 --.-K/s in 0s 621 | 622 | 2018-11-12 19:46:04 (5.45 MB/s) - ‘nginx_status’ saved [97/97] 623 | 624 | [root@localhost conf.d]# cat nginx_status 625 | Active connections: 1 当前活动连接数 包含正在等待的连接 并发数 626 | server accepts handled requests 627 | 7 7 7 628 | Reading: 0当前正在发生请求 Writing: 1 nginx相应写回客户端的连接数 629 | Waiting: 0 当前空闲的连接数 630 | ``` 631 | ###### 2.ngxtop 632 | [ngxtop](https://github.com/lebinh/ngxtop) 633 | ``` 634 | 安装 python-pip 635 | yum install epel-release 636 | yum install python-pip 637 | 安装 ngxtop 638 | pip install ngxtop 639 | 指定配置文件,查询具体访问:ngxtop -c /etc/nginx/nginx.conf 640 | 查询状态是200:ngxtop -c /etc/nginx/nginx.conf -i 'status == 200' 641 | 查询访问最多 ip:ngxtop -c /etc/nginx/nginx.conf -g remote_addr 642 | ngxtop查询访问最多 ip 643 | ``` 644 | ###### 3.nginx-rrd 图形化监控 645 | ``` 646 | nginx-rrd 依赖于前面的ngx_http_stub_status 647 | 安装 php 648 | yum install php php-gd php-soap php-mbstring php-xmlrpc php-dom php-fpm -y 649 | Ngnix融合 php-fpm(/etc/php-fpm.d/www.conf) 650 | 651 | 修改 652 | [root@localhost nginx]# vi /etc/php-fpm.d/www.conf 653 | user = nginx 654 | ; RPM: Keep a group allowed to write in log dir. 655 | group = nginx 656 | 启动 php-fpm 657 | [root@localhost nginx]# systemctl start php-fpm 658 | [root@localhost nginx]# netstat -nat | grep 9000 659 | tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 660 | 修改 Nginx 配置文件 661 | [root@localhost nginx]# vi /etc/nginx/conf.d/default.conf 662 | 加入 663 | location ~ .php$ { 664 | root /usr/share/nginx/html; 665 | fastcgi_pass 127.0.0.1:9000; 666 | fastcgi_index index.php; 667 | fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; 668 | include fastcgi_params; 669 | } 670 | ``` 671 | 新建php文件查看php是否安装成功 672 | ``` 673 | [root@localhost nginx]# cd /usr/share/nginx/html 674 | [root@localhost html]# vi index.php 675 | 676 | 关闭防火墙 677 | systemctl stop firewalld 678 | 访问 http://192.168.81.128/index.php 检测配置是否成功 679 | ``` 680 | 安装 rddtool 相关依赖 681 | ``` 682 | [root@localhost html]# yum install perl rrdtool perl-libwww-perl libwww-perl perl-rrdtool -y 683 | 移动文件到当前文件 684 | [root@localhost tmp]# mv /usr/share/nginx/html/nginx-rrd-0.1.4.tgz . 685 | ``` 686 | 安装 nginx-rdd 687 | ``` 688 | wget http://soft.vpser.net/status/nginx-rrd/nginx-rrd-0.1.4.tgz 689 | tar zxvf nginx-rrd-0.1.4.tgz 690 | cd nginx-rrd-0.1.4 691 | cp usr/sbin/* /usr/sbin # 复制主程序文件到 /usr/sbin 下 692 | cp etc/nginx-rrd.conf /etc # 复制配置文件到 /etc 下 693 | cp html/index.php /usr/share/nginx/html/ 694 | ``` 695 | ``` 696 | 修改配置(vi /etc/nginx-rrd.conf) 697 | RRD_DIR="/usr/share/nginx/html/nginx-rrd"; 698 | WWW_DIR="/usr/share/nginx/html"; 699 | 添加定时任务 700 | [root@localhost nginx-rrd-0.1.4]# crontab -e 701 | * * * * * /bin/sh /usr/sbin/nginx-collect 702 | */1 * * * * /bin/sh /usr/sbin/nginx-graph 703 | 查看定时任务执行情况 704 | tail -f /var/log/cron 705 | ab 压测(未安装 yum -y install httpd-tools) 706 | ab -n 10000 -c 10 http://127.0.0.1/index.html 707 | 访问 http://192.168.81.128/index.php 即可得到如下这种图形化界面: 708 | ``` 709 | ##### 4.Nginx 优化 710 | ###### 4.配置线程数和并发数 711 | ``` 712 | 增加工作线程数和并发连接数 713 | [root@localhost /]# vi /etc/nginx/nginx.conf 714 | worker_processes 4; # 一般CPU 是几核就设置为几 也可以设置成auto 715 | events { 716 | worker_connections 10240; # 每个进程打开的最大连接数,包含了 Nginx 与客户端和 Nginx 与 upstream 之间的连接 717 | multi_accept on; # 可以一次建立多个连接 718 | use epoll; #epoll这种网络模型 719 | } 720 | 查看nginx 语法是否正确 721 | [root@localhost /]# nginx -t 722 | nginx: the configuration file /etc/nginx/nginx.conf syntax is ok 723 | 启用长连接 724 | [root@localhost /]# vi /etc/nginx/nginx.conf 725 | 配置反向代理 726 | upstream server_pool{ 727 | server localhost:8080 weight=1 max_fails=2 fail_timeout=30s; 728 | server localhost:8081 weight=1 max_fails=2 fail_timeout=30s; 729 | keepalive 300; # 300个长连接 提高效率 730 | } 731 | 配置反向代理服务 732 | location / { 733 | proxy_http_version 1.1; 734 | proxy_set_header Upgrade $http_upgrade; 735 | proxy_set_header Connection "upgrade"; 736 | proxy_pass http://server_pool; #所有请求都代理给server_pool 737 | } 738 | 配置压缩 739 | gzip on; 740 | gzip_http_version 1.1; 741 | gzip_disable "MSIE [1-6].(?!.*SV1)"; 742 | gzip_proxied any; 743 | gzip_types text/plain text/css application/javascript application/x-javascript application/json application/xml application/vnd.ms-fontobject application/x-font-ttf application/svg+xml application/x-icon; 744 | gzip_vary on; 745 | gzip_static on; 746 | 操作系统优化 747 | 配置文件/etc/sysctl.conf 748 | sysctl -w net.ipv4.tcp_syncookies=1 # 防止一个套接字在有过多试图连接到时引起过载 749 | sysctl -w net.core.somaxconn=1024 # 默认128,操作系统连接队列 750 | sysctl -w net.ipv4.tcp_fin_timeout=10 # timewait 的超时时间 751 | sysctl -w net.ipv4.tcp_tw_reuse=1 # os 直接使用 timewait的连接 752 | sysctl -w net.ipv4.tcp_tw_recycle=0 # 回收禁用 753 | /etc/security/limits.conf 754 | hard nofile 204800 755 | soft nofile 204800 756 | soft core unlimited 757 | soft stack 204800 758 | 其它优化 759 | sendfile on; # 减少文件在应用和内核之间拷贝 760 | tcp_nopush on; # 当数据包达到一定大小再发送 761 | tcp_nodelay off; # 有数据随时发送 762 | ``` 763 | ##### 6.JVM层GC调优 764 | [jvm虚拟机规范](https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.html) 765 | ###### 1.运行时数据区: 766 | 程序计数器 PC Register 767 | 虚拟机栈 JVM Stacks 768 | 堆 Heap(java虚拟机所管理内存中最大的一块,存放对象实例) 769 | 方法区 Method Area(线程共享的内存区域,存储类信息,常量,静态常量,编译器编译后的代码) 770 | 常量池 Run-Time Constant Pool 771 | 本地方法栈 Native Method Stacks 772 | 773 | ###### 2.常用参数: 774 | ``` 775 | -Xms 最小堆内存 -Xmx最大堆内存 776 | -XX:NewSize 新生代大小 -XX:MaxNewSize最大新生代大小 777 | -XX:NewRatio new和old的比例 -XX:SurvivorRatio survivor和edn的比例 778 | -XX:MetaspaceSize -XX:MaxMetaspaceSize 一般调大 779 | -XX:+UseCompressedClassPointers 是否启用压缩类指针 780 | -XX:CompressedClassSpaceSize 781 | -XX:InitialCodeCacheSize 782 | -XX:ReservedCodeCacheSize 783 | ``` 784 | ###### 3.垃圾回收算法 785 | java自动垃圾回收 786 | 思想:枚举根节点,做可达性分析 787 | 根节点:类加载器、Thread、虚拟机栈的本地变量表、static 成员、常量引用、本地方法栈的变量 788 | 算法:标记清除、复制、标记整理、分带垃圾回收 789 | jvm垃圾回收yong区用复制算法old区用标记清除或者标记整理 790 | 对象分配:对象优先在 Eden 区分配、大对象直接进入Old 区(-XX:PretenureSizeThreshold)、长期存活对象进入 Old 区(-XX:MaxTenuringThreshold, -XX:+PrintTenuringDistribution, -XX:TargetSurvivorRatio 791 | ###### 4.垃圾收集器 792 | 常见配置示例(bin/catalina.sh) 793 | ``` 794 | PARALLEL_OPTION="-XX:+UseParallelGC -XX:+UseParallelOldGC -XX:MaxGCPauseMillis=200 -XX:GCTimeRatio=99" 795 | CMS_OPTION="-XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5" 796 | G1_OPTION="-XX:+UseG1GC -XX:+UseStringDeduplication -XX:StringDeduplicationAgeThreshold=3 -XX:+UseCompressedClassPointers -XX:MaxGCPauseMillis=200" 797 | 798 | JAVA_OPTS="$JAVA_OPTS $CMS_OPTION -Xms128M -Xmx128M -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M -XX:+UseCompressedClassPointers" 799 | 串行收集器 Serial::Serial, Serial Old (-XX:+UseSerialGC -XX:+UseSerialOldGC) 800 | 并行收集器 Parallel(吞吐量优先, Server 模式默认收集器):Parallel Scavenge, Parallel Old (-XX:+UseParallelGC, -XX:+UseParallelOldGC) 801 | 802 | -XX:ParallelGCThreads= 多少个 GC 线程(CPU> 8 N=5/8; CPU<8 N=CPU) 803 | Parallel Collector Ergonomics: 804 | -XX:MaxGCPauseMillis= 805 | -XX:GCTimeRatio= 806 | -Xmx 807 | 动态内存调整 808 | -XX:YoungGenerationSizeIncrement= 809 | -XX:TenuredGenerationSizeIncrement= 810 | -XX:AdaptiveSizeDecrementScaleFactor= 811 | 812 | 并发收集器 Concurent(停顿时间优先):CMS (-XX:+UseConcMarkSweepGC -XX:+UseParNewGC), G1(-XX:UseG1GC) 813 | 停顿时间:垃圾收集器做垃圾回收中断应用执行的时间。 814 | -xx:MANGCPauseMillis 815 | 吞吐量:花在垃圾收集的时间和花在应用时间的占比。-xx:GCTimeRatui=,垃圾收集时间占:1/1+n 吞吐量最大的时候停顿时间最小最好 816 | ``` 817 | CMS 818 | ``` 819 | 并发收集,低停顿,低延迟,老年代收集器 820 | 1. CMS initial mark: 初始标记 Root, STW 821 | 2. CMS concurrent mark:并发标记 822 | 3. CMS-concurrent-preclean:并发预清理 823 | 4. CMS remark:重新标记,STW 824 | 5. CMS concurrent sweep:并发清除 825 | 6. CMS-concurrent-reset:并发重置 826 | 缺点:CPU 敏感、产生浮动垃圾和空间碎片 827 | 相关参数: 828 | -XX:ConcGCThreads:并发的 GC 线程数 829 | -XX:+UseCMSCompactAtFullCollection:FullGC 之后做压缩 830 | -XX:CMSFullGCsBeforeCompaction:多少次 FullGC之后压缩一次 831 | -XX:CMSInitiatingOccupancyFraction:触发 FullGC 832 | -XX:+UseCMSInitiatingOccupancyOnly:是否动态可调 833 | -XX:+CMSScavengeBeforeRemark:FullGC之前先做 YGC 834 | -XX:+CMSClassUnloadingEnable:启用回收Perm 区 835 | ``` 836 | iCMS 837 | 适用于单核或者双核 838 | G1 Collector(JDK 7开始,推荐使用)新生代和老生代收集器 839 | G1的几个概念 840 | Region 841 | SATB:Snapshot-At-The-Beginning,它是通过 Root Tracing 得到的,GC 开始时候存活对象的快照。 842 | RSet:记录了其他 Region中的对象引用本 Region 中对象的关系,属于 points-into 结构(谁引用了我的对象) 843 | YoungGC 844 | 新对象进入 Eden 区 845 | 存活对象拷贝到Survivor 区 846 | 存活时间达到年龄阈值时,对象晋升到 Old 区 847 | MixedGC 848 | 不是 FullGC,回收所有的 Young和所有的 Old 849 | global concurrent marking 850 | 1. Initial marking phase: 标记 GC Root, STW 851 | 2. Root region scanning phase:标记存活 Region 852 | 3. Concurrent marking phase:标记存活的对象 853 | 4. Remark phase:重新标记,STW 854 | 5. Cleanup phase:部分 STW 855 | 856 | MixedGC时机 857 | InitiatingHeapOccupancyPercent 858 | G1HeapWastePercent 859 | G1MixedGCLiveThresholdPercent Old区的region被回收时候存活对象占比 860 | G1MixedGCCountTarget 一次global concurrent marking 之后最多执行mixed gc 的次数 861 | G1OldGCSetRegionThresholdPercent 一次mixed GC中能被选入cset的最多old区的region数量 862 | -XX:+UseG1GC 开启 G1 863 | -XX:G1HeapRegionSize=n, Region 的大小,1-32M,最多2048个 864 | -XX:MaxGCPauseMillis=200 最大停顿时间 865 | -XX:G1NewSizePercent、-XX:G1MaxNewSizePercent 866 | -XX:G1ReservePercent=10 保留防止 to space溢出 867 | -XX:ParallelGCThreads=n SWT线程数并行 868 | -XX:ConcGCThreads=n 并发线程数=1/4*并行 869 | 870 | 最佳实践 871 | 年轻代大小:避免使用-Xmn, -XX:NewRatio 等显式 Young 区大小,会覆盖暂停时间目标 872 | 暂停时间目标:暂停时间不要太严苛,其吞吐量目标是90%的应用程序时间和10%的垃圾回收时间,太严苛会直接影响到吞吐量 873 | 874 | 需要切换到 G1的情况: 875 | 1. 50%以上的堆被存活对象占用 876 | 2. 对象分配和晋升的速度变化非常大 877 | 3. 垃圾回收时间特别长,超过了1秒 878 | 879 | 查看方法:jinfo -flag xxx 880 | 881 | 并行:多条垃圾收集线程并行工作,但用户线程处于等待状态 882 | 并发:用户线程与垃圾收集线程同时执行(或交替执行) 883 | 884 | 如何选择垃圾收集器 885 | ``` 886 | 优先调整堆的大小让服务器自己来选择 887 | 如果内存小于100m,使用串行收集器 888 | 如果是单核,并且没有停顿时间的要求,串行或者jvm自己选 889 | 如果允许停顿时间超过1s,选择并行或者jvm自己选 890 | 如果响应时间最重要,并且不能超过1s,使用并发收集器 891 | ``` 892 | 893 | ###### 5.可视化 GC 日志分析工具 894 | 打印日志相关参数: 895 | ``` 896 | -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution 897 | 例(默认为 ParallelGC, 其它的添加-XX:+UseConcMarkSweepGC或-XX:+UseG1GC即可): 898 | JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log" 899 | CMS日志格式 900 | G1日志格式 901 | [在线工具gceasy](http://gceasy.io/) 902 | 访问 GCeasy 官网导入日志即可获取可视化分析及优先建议 903 | ``` 904 | GCViewer 905 | ``` 906 | mvn clean package -Dmaven.test.skip 生成 jar包,双击执行,导入日志即可进入图形化分析页面 907 | ``` 908 | Tomcat 的 GC 调优实战 909 | ParallelGC调优 910 | ``` 911 | 设置 Metaspace 大小 -XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M 912 | 添加吞吐量和停顿时间参数 -XX:GCTimeRatio=99 -XX:MaxGCPauseMillis=100 913 | 修改动态扩容增量 -XX:YoungGenerationSizeIncrement=30 914 | ``` 915 | G1 GC 最佳实践 916 | ``` 917 | -XX:InitiatingHeapOccupancyPercent: Use to change the marking threshold. 918 | -XX:G1MixedGCLiveThresholdPercent and -XX:G1HeapWastePercent: Use to change the mixed garbage collection decisions. 919 | -XX:G1MixedGCCountTarget and -XX:G1OldCSetRegionThresholdPercent: Use to adjust the CSet for old regions. 920 | ``` 921 | [jvm内存调优官方文档](https://docs.oracle.com/javase/9/gctuning/introduction-garbage-collection-tuning.htm#JSGCT-GUID-326EB4CF-8C8C-4267-8355-21AB04F0D304) 922 | ##### 7.JAVA代码层调优 923 | 最终代码:monitor_tuning 924 | JVM字节码指令与 javap 925 | javap 926 | ``` 927 | cd monitor_tuning/target/classes/org/alanhou/monitor_tuning/chapter8/ 928 | javap -verbose Test1.class > Test1.txt 即可保存字节码文件 929 | ``` 930 | [常量池](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4) 931 | [字段描述符](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.2) 932 | [方法描述符](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3.3) 933 | [字节码指令](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html) 934 | i++与++i,字符串拼接+原理 935 | javap -verbose SelfAdd.class > SelfAdd.txt 936 | 通过对 f1()和 f2()的字节码,我们得出结论 i++和++i 的执行效果完全相同 937 | ``` 938 | public static void f1(); 939 | descriptor: ()V 940 | flags: ACC_PUBLIC, ACC_STATIC 941 | Code: 942 | stack=2, locals=1, args_size=0 943 | 0: iconst_0 944 | 1: istore_0 945 | 2: goto 15 946 | 5: getstatic #25 // Field java/lang/System.out:Ljava/io/PrintStream; 947 | 8: iload_0 948 | 9: invokevirtual #31 // Method java/io/PrintStream.println:(I)V 949 | 12: iinc 0, 1 950 | 15: iload_0 951 | 16: bipush 10 952 | 18: if_icmplt 5 953 | 21: return 954 | ``` 955 | 其他代码优先方法 956 | 字符串拼接+ 957 | ``` 958 | javap -verbose StringAdd.class >StringAdd.txt 959 | 通过字节码可以看出+拼接符效率要低于 append 960 | for循环每次要new一个stringbuffer 961 | ``` 962 | Try-Finally 963 | javap -verbose TryFinally.class >TryFinally.txt 964 | Constant variable(final) 965 | javap -verbose StringConstant.class >StringConstant.txt 966 | jvm是基于栈的架构 967 | 常用代码优化方法 968 | 1. 尽量重用对象,不要循环创建对象,比如:for 循环字符串拼接(不在 for中使用+拼接,先new 一个StringBuilder再在 for 里 append) 969 | 2. 容器类初始化的地时候指定长度(扩容比较耗时) 970 | ``` 971 | List collection = new ArrayLIst(5); 972 | Map map = new HashMap(32); 973 | ``` 974 | 3. **ArrayList(底层数组)随机遍历快,LinkedList(底层双向链表)添加删除快只需移动一个指针,hashmap底层数组+链表** 975 | 4. 集合遍历尽量减少重复计算 976 | 5. 使用 Entry 遍历 Map 977 | ``` 978 | for(Map.Entryentry:map.entrySet()){ 979 | String key=entry.getKey(); 980 | String value=entry.getValue(); 981 | } 982 | ``` 983 | 6. 大数组复制使用System.arraycopy 984 | 7. 尽量使用基本类型而不是包装类型 985 | Integer底层使用缓存,因为没有1000没有缓存,所以要new 986 | 8. 不要手动调用 System.gc() 987 | 9. 及时消除过期对象的引用,防止内存泄漏 988 | 10. 尽量使用局部变量,减小变量的作用域 989 | 11. 尽量使用非同步的容器ArraryList vs. Vector(建议使用ArraryList)手动加锁 990 | 12. 尽量减小同步作用范围, synchronized 方法 vs. 代码块(建议synchronized 方法) 991 | 13. 用ThreadLocal 缓存线程不安全的对象,SimpleDateFormat 992 | 14. 尽量使用延迟加载 993 | 15. 尽量减少使用反射,必须用加缓存 994 | 16. 尽量使用连接池、线程池、对象池、缓存 995 | 17. 及时释放资源, I/O 流、Socket、数据库连接 996 | 18. 慎用异常,不要用抛异常来表示正常的业务逻辑 997 | 19. String 操作尽量少用正则表达式 998 | 20. 日志输出注意使用不同的级别 999 | 21. 日志中参数拼接使用占位符 1000 | log.info("orderId:" + orderId); 不推荐 1001 | log.info("orderId:{}", orderId); 推荐(没有字符串拼接) 1002 | 1003 | < END > 1004 | 1005 | 程序员NBA 1006 | 一个有故事的程序员 1007 | 1008 | 微信扫描二维码,关注我的公众号 1009 | ![image](https://mmbiz.qpic.cn/mmbiz_jpg/d7tnZXLg9m8vyCxEmcVwcdoaFfaEJAHRYH32r3a4G3Pgg854j1qW1wBulzII2J9ntcKCW1eM52pHd1HD5ZcYicQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1) 1010 | 1011 | 或者加我微信1414200300 一起讨论java技术 1012 | 课程链接:https://coding.imooc.com/class/241.html 1013 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.imooc 7 | monitor_tuning 8 | 0.0.1-SNAPSHOT 9 | war 10 | 11 | monitor_tuning 12 | Demo project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 2.0.2.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | UTF-8 24 | 1.8 25 | 1.8 26 | 1.8 27 | 28 | 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-starter-web 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | test 39 | 40 | 41 | 42 | asm 43 | asm 44 | 3.3.1 45 | 46 | 47 | 48 | com.sun.btrace 49 | btrace-agent 50 | 1.3.11 51 | jar 52 | system 53 | D:\Program Files\btrace-bin-1.3.11.1\build\btrace-agent.jar 54 | 55 | 56 | com.sun.btrace 57 | btrace-boot 58 | 1.3.11 59 | jar 60 | system 61 | D:\Program Files\btrace-bin-1.3.11.1\build\btrace-boot.jar 62 | 63 | 64 | com.sun.btrace 65 | btrace-client 66 | 1.3.11 67 | jar 68 | system 69 | D:\Program Files\btrace-bin-1.3.11.1\build\btrace-client.jar 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.springframework.boot 78 | spring-boot-maven-plugin 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/MonitorTuningApplication.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.builder.SpringApplicationBuilder; 6 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 7 | 8 | @SpringBootApplication 9 | public class MonitorTuningApplication extends SpringBootServletInitializer{ 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(MonitorTuningApplication.class, args); 13 | } 14 | 15 | @Override 16 | protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { 17 | return builder.sources(MonitorTuningApplication.class); 18 | } 19 | } 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter2/CpuController.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter2; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | @RestController 10 | public class CpuController { 11 | 12 | /** 13 | * 死循环 14 | * */ 15 | @RequestMapping("/loop") 16 | public List loop(){ 17 | String data = "{\"data\":[{\"partnerid\":]"; 18 | return getPartneridsFromJson(data); 19 | } 20 | 21 | private Object lock1 = new Object(); 22 | private Object lock2 = new Object(); 23 | 24 | /** 25 | * 死锁 26 | * */ 27 | @RequestMapping("/deadlock") 28 | public String deadlock(){ 29 | new Thread(()->{ 30 | synchronized(lock1) { 31 | try {Thread.sleep(1000);}catch(Exception e) {} 32 | synchronized(lock2) { 33 | System.out.println("Thread1 over"); 34 | } 35 | } 36 | }) .start(); 37 | new Thread(()->{ 38 | synchronized(lock2) { 39 | try {Thread.sleep(1000);}catch(Exception e) {} 40 | synchronized(lock1) { 41 | System.out.println("Thread2 over"); 42 | } 43 | } 44 | }) .start(); 45 | return "deadlock"; 46 | } 47 | public static List getPartneridsFromJson(String data){ 48 | //{\"data\":[{\"partnerid\":982,\"count\":\"10000\",\"cityid\":\"11\"},{\"partnerid\":983,\"count\":\"10000\",\"cityid\":\"11\"},{\"partnerid\":984,\"count\":\"10000\",\"cityid\":\"11\"}]} 49 | //上面是正常的数据 50 | List list = new ArrayList(2); 51 | if(data == null || data.length() <= 0){ 52 | return list; 53 | } 54 | int datapos = data.indexOf("data"); 55 | if(datapos < 0){ 56 | return list; 57 | } 58 | int leftBracket = data.indexOf("[",datapos); 59 | int rightBracket= data.indexOf("]",datapos); 60 | if(leftBracket < 0 || rightBracket < 0){ 61 | return list; 62 | } 63 | String partners = data.substring(leftBracket+1,rightBracket); 64 | if(partners == null || partners.length() <= 0){ 65 | return list; 66 | } 67 | while(partners!=null && partners.length() > 0){ 68 | int idpos = partners.indexOf("partnerid"); 69 | if(idpos < 0){ 70 | break; 71 | } 72 | int colonpos = partners.indexOf(":",idpos); 73 | int commapos = partners.indexOf(",",idpos); 74 | if(colonpos < 0 || commapos < 0){ 75 | //partners = partners.substring(idpos+"partnerid".length());//1 76 | continue; 77 | } 78 | String pid = partners.substring(colonpos+1,commapos); 79 | if(pid == null || pid.length() <= 0){ 80 | //partners = partners.substring(idpos+"partnerid".length());//2 81 | continue; 82 | } 83 | try{ 84 | list.add(Long.parseLong(pid)); 85 | }catch(Exception e){ 86 | //do nothing 87 | } 88 | partners = partners.substring(commapos); 89 | } 90 | return list; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter2/MemoryController.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter2; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.UUID; 6 | 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class MemoryController { 12 | 13 | private List userList = new ArrayList(); 14 | private List> classList = new ArrayList>(); 15 | 16 | /** 17 | * -Xmx32M -Xms32M 18 | * */ 19 | @GetMapping("/heap") 20 | public String heap() { 21 | int i=0; 22 | while(true) { 23 | userList.add(new User(i++, UUID.randomUUID().toString())); 24 | } 25 | } 26 | 27 | 28 | /** 29 | * -XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M 30 | * */ 31 | @GetMapping("/nonheap") 32 | public String nonheap() { 33 | while(true) { 34 | classList.addAll(Metaspace.createClasses()); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter2/Metaspace.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter2; 2 | import java.util.ArrayList; 3 | import java.util.List; 4 | 5 | import org.objectweb.asm.ClassWriter; 6 | import org.objectweb.asm.MethodVisitor; 7 | import org.objectweb.asm.Opcodes; 8 | 9 | /* 10 | * https://blog.csdn.net/bolg_hero/article/details/78189621 11 | * 继承ClassLoader是为了方便调用defineClass方法,因为该方法的定义为protected 12 | * */ 13 | public class Metaspace extends ClassLoader { 14 | 15 | public static List> createClasses() { 16 | // 类持有 17 | List> classes = new ArrayList>(); 18 | // 循环1000w次生成1000w个不同的类。 19 | for (int i = 0; i < 10000000; ++i) { 20 | ClassWriter cw = new ClassWriter(0); 21 | // 定义一个类名称为Class{i},它的访问域为public,父类为java.lang.Object,不实现任何接口 22 | cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null, 23 | "java/lang/Object", null); 24 | // 定义构造函数方法 25 | MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "", 26 | "()V", null, null); 27 | // 第一个指令为加载this 28 | mw.visitVarInsn(Opcodes.ALOAD, 0); 29 | // 第二个指令为调用父类Object的构造函数 30 | mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", 31 | "", "()V"); 32 | // 第三条指令为return 33 | mw.visitInsn(Opcodes.RETURN); 34 | mw.visitMaxs(1, 1); 35 | mw.visitEnd(); 36 | Metaspace test = new Metaspace(); 37 | byte[] code = cw.toByteArray(); 38 | // 定义类 39 | Class exampleClass = test.defineClass("Class" + i, code, 0, code.length); 40 | classes.add(exampleClass); 41 | } 42 | return classes; 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter2/User.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter2; 2 | 3 | public class User { 4 | private int id; 5 | private String name; 6 | public User(int id, String name) { 7 | super(); 8 | this.id = id; 9 | this.name = name; 10 | } 11 | public int getId() { 12 | return id; 13 | } 14 | public void setId(int id) { 15 | this.id = id; 16 | } 17 | public String getName() { 18 | return name; 19 | } 20 | public void setName(String name) { 21 | this.name = name; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter4/Ch4Controller.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter4; 2 | 3 | import org.springframework.web.bind.annotation.RequestMapping; 4 | import org.springframework.web.bind.annotation.RequestParam; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | import com.imooc.monitor_tuning.chapter2.User; 8 | 9 | @RestController 10 | @RequestMapping("/ch4") 11 | public class Ch4Controller { 12 | 13 | @RequestMapping("/arg1") 14 | public String arg1(@RequestParam("name")String name) { 15 | return "hello,"+name; 16 | } 17 | 18 | @RequestMapping("/arg2") 19 | public User arg2(User user) { 20 | return user; 21 | } 22 | 23 | @RequestMapping("/constructor") 24 | public User constructor(User user) { 25 | return user; 26 | } 27 | 28 | @RequestMapping("/same1") 29 | public String same(@RequestParam("name")String name) { 30 | return "hello,"+name; 31 | } 32 | @RequestMapping("/same2") 33 | public String same(@RequestParam("name")String name,@RequestParam("id")int id) { 34 | return "hello,"+name+","+id; 35 | } 36 | 37 | @RequestMapping("/exception") 38 | public String exception() { 39 | try { 40 | System.out.println("start..."); 41 | System.out.println(1/0); 42 | System.out.println("end..."); 43 | }catch(Exception e) { 44 | // 45 | } 46 | return "success"; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter4/PrintArgComplex.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter4; 2 | import java.lang.reflect.Field; 3 | 4 | import com.imooc.monitor_tuning.chapter2.User; 5 | import com.sun.btrace.BTraceUtils; 6 | import com.sun.btrace.annotations.BTrace; 7 | import com.sun.btrace.annotations.Kind; 8 | import com.sun.btrace.annotations.Location; 9 | import com.sun.btrace.annotations.OnMethod; 10 | import com.sun.btrace.annotations.ProbeClassName; 11 | import com.sun.btrace.annotations.ProbeMethodName; 12 | 13 | @BTrace 14 | public class PrintArgComplex { 15 | 16 | 17 | @OnMethod( 18 | clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller", 19 | method="arg2", 20 | location=@Location(Kind.ENTRY) 21 | ) 22 | public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, User user) { 23 | //print all fields 24 | BTraceUtils.printFields(user); 25 | //print one field 26 | Field filed2 = BTraceUtils.field("com.imooc.monitor_tuning.chapter2.User", "name"); 27 | BTraceUtils.println(BTraceUtils.get(filed2, user)); 28 | BTraceUtils.println(pcn+","+pmn); 29 | BTraceUtils.println(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter4/PrintArgSimple.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter4; 2 | import com.sun.btrace.AnyType; 3 | import com.sun.btrace.BTraceUtils; 4 | import com.sun.btrace.annotations.BTrace; 5 | import com.sun.btrace.annotations.Kind; 6 | import com.sun.btrace.annotations.Location; 7 | import com.sun.btrace.annotations.OnMethod; 8 | import com.sun.btrace.annotations.ProbeClassName; 9 | import com.sun.btrace.annotations.ProbeMethodName; 10 | 11 | @BTrace 12 | public class PrintArgSimple { 13 | 14 | @OnMethod( 15 | clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller", 16 | method="arg1", 17 | location=@Location(Kind.ENTRY) 18 | ) 19 | public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) { 20 | BTraceUtils.printArray(args); 21 | BTraceUtils.println(pcn+","+pmn); 22 | BTraceUtils.println(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter4/PrintConstructor.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter4; 2 | import com.sun.btrace.AnyType; 3 | import com.sun.btrace.BTraceUtils; 4 | import com.sun.btrace.annotations.BTrace; 5 | import com.sun.btrace.annotations.OnMethod; 6 | import com.sun.btrace.annotations.ProbeClassName; 7 | import com.sun.btrace.annotations.ProbeMethodName; 8 | 9 | @BTrace 10 | public class PrintConstructor { 11 | 12 | @OnMethod( 13 | clazz="com.imooc.monitor_tuning.chapter2.User", 14 | method="" 15 | ) 16 | public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) { 17 | BTraceUtils.println(pcn+","+pmn); 18 | BTraceUtils.printArray(args); 19 | BTraceUtils.println(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter4/PrintJinfo.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter4; 2 | 3 | import com.sun.btrace.BTraceUtils; 4 | import com.sun.btrace.annotations.BTrace; 5 | 6 | @BTrace 7 | public class PrintJinfo { 8 | static { 9 | BTraceUtils.println("System Properties:"); 10 | BTraceUtils.printProperties(); 11 | BTraceUtils.println("VM Flags:"); 12 | BTraceUtils.printVmArguments(); 13 | BTraceUtils.println("OS Enviroment:"); 14 | BTraceUtils.printEnv(); 15 | BTraceUtils.exit(0); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter4/PrintLine.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter4; 2 | import com.sun.btrace.BTraceUtils; 3 | import com.sun.btrace.annotations.BTrace; 4 | import com.sun.btrace.annotations.Kind; 5 | import com.sun.btrace.annotations.Location; 6 | import com.sun.btrace.annotations.OnMethod; 7 | import com.sun.btrace.annotations.ProbeClassName; 8 | import com.sun.btrace.annotations.ProbeMethodName; 9 | 10 | @BTrace 11 | public class PrintLine { 12 | 13 | @OnMethod( 14 | clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller", 15 | method="exception", 16 | location=@Location(value=Kind.LINE, line=41) 17 | ) 18 | public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) { 19 | BTraceUtils.println(pcn+","+pmn + "," +line); 20 | BTraceUtils.println(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter4/PrintOnThrow.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the Classpath exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions.*/ 24 | 25 | 26 | package com.imooc.monitor_tuning.chapter4; 27 | 28 | import com.sun.btrace.BTraceUtils; 29 | import com.sun.btrace.annotations.BTrace; 30 | import com.sun.btrace.annotations.Kind; 31 | import com.sun.btrace.annotations.Location; 32 | import com.sun.btrace.annotations.OnMethod; 33 | import com.sun.btrace.annotations.Self; 34 | import com.sun.btrace.annotations.TLS; 35 | 36 | @BTrace 37 | public class PrintOnThrow { 38 | // store current exception in a thread local 39 | // variable (@TLS annotation). Note that we can't 40 | // store it in a global variable! 41 | @TLS 42 | static Throwable currentException; 43 | 44 | // introduce probe into every constructor of java.lang.Throwable 45 | // class and store "this" in the thread local variable. 46 | @OnMethod( 47 | clazz="java.lang.Throwable", 48 | method="" 49 | ) 50 | public static void onthrow(@Self Throwable self) {//new Throwable() 51 | currentException = self; 52 | } 53 | 54 | @OnMethod( 55 | clazz="java.lang.Throwable", 56 | method="" 57 | ) 58 | public static void onthrow1(@Self Throwable self, String s) {//new Throwable(String msg) 59 | currentException = self; 60 | } 61 | 62 | @OnMethod( 63 | clazz="java.lang.Throwable", 64 | method="" 65 | ) 66 | public static void onthrow1(@Self Throwable self, String s, Throwable cause) {//new Throwable(String msg, Throwable cause) 67 | currentException = self; 68 | } 69 | 70 | @OnMethod( 71 | clazz="java.lang.Throwable", 72 | method="" 73 | ) 74 | public static void onthrow2(@Self Throwable self, Throwable cause) {//new Throwable(Throwable cause) 75 | currentException = self; 76 | } 77 | 78 | // when any constructor of java.lang.Throwable returns 79 | // print the currentException's stack trace. 80 | @OnMethod( 81 | clazz="java.lang.Throwable", 82 | method="", 83 | location=@Location(Kind.RETURN) 84 | ) 85 | public static void onthrowreturn() { 86 | if (currentException != null) { 87 | BTraceUtils.Threads.jstack(currentException); 88 | BTraceUtils.println("====================="); 89 | currentException = null; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter4/PrintRegex.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter4; 2 | import com.sun.btrace.BTraceUtils; 3 | import com.sun.btrace.annotations.BTrace; 4 | import com.sun.btrace.annotations.OnMethod; 5 | import com.sun.btrace.annotations.ProbeClassName; 6 | import com.sun.btrace.annotations.ProbeMethodName; 7 | 8 | @BTrace 9 | public class PrintRegex { 10 | 11 | @OnMethod( 12 | clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller", 13 | method="/.*/" ) 14 | public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn) { 15 | BTraceUtils.println(pcn+","+pmn); 16 | BTraceUtils.println(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter4/PrintReturn.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter4; 2 | import com.sun.btrace.AnyType; 3 | import com.sun.btrace.BTraceUtils; 4 | import com.sun.btrace.annotations.BTrace; 5 | import com.sun.btrace.annotations.Kind; 6 | import com.sun.btrace.annotations.Location; 7 | import com.sun.btrace.annotations.OnMethod; 8 | import com.sun.btrace.annotations.ProbeClassName; 9 | import com.sun.btrace.annotations.ProbeMethodName; 10 | import com.sun.btrace.annotations.Return; 11 | 12 | @BTrace 13 | public class PrintReturn { 14 | 15 | @OnMethod( 16 | clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller", 17 | method="arg1", 18 | location=@Location(Kind.RETURN) 19 | ) 20 | public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, @Return AnyType result,String name) { 21 | BTraceUtils.println(pcn+","+pmn + "," + result+ "," + name); 22 | BTraceUtils.println(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter4/PrintSame.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter4; 2 | import com.sun.btrace.BTraceUtils; 3 | import com.sun.btrace.annotations.BTrace; 4 | import com.sun.btrace.annotations.OnMethod; 5 | import com.sun.btrace.annotations.ProbeClassName; 6 | import com.sun.btrace.annotations.ProbeMethodName; 7 | 8 | @BTrace 9 | public class PrintSame { 10 | 11 | @OnMethod( 12 | clazz="com.imooc.monitor_tuning.chapter4.Ch4Controller", 13 | method="same" 14 | ) 15 | public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, String name,int id) { 16 | BTraceUtils.println(pcn+","+pmn + "," + name+ "," + id); 17 | BTraceUtils.println(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter5/Ch5Controller.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter5; 2 | 3 | import org.springframework.web.bind.annotation.RequestMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | @RestController 7 | @RequestMapping("/ch5") 8 | public class Ch5Controller { 9 | 10 | @RequestMapping("/hello") 11 | public String hello() { 12 | String str = ""; 13 | for(int i=0;i<10;i++) { 14 | str += i; 15 | } 16 | return str; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter8/Constant.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter8; 2 | 3 | public class Constant { 4 | 5 | public static void main(String[] args) { 6 | } 7 | /** 8 | 0: ldc #8 // String hello 9 | 2: astore_0 10 | 3: ldc #46 // String helloworld 11 | 5: astore_1 12 | 6: ldc #48 // String hellohelloworld 13 | 8: astore_2 14 | 9: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream; 15 | 12: aload_2 16 | 13: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 17 | 16: return 18 | * */ 19 | public static void f1() { 20 | final String x="hello"; 21 | final String y=x+"world"; 22 | String z=x+y; 23 | System.out.println(z); 24 | } 25 | /** 26 | 0: ldc #19 // String hello 27 | 2: astore_1 28 | 3: ldc #21 // String helloworld 29 | 5: astore_2 30 | 6: new #42 // class java/lang/StringBuilder 31 | 9: dup 32 | 10: ldc #19 // String hello 33 | 12: invokespecial #44 // Method java/lang/StringBuilder."":(Ljava/lang/String;)V 34 | 15: aload_2 35 | 16: invokevirtual #46 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 36 | 19: invokevirtual #50 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 37 | 22: astore_3 38 | 23: getstatic #25 // Field java/lang/System.out:Ljava/io/PrintStream; 39 | 26: aload_3 40 | 27: invokevirtual #31 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 41 | 30: return 42 | * */ 43 | public void f2(){ 44 | final String x="hello"; 45 | String y=x+"world"; 46 | String z=x+y; 47 | System.out.println(z); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter8/SelfAdd.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter8; 2 | 3 | public class SelfAdd { 4 | 5 | public static void main(String[] args) { 6 | f3(); 7 | f4(); 8 | } 9 | /** 10 | public static void f1(); 11 | descriptor: ()V 12 | flags: ACC_PUBLIC, ACC_STATIC 13 | Code: 14 | stack=2, locals=1, args_size=0 15 | 0: iconst_0 16 | 1: istore_0 17 | 2: goto 15 18 | 5: getstatic #19 // Field java/lang/System.out:Ljava/io/PrintStream; 19 | 8: iload_0 20 | 9: invokevirtual #25 // Method java/io/PrintStream.println:(I)V 21 | 12: iinc 0, 1 22 | 15: iload_0 23 | 16: bipush 10 24 | 18: if_icmplt 5 25 | 21: return 26 | * */ 27 | public static void f1() { 28 | for(int i=0;i<10;i++) { 29 | System.out.println(i); 30 | } 31 | } 32 | /** 33 | public static void f2(); 34 | descriptor: ()V 35 | flags: ACC_PUBLIC, ACC_STATIC 36 | Code: 37 | stack=2, locals=1, args_size=0 38 | 0: iconst_0 39 | 1: istore_0 40 | 2: goto 15 41 | 5: getstatic #19 // Field java/lang/System.out:Ljava/io/PrintStream; 42 | 8: iload_0 43 | 9: invokevirtual #25 // Method java/io/PrintStream.println:(I)V 44 | 12: iinc 0, 1 45 | 15: iload_0 46 | 16: bipush 10 47 | 18: if_icmplt 5 48 | 21: return 49 | * */ 50 | public static void f2() { 51 | for(int i=0;i<10;++i) { 52 | System.out.println(i); 53 | } 54 | } 55 | /** 56 | public static void f3(); 57 | descriptor: ()V 58 | flags: ACC_PUBLIC, ACC_STATIC 59 | Code: 60 | stack=2, locals=2, args_size=0 61 | 0: iconst_0 62 | 1: istore_0 63 | 2: iload_0 64 | 3: iinc 0, 1 65 | 6: istore_1 66 | 7: getstatic #19 // Field java/lang/System.out:Ljava/io/PrintStream; 67 | 10: iload_1 68 | 11: invokevirtual #25 // Method java/io/PrintStream.println:(I)V 69 | 14: return 70 | * */ 71 | public static void f3() { 72 | int i=0; 73 | int j = i++; 74 | System.out.println(j); 75 | } 76 | /** 77 | public static void f4(); 78 | descriptor: ()V 79 | flags: ACC_PUBLIC, ACC_STATIC 80 | Code: 81 | stack=2, locals=2, args_size=0 82 | 0: iconst_0 83 | 1: istore_0 84 | 2: iinc 0, 1 85 | 5: iload_0 86 | 6: istore_1 87 | 7: getstatic #19 // Field java/lang/System.out:Ljava/io/PrintStream; 88 | 10: iload_1 89 | 11: invokevirtual #25 // Method java/io/PrintStream.println:(I)V 90 | 14: return 91 | * */ 92 | public static void f4() { 93 | int i=0; 94 | int j = ++i; 95 | System.out.println(j); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter8/SimpleDateFormatUtil.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter8; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | public class SimpleDateFormatUtil { 7 | private static ThreadLocal dateFormatHolder = new ThreadLocal() { 8 | protected SimpleDateFormat initialValue() { 9 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 10 | } 11 | }; 12 | public static void main(String[] args) { 13 | dateFormatHolder.get().format(new Date()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter8/Singleton.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter8; 2 | 3 | public class Singleton { 4 | private Singleton() {} 5 | private static class SingletonHolder{ 6 | private static Singleton instance = new Singleton(); 7 | } 8 | public static Singleton getInstance() { 9 | return SingletonHolder.instance; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter8/Stack.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter8; 2 | 3 | import java.util.Arrays; 4 | import java.util.EmptyStackException; 5 | 6 | public class Stack { 7 | private Object[] elements; 8 | private int size = 0; 9 | private static final int DEFAULT_INITIAL_CAPACITY = 16; 10 | 11 | public Stack() { 12 | elements = new Object[DEFAULT_INITIAL_CAPACITY]; 13 | } 14 | 15 | public void push(Object e) { 16 | ensureCapacity(); 17 | elements[size++] = e; 18 | } 19 | 20 | public Object pop() { 21 | if (size == 0) 22 | throw new EmptyStackException(); 23 | Object object = elements[size-1]; 24 | elements[size-1] = null; 25 | size--; 26 | return object; 27 | } 28 | 29 | /** 30 | * Ensure space for at least one more element, roughly doubling the capacity 31 | * each time the array needs to grow. 32 | */ 33 | private void ensureCapacity() { 34 | if (elements.length == size) 35 | elements = Arrays.copyOf(elements, 2 * size + 1); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter8/StringAdd.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter8; 2 | 3 | public class StringAdd { 4 | public static void main(String[] args) { 5 | } 6 | 7 | /** 8 | public static void f1(); 9 | descriptor: ()V 10 | flags: ACC_PUBLIC, ACC_STATIC 11 | Code: 12 | stack=3, locals=2, args_size=0 13 | 0: ldc #19 // String 14 | 2: astore_0 15 | 3: iconst_0 16 | 4: istore_1 17 | 5: goto 31 18 | 8: new #21 // class java/lang/StringBuilder 19 | 11: dup 20 | 12: aload_0 21 | 13: invokestatic #23 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 22 | 16: invokespecial #29 // Method java/lang/StringBuilder."":(Ljava/lang/String;)V new StringBuilder(src) 23 | 19: ldc #32 // String A 24 | 21: invokevirtual #34 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 25 | 24: invokevirtual #38 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 26 | 27: astore_0 27 | 28: iinc 1, 1 28 | 31: iload_1 29 | 32: bipush 10 30 | 34: if_icmplt 8 31 | 37: getstatic #42 // Field java/lang/System.out:Ljava/io/PrintStream; 32 | 40: aload_0 33 | 41: invokevirtual #48 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 34 | 44: return 35 | * */ 36 | public static void f1() { 37 | String src = ""; 38 | for(int i=0;i<10;i++) { 39 | //每一次循环都会new一个StringBuilder 40 | src = src + "A"; 41 | } 42 | System.out.println(src); 43 | } 44 | public static void f2() { 45 | //只要一个StringBuilder 46 | StringBuilder src = new StringBuilder(); 47 | for(int i=0;i<10;i++) { 48 | src.append("A"); 49 | } 50 | System.out.println(src); 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter8/StringConstant.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter8; 2 | 3 | public class StringConstant { 4 | public static void main(String[] args) { 5 | String hello = "Hello", lo = "lo"; 6 | System.out.print((hello == "Hello") ); 7 | System.out.print((Other.hello == hello) ); 8 | System.out.print((hello == ("Hel"+"lo")) ); 9 | System.out.print((hello == ("Hel"+lo))); 10 | System.out.println(hello == ("Hel"+lo).intern()); 11 | } 12 | public static class Other{ 13 | public static String hello = "Hello"; 14 | } 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter8/SynchronizedTest.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter8; 2 | 3 | public class SynchronizedTest { 4 | public static void main(String[] args) { 5 | } 6 | public synchronized void f1() {//在this對象上加鎖 7 | System.out.println("f1"); 8 | } 9 | public void f2() { 10 | synchronized(this) { 11 | System.out.println("f2"); 12 | } 13 | } 14 | public static synchronized void f3() { 15 | System.out.println("f3"); 16 | } 17 | public static void f4() { 18 | synchronized(SynchronizedTest.class) { 19 | System.out.println("f4"); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter8/Test1.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter8; 2 | 3 | public class Test1 { 4 | public static void main(String args) { 5 | int a=2; 6 | int b=3; 7 | int c = a + b; 8 | System.out.println(c); 9 | } 10 | /*** 11 | public static void main(java.lang.String); 12 | descriptor: (Ljava/lang/String;)V 13 | flags: ACC_PUBLIC, ACC_STATIC 14 | Code: 15 | # 操作数栈的深度2 16 | # 本地变量表最大长度(slot为单位),64位的是2,其他是1,索引从0开始,如果是非static方法索引0代表this,后面是入参,后面是本地变量 17 | # 1个参数,实例方法多一个this参数 18 | stack=2, locals=4, args_size=1 19 | 0: iconst_2 #常量2压栈 20 | 1: istore_1 #出栈保存到本地变量1里面 21 | 2: iconst_3 #常量3压栈 22 | 3: istore_2 #出栈保存到本地变量2里面 23 | 4: iload_1 #局部变量1压栈 24 | 5: iload_2 #局部变量2压栈 25 | 6: iadd # 栈顶两个元素相加,计算结果压栈 26 | 7: istore_3 # 出栈保存到局部变量3里面 27 | 8: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream; 28 | 11: iload_3 29 | 12: invokevirtual #22 // Method java/io/PrintStream.println:(I)V 30 | 15: return 31 | LineNumberTable: 32 | line 5: 0 33 | line 6: 2 34 | line 7: 4 35 | line 8: 8 36 | line 9: 15 37 | LocalVariableTable: 38 | Start Length Slot Name Signature 39 | 0 16 0 args Ljava/lang/String; 40 | 2 14 1 a I 41 | 4 12 2 b I 42 | 8 8 3 c I 43 | **/ 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/imooc/monitor_tuning/chapter8/TryFinally.java: -------------------------------------------------------------------------------- 1 | package com.imooc.monitor_tuning.chapter8; 2 | 3 | public class TryFinally { 4 | public static void main(String[] args) { 5 | System.out.println(f1()); 6 | } 7 | /** 8 | public static java.lang.String f1(); 9 | descriptor: ()Ljava/lang/String; 10 | flags: ACC_PUBLIC, ACC_STATIC 11 | Code: 12 | stack=1, locals=3, args_size=0 13 | 0: ldc #34 // String hello 14 | 2: astore_0 15 | 3: aload_0 16 | 4: astore_2 17 | 5: ldc #36 // String imooc 18 | 7: astore_0 19 | 8: aload_2 20 | 9: areturn 21 | 10: astore_1 22 | 11: ldc #36 // String imooc 23 | 13: astore_0 24 | 14: aload_1 25 | 15: athrow 26 | * */ 27 | public static String f1() { 28 | String str = "hello"; 29 | try{ 30 | return str; 31 | } 32 | finally{ 33 | str = "imooc"; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8080 --------------------------------------------------------------------------------