├── .gitattributes ├── .gitignore ├── .vscode └── launch.json ├── README.md ├── img-jvm ├── TLAB.jpg ├── base-object-lock.png ├── basic.png ├── card-table-2.png ├── card-table-3.png ├── card-table.png ├── class-exception-table.png ├── class-init.png ├── class-line-table.png ├── class-load-check.png ├── class-load.png ├── class-method.png ├── class-prase.png ├── class.png ├── classloader-double-parent.png ├── classloader-main-2.gif ├── classloader-main.jpg ├── classloader-spi.png ├── cms.png ├── codebyte-1.webp ├── codebyte-2.webp ├── codebyte-3.webp ├── exception-table-1.png ├── exception-table-2.png ├── g1.png ├── heap.jpg ├── jvmargs.jpg ├── lock-1.jpg ├── lock-2.jpg ├── lock.jpg ├── mark-word.png ├── stack-1.png ├── stack-2.png └── stack-3.png ├── jvm.iml ├── maven-source ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── pom.xml └── src │ ├── main │ └── java │ │ └── cn │ │ └── xiaowenjie │ │ ├── App.java │ │ ├── ClassStuct.java │ │ ├── FinallyDemo.java │ │ ├── GuavaFutureDemo.java │ │ ├── IEEE754.java │ │ ├── LocalVarAndGC.java │ │ ├── Main.java │ │ ├── SemaphorePVDemo.java │ │ ├── StampedLockDemo.java │ │ ├── StringInternDemo.java │ │ ├── TestStockDeep.java │ │ ├── ThreadWaitNotify.java │ │ ├── akka │ │ └── sample │ │ │ ├── AkkaQuickstart.java │ │ │ ├── Greeter.java │ │ │ └── Printer.java │ │ ├── bytecode │ │ ├── Calc.java │ │ └── ConstDemo.java │ │ ├── classloader │ │ ├── FindClassLoader.java │ │ └── HelloLoader.java │ │ └── completablefuturedemos │ │ ├── Demo1.java │ │ ├── Demo2.java │ │ ├── Demo3.java │ │ └── Demo4.java │ └── test │ └── java │ └── cn │ └── xiaowenjie │ └── AppTest.java └── 实战java高并发程序设计.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | /.idea 25 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "type": "java", 5 | "name": "CodeLens (Launch) - AkkaQuickstart", 6 | "request": "launch", 7 | "mainClass": "cn.xiaowenjie.akka.sample.AkkaQuickstart", 8 | "projectName": "jvm" 9 | }, 10 | { 11 | "type": "java", 12 | "name": "CodeLens (Launch) - Main", 13 | "request": "launch", 14 | "mainClass": "cn.xiaowenjie.akka.Main", 15 | "projectName": "jvm" 16 | }, 17 | { 18 | "type": "java", 19 | "name": "CodeLens (Launch) - SemaphorePVDemo", 20 | "request": "launch", 21 | "mainClass": "cn.xiaowenjie.SemaphorePVDemo", 22 | "projectName": "jvm" 23 | }, 24 | { 25 | "type": "java", 26 | "name": "CodeLens (Launch) - Demo4", 27 | "request": "launch", 28 | "mainClass": "cn.xiaowenjie.completablefuturedemos.Demo4", 29 | "projectName": "jvm" 30 | }, 31 | { 32 | "type": "java", 33 | "name": "CodeLens (Launch) - Demo3", 34 | "request": "launch", 35 | "mainClass": "cn.xiaowenjie.completablefuturedemos.Demo3", 36 | "projectName": "jvm" 37 | }, 38 | { 39 | "type": "java", 40 | "name": "CodeLens (Launch) - Demo1", 41 | "request": "launch", 42 | "mainClass": "cn.xiaowenjie.completablefuturedemos.Demo1", 43 | "projectName": "jvm" 44 | }, 45 | { 46 | "type": "java", 47 | "name": "CodeLens (Launch) - ThreadWaitNotify", 48 | "request": "launch", 49 | "mainClass": "cn.xiaowenjie.ThreadWaitNotify", 50 | "projectName": "jvm" 51 | }, 52 | { 53 | "type": "java", 54 | "name": "CodeLens (Launch) - GuavaFutureDemo", 55 | "request": "launch", 56 | "mainClass": "cn.xiaowenjie.GuavaFutureDemo", 57 | "projectName": "jvm" 58 | }, 59 | { 60 | "type": "java", 61 | "name": "CodeLens (Launch) - IEEE754", 62 | "request": "launch", 63 | "mainClass": "xiaowenjie.IEEE754", 64 | "projectName": "jvm_68cc5fa6" 65 | }, 66 | { 67 | "type": "java", 68 | "name": "CodeLens (Launch) - ThreadWaitNotify", 69 | "request": "launch", 70 | "mainClass": "xiaowenjie.ThreadWaitNotify", 71 | "projectName": "jvm_68cc5fa6" 72 | } 73 | ] 74 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 实战JAVA虚拟机 2 | 3 | # java语言规范 4 | 5 | 语法,词法,支持的数据类型,变量类型,数据类型转换的约定,数组,异常等,告诉开发人员“java代码应该怎么样写” 6 | 7 | ## 词法 8 | 9 | 什么样的单词是对的。 10 | 11 | >整数可以有下划线 12 | 13 | ## 语法 14 | 15 | 什么样的语句是对的。 16 | 17 | ## 数据类型的定义 18 | 19 | char为16位无符号整数。 20 | float和double为满足IEEE754的32位浮点数和64位浮点数。 21 | 22 | 引用数据类型分为3重 23 | * 类/接口 24 | * 泛型类型 25 | * 数组类型 26 | 27 | ## 数字编码 28 | 29 | 整数用补码表示,正数的补码是本身,负数的补码就是反码+1。反码就是符号位不变,其他位取反。 30 | 31 | ### 补码的好处 32 | * 0既不是正数也不是负数,反码不好表示,补码则相同。 33 | * 补码将加减法的做法完全统一,无需区分正数和负数 34 | 35 | ### 浮点数的表示 36 | 37 | **IEEE754**规范,一个浮点数由符号位,指数位和尾数位3部分组成。 38 | 32位的float类型,符号位1位,指数位8位,尾数位为23位。 39 | 40 | > s eeeeeeee m(23个)\ 41 | > 当e全部为0的时候,m前面加0,否则加1 42 | 43 | 44 | 浮点数取值为 s * m * 2的(e-127)次方 45 | 46 | -5 = 1 1000001 010(21个0) 47 | 48 | 因为 e 不全为0,前面加个1,实际尾数为 1010(21个0) 49 | 50 | ``` 51 | -5 = -1 * 2的(129-127)次方 * (1*2的0次方+0*2的-1次方+1*2的-2次方+0*2的-3次方+后面都是0) 52 | = -1 * 4 * (1 + 0 + 1/4 + 0 ...) 53 | = -1 * 4 * 1.25 54 | = -5 55 | ``` 56 | 57 | ```java 58 | float f = -5; 59 | 60 | // float的内部表示 61 | System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f))); 62 | //1 10000001(指数位8位) 01000000000000000000000(23位) 63 | 64 | 65 | double d = -5; 66 | 67 | //1 10000000001(指数位11位) 0100000000000000000000000000000000000000000000000000(52位) 68 | // double 的内部表示 69 | System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d))); 70 | ``` 71 | 72 | [浮点数的大小比较为什么不能用等号?](https://www.cnblogs.com/xiehongfeng100/p/4851201.html) 73 | 74 | ```java 75 | { 76 | float f1 = 0.1f; 77 | float f2 = 0.1f; 78 | 79 | 80 | // 默认是 double 81 | System.out.println(15.1 * 100 + 0.9 * 100 == 16.0 * 100); //true 82 | 83 | // 默认是 double 84 | System.out.println(16.1 * 100 + 0.9 * 100 == 17.0 * 100); //false 85 | 86 | // float 87 | System.out.println(16.1f * 100 + 0.9f * 100 == 17.0f * 100); //true 88 | 89 | System.out.println(Float.compare(16.1f * 100 + 0.9f * 100, 17.0f * 100)); //0 90 | 91 | System.out.println((16.1 + 0.9) * 100 == 17.0 * 100); // true 92 | } 93 | 94 | { 95 | float f1 = 1f / 3; 96 | float f2 = 1f / 3; 97 | System.out.println(f1 == f2); // true 98 | } 99 | 100 | ``` 101 | 102 | # 基本结构 103 | 104 | ![基本结构](img-jvm/basic.png) 105 | 106 | NIO可以使用直接内存(堆外内存)。 107 | 108 | PC:如果是不是本地方法,PC指向正在执行的指令,如果是本地方法,PC就是undefined。 109 | 110 | ## 堆 111 | 112 | ![](img-jvm/heap.jpg) 113 | 114 | ![](img-jvm/jvmargs.jpg) 115 | 116 | ## 栈 117 | 118 | > -Xss128K 指定最大栈空间。 119 | 120 | 线程私有的内存空间。 121 | 122 | 至少包含: 123 | * 局部变量表 124 | * 操作数栈 125 | * 帧数据区 126 | 127 | 函数被返回的时候,栈帧被弹出。二种方式 128 | * 正常返回,就是return 129 | * 异常抛出 130 | 131 | 栈深度太大,会抛出 **StackOverflowError** 栈溢出错误。 132 | 133 | 134 | ### 局部变量表 135 | 136 | 保持函数的参数和局部变量。越多越大,嵌套调用次数就会减少。 137 | 138 | ```java 139 | public static void recursion(int i1, float f1, long l1, double d1){ 140 | count++; 141 | 142 | // 局部变量 143 | int i2=1; // 1 word = 32bit 144 | float f2 = 2f; // 1 word 145 | long l2 = 3L; // 2 word 146 | double d2 = 4; // 2 word 147 | 148 | Object o1= null; // 1 word 149 | Object o2= null; // 1 word 150 | 151 | recursion(i1,f1,l1, d1); 152 | } 153 | ``` 154 | 155 | ![占用空间](img-jvm/stack-1.png) 156 | 157 | ![局部变量表内容](img-jvm/stack-2.png) 158 | 159 | **槽位复用** 160 | 161 | ```java 162 | // 复用槽位 163 | public static void recursion2(int i1){ 164 | { 165 | int i2 = i1; 166 | System.out.println(i2); 167 | } 168 | 169 | int i3 = 1; 170 | } 171 | ``` 172 | 173 | ![](img-jvm/stack-3.png) 174 | 175 | **占2word的槽位的复用** 176 | 177 | ```java 178 | // 局部变量表是4 word 179 | public static void recursion3(double d1){ 180 | { 181 | double d2 = d1; // 2 word 182 | System.out.println(d2); 183 | } 184 | 185 | int i3 = 1; // 1 word 复用 d2的第一个1槽位 186 | int i4 = 2; // 1 word 复用 d2的第二个操作 187 | } 188 | 189 | ``` 190 | 191 | **槽位和gc** 192 | 193 | ```java 194 | public static void gc(){ 195 | { 196 | byte[] a = new byte[6*1024*1024]; 197 | } 198 | 199 | // a 虽然失效,但仍然在局部变量表,无法gc 200 | System.gc(); 201 | } 202 | 203 | public static void gc2(){ 204 | { 205 | byte[] a = new byte[6*1024*1024]; 206 | } 207 | 208 | int c = 1; 209 | 210 | // a 失效,槽位重用,a已经不存在,可以回收 211 | System.gc(); 212 | } 213 | ``` 214 | 215 | ### 异常处理表 216 | 217 | 异常处理表是帧数据区重要的一部分 218 | 219 | ```java 220 | public static void main(String[] args) { 221 | try { 222 | recursion(1,2,3,4); 223 | } 224 | catch (Throwable e){ 225 | System.out.println(count); 226 | e.printStackTrace(); 227 | } 228 | } 229 | ``` 230 | 231 | **机器码如下** 232 | 233 | ![异常处理表](img-jvm/exception-table-1.png) 234 | 235 | 236 | **异常表如下** 237 | 238 | ![异常处理表](img-jvm/exception-table-2.png) 239 | 240 | 241 | ## 栈上分配 242 | 243 | 基础的基于 **逃逸分析** (判断对象的作用域是否可能逃逸出函数体) 244 | 245 | 必须 **server模式** 。栈上分配并没有真正实现,是通过标量替换实现的。? 246 | 247 | > -server -XX:+DoEscapeAnalysis -XX:+EliminateAllocations 248 | 249 | 250 | 251 | **逃逸分析** 252 | 253 | * 同步消除 254 | * 标量替换 255 | 256 | 通过-XX:+**EliminateAllocations** 可以开启标量替换, -XX:+**PrintEliminateAllocations** 查看标量替换情况。 257 | 258 | ## 方法区 259 | 260 | 1.8之前可以理解为 **永久区**(PerSize,MaxPerSize)。1.8之后使用 **元数据区** 取代。(MaxMetaspaceSize)。 261 | 262 | 263 | 264 | 265 | 266 | 267 | --- 268 | --- 269 | --- 270 | 271 | 272 | # 常用java虚拟机参数 273 | 274 | ## 查看参数 275 | 276 | -XX:+PrintFlagsInitial 打印所有参数 277 | 278 | -XX:+PrintFlagsFinal 279 | 280 | -XX:+PrintCommmandLineFlags 打印传递的参数 281 | 282 | -XX:+PrintVMOptions 283 | 284 | ## GC信息 285 | 286 | PrintGC/PrintGCDetails 287 | 288 | PrintHeapAtGC(GC前后堆信息) 289 | 290 | -Xloggc:log/gc.log 日志存放 291 | 292 | 293 | 294 | ## 类加载、卸载 295 | 296 | -verbose:class 跟踪类加载和卸载。等于 -XX:+TraceClassLoding 和 -XX:+TraceClassUnLoding 297 | 298 | > verbose adj.冗长的;啰嗦的;唠叨的。详细;罗嗦的;详细的 299 | 300 | 301 | ## 非堆内存 302 | 303 | -XX:MaxDirectMemorySize,如果不配置,默认为最大堆空间(-Xmx)。直接内存使用达到时候会触发GC,可能导致OOM 304 | 305 | ## 虚拟机工作模式 306 | 307 | Client / Server / 308 | 309 | 310 | 311 | 312 | --- 313 | --- 314 | --- 315 | # GC算法 316 | 317 | ## 引用计数器 318 | 319 | 存在循环引用和性能问题。没有采用。 320 | 321 | ## 标记清除法 322 | 323 | 标记阶段:通过根节点,标记所有可到达对象 324 | 325 | **缺点** 回收后的空间是不连续的。 326 | 327 | ## 复制算法(新生代) 328 | 329 | 内存分2块,每次用一块。 330 | 331 | **新生代**:分为eden,from,to 3块。from和to称为survivor区,大小一样的2块。 332 | 333 | **老生代** 334 | 335 | 这种算法适合 **新生代** 。 336 | 337 | ## 标记压缩法(老生代) 338 | 339 | 将所有存活对象压缩到内存一端,然后直接清除边界外的空间。 340 | 341 | ## 分代算法 342 | 343 | 新生代采用复制算法,老生代采用标记压缩法/标记清除算法。 344 | 345 | ### 卡表(Card Table) 346 | 347 | 卡表中每一个位表示年老代4K的空间,卡表记录未0的年老代区域没有任何对象指向新生代,只有卡表位为1的区域才有对象包含新生代引用,因此在新生代GC时,只需要扫描卡表位为1所在的年老代空间。使用这种方式,可以大大加快新生代的回收速度。 348 | 349 | ![card table](img-jvm/card-table.png) 350 | 351 | 卡表为1的时候,才扫描对应的老生代。 352 | 353 | 新生代GC的时候,只需要扫描所有卡表为1所在的老生代空间。加快新生代GC时间。 354 | 355 | ![card table](img-jvm/card-table-2.png) 356 | 357 | ![card table](img-jvm/card-table-3.png) 358 | 359 | 360 | ## 分区算法 361 | 362 | 内存分为多个区处理。 363 | 364 | 365 | ## 可触及性 366 | 367 | 包含3种状态 368 | 369 | * 可触及的:根节点开始可到达。 370 | * 可复活的:对象所有引用被释放,但是对象可能在finalize函数中复活。 371 | * 不可触及的:finalize函数已经调用,而且没有复活。 372 | 373 | ## 引用 374 | 375 | * 强引用 376 | * 软引用:可被回收。GC 不一定会回收,但内存紧张就会被回收,不会导致OOM。 377 | * 弱引用:发现就回收,不够空间够不够。 378 | * 虚引用:对象回收跟踪,必须和引用队列一起使用,作用在于跟踪垃圾回收过程。 379 | 380 | ## STW(Stop-The-World) 381 | 382 | 383 | 384 | 385 | 386 | --- 387 | --- 388 | --- 389 | # 垃圾收集器和内存分配 390 | 391 | ## 串行回收器 392 | 393 | -XX:+UseSerialGC,老生代和新生代都使用。 394 | 395 | ## 并行回收器 396 | 397 | ### 新生代 ParallelGC 回收器 398 | 399 | 复制算法 400 | 401 | ### 老生代 ParallelOldGC 回收器 402 | 403 | 关注吞吐量。 404 | 405 | ## CMS回收器 406 | 407 | 标记清除算法。 408 | 409 | ![](img-jvm/cms.png) 410 | 411 | * 初始标记:STW,标记根对象 412 | * 并发标记:标记所有对象 413 | * 预清理:清理前准备以及控制停顿时间 414 | * 重新标记:STW,修正并发标记数据 415 | * 并发清理 416 | * 并发重制 417 | 418 | 除了那2个STW(图片中全红的),其他时候可以和应用线程并发执行。 419 | 420 | ## G1回收器(Garbage First) 421 | 422 | jdk1.7的并行的分代垃圾回收器,依然区分年轻代和老生代,依然有eden区和servivor区。 423 | 424 | 没有采用传统物理隔离的新生代和老年代的布局方式,仅仅以逻辑上划分为新生代和老年代,选择的将 Java 堆区划分为 2048 个大小相同的独立 Region 块。 425 | 426 | ![g1](img-jvm/g1.png) 427 | 428 | ### 对象进入老生代 429 | 430 | * 多次gc存活 431 | * 大对象直接进入 432 | * 超过from/to大小 433 | * 超过 **PertenureSizeThredhold** 参数大小 434 | 435 | ### TLAB上分配对象 436 | 437 | Thread Local Allocation Buffer,线程本地分配缓存。 438 | 439 | 为了加速对象分配。由于对象一般在堆上,而堆是共享的,需要同步。 440 | 441 | 占用eden区空间,在TLAB启用情况下,虚拟机会为每一个线程分配一块TLAB空间。默认很小(2048)。 442 | 443 | -XX:+UserTLAB / -XX:PrintTLAB 444 | 445 | -XX:TLABSize=102400 指定大小 446 | 447 | -XX:-ResizeTLAB 大小会一直调整,可以禁止调整,一次性设置值。 448 | 449 | ![](img-jvm/TLAB.jpg) 450 | 451 | ### finalize()方法对垃圾回收的影响 452 | 453 | 函数finalize是由FinalizerThread线程处理的。每一个即将回收并且包含finalize方法的对象都会在正式回收前加入FinalizerThread的执行队列。 454 | 455 | 糟糕的Finalize方法(如耗时sleep 1秒),会导致对象来不及回收,导致OOM。 456 | 457 | **MAT** 工具可以查看 Finalizers 458 | 459 | 460 | 461 | 462 | --- 463 | --- 464 | --- 465 | # 性能监控工具 466 | 467 | ## Linux下工具 468 | 469 | * top:显示系统整体资源使用情况 470 | * vmstat:监控内存和CPU 471 | * vmstat 1 3 每秒一次,一共3次 472 | * iostat:监控IO使用 473 | * iostat 1 3 474 | * pidstat:多功能诊断器(sysstat组件之一) 475 | * 需要安装 sudo apt-get install sysstat 476 | 477 | ## windows下工具 478 | 479 | * perfmon windows自带的性能监控器 480 | * pslist 需要安装 481 | * JDK性能监控工具 482 | * jps 483 | * jps -mlv 484 | * jstat 查看堆、GC情况 485 | * jinfo 查看和修改jvm参数(只是某些jvm参数) 486 | * jmap 生成堆,实例统计信息,classloader信息,Finalizer队列 487 | * jmap -histo \ 488 | * jmap -dump:format=b,file=c:\heap.hprof \ 489 | * jhat 堆分析,分析jmap的dump文件,自动开启http服务网页查看 490 | * 支持OOL查询语句 491 | * jstack 线程堆栈分析(线程状态,死锁等) 492 | * jstatd 远程主机信息收集(RMI服务端程序) 493 | * jcmd 基本上就是上面命令的合计 494 | * jcmd 6356 列出可以执行的命令 495 | * jcmd 6356 GC.run 执行gc 496 | * jcmd 6356 GC.run 497 | * jcmd 6356 GC.class_histogram 498 | * hprof 性能统计工具 499 | 500 | ## jconsole 图形化工具 501 | 502 | ## visual vm 503 | 504 | ## bttrace 505 | 506 | 不停机情况下插入字节码。可以在 **visual vm** 的 **插件** 里面使用。 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | --- 516 | --- 517 | --- 518 | # 分析 java 堆 519 | 520 | 521 | ## OOM类型 522 | 523 | * Java Heap 溢出(Java heap spacess) 524 | * 虚拟机栈和本地方法栈溢出(StackOverflowError) 525 | * 运行时永久区溢出(PermGen space),如常量池等 526 | * 方法区溢出 (PermGen space) 527 | * 线程过多(unable to creat new native thread) 528 | * GC效率低下 (GC overhead limit exceeded) 529 | * 直接内存内存溢出 (Direct buffer memory) 530 | * 数组过大 (Requested array size exceeds VM limit)>=Integer.MAX_VALUE-1时 531 | 532 | ## string 虚拟机中实现 533 | 534 | * 不变性 535 | * intern 536 | 537 | jdk6存在内存泄漏,string的长度和value无关。jdk7已解决。 538 | 539 | ### string常量池 540 | 541 | jdk6之前,属于永久区。jdk7后,移到了堆中。 542 | 543 | ```java 544 | public static void main(String[] args) { 545 | List list = new ArrayList(); 546 | 547 | int i = 0; 548 | 549 | while(true){ 550 | list.add(("dd".repeat(1000)+String.valueOf(i++)).intern()); 551 | } 552 | } 553 | ``` 554 | 555 | * jdk6报: OOM:permgen space 556 | * JDK7后报:java.lang.OutOfMemoryError: Java heap space 557 | 558 | 559 | --- 560 | --- 561 | --- 562 | # 锁和并发 563 | 564 | ## 对象头和锁 565 | 566 | ![](img-jvm/mark-word.png) 567 | 568 | > biased - adj.偏向;偏重;有偏见的;倾向性的 569 | 570 | 锁对象头上会记录获取锁的线程,和锁的姿态。 571 | 572 | ![](img-jvm/lock.jpg) 573 | 574 | ## 完整过程 575 | 576 | **锁的转换** 577 | 578 | ![](img-jvm/lock-1.jpg) 579 | 580 | **重量级锁** 581 | 582 | ![](img-jvm/lock-2.jpg) 583 | 584 | 585 | ## 偏向锁 586 | 587 | jdk1.6 588 | 589 | > +XX:+UseBiasedLocking 启用偏向锁 590 | > +XX:BiasedLockingStartupDelay=0 虚拟机默认4秒后启动偏向锁,可以通过设置马上启动。 591 | 592 | 锁被某个线程获取之后,进入了偏向锁模式。线程再次请求的时候,无需再次进行同步,节省时间。如果有其他线程请求锁,则退出偏向锁模式。 593 | 594 | > 竞争激励的场景,偏向锁反而会因为频繁切换影响性能,可以使用jvm参数关掉。 595 | 596 | ## 轻量级锁 597 | 598 | 如果偏向锁**失败**,虚拟机会让线程申请轻量级锁。虚拟机内部,使用 **BasicObjectLock** 的对象实现。这个对象有一个**BasicLock对象**和一个**持有该锁的Java对象指针**组成。BasicObjectLock对象放置在java栈的**栈帧**中。BaseLock对象内部还维护着 **displaced_header** 字段,用于备份对象头部的**Mark Word**。 599 | 600 | ![](img-jvm/base-object-lock.png) 601 | 602 | ## 锁膨胀 603 | 604 | 如果轻量级锁失败,就会膨胀成重量级锁。 605 | 606 | ## 自旋锁 607 | 608 | 609 | ## 锁消除 610 | 611 | 不存在逸出的地方,编译的时候会自动去掉锁。 612 | 613 | > -XX:+DoEscapeAnalysis -XX:+EliminateLocks 614 | 615 | ## 锁优化 616 | 617 | * 减少锁持有时间 618 | * 锁粗化 (大量切换请求还不如粗化) 619 | * 减小锁粒度 620 | * 锁分离 621 | * **LinkedBlockingQueue** , 有 putLock 和 takeLock 2把 ReentrantLock 锁。 622 | 623 | LinkedBlockingQueue 代码片段: 624 | 625 | ```java 626 | /** Lock held by take, poll, etc */ 627 | private final ReentrantLock takeLock = new ReentrantLock(); 628 | 629 | /** Wait queue for waiting takes */ 630 | private final Condition notEmpty = takeLock.newCondition(); 631 | 632 | /** Lock held by put, offer, etc */ 633 | private final ReentrantLock putLock = new ReentrantLock(); 634 | 635 | /** Wait queue for waiting puts */ 636 | private final Condition notFull = putLock.newCondition(); 637 | ``` 638 | 639 | ## 无锁 640 | 641 | ### CAS (Compare And Swap) 642 | 643 | ### LongAdder(jdk8) 644 | 645 | 实质是想通过多个原子锁,来替代单一锁,减少多线程对单一锁的竞争,提高并发写的能力。distributed-cache-line-counter-scalable、LongAddr、ConcurrentHashMap都是这种思想。 646 | 647 | LongAdder适合的场景是 **统计求和计数的场景**,而且LongAdder基本只提供了add方法,而AtomicLong还具有cas方法。 648 | 649 | ## 将随机变为可控:理解java内存模型 650 | 651 | ### 原子性 652 | 653 | 64位的long类型读写并非原子操作。需要定义为 **volatie** 即可解决。 654 | 655 | ## 有序性 656 | 657 | 三种地方可能会导致指令重排: 658 | 659 | * 编译器优化的重排序 660 | * 指令级并行的重排序(指令级并行技术(Instruction-Level 661 | Parallelism, ILP)来将多条指令重叠执行) 662 | * 内存系统的重排序(处理器使用缓存和读/写缓冲区) 663 | 664 | 关键字: 665 | 666 | * as-if-serial语义: 不管怎么重排序(编译器和处理器为了提高并行度), 667 | (单线程)程序的执行结果不能被改变 668 | * happens- before程序顺序规则 669 | 670 | 671 | ## 可见性 672 | 673 | --- 674 | --- 675 | --- 676 | # Class 文件结构 677 | 678 | 679 | ![Class 文件结构](img-jvm/class.png) 680 | 681 | 682 | Class文件格式 683 | 684 | Class文件格式ClassFile结构体的C语言描述如下: 685 | 686 | ```C 687 | struct ClassFile 688 | { 689 | u4 magic; // 识别Class文件格式,具体值为0xCAFEBABE, 690 | u2 minor_version; // Class文件格式副版本号, 691 | u2 major_version; // Class文件格式主版本号, 692 | u2 constant_pool_count; // 常数表项个数, 693 | cp_info **constant_pool; // 常数表,又称变长符号表, 694 | u2 access_flags; // Class的声明中使用的修饰符掩码, 695 | u2 this_class; // 常数表索引,索引内保存类名或接口名, 696 | u2 super_class; // 常数表索引,索引内保存父类名, 697 | u2 interfaces_count; // 超接口个数, 698 | u2 *interfaces; // 常数表索引,各超接口名称, 699 | u2 fields_count; // 类的域个数, 700 | field_info **fields; // 域数据,包括属性名称索引,域修饰符掩码等, 701 | u2 methods_count; // 方法个数, 702 | method_info **methods; // 方法数据,包括方法名称索引,方法修饰符掩码等, 703 | u2 attributes_count; // 类附加属性个数, 704 | attribute_info **attributes; // 类附加属性数据,包括源文件名等。 705 | }; 706 | ``` 707 | 708 | 709 | 其中 u2 为 unsigned short,u4 为 unsigned long: 710 | 711 | ```c 712 | typedef unsigned char u1; 713 | typedef unsigned short u2; 714 | typedef unsigned long u4; 715 | ``` 716 | 717 | 718 | cp_info **constant_pool 是常量表的指针数组,指针数组个数为 constant_pool_count,结构体cp_info为 719 | 720 | >注意:各种常量结构不同 721 | 722 | ```c 723 | // 类 724 | struct constant_class_info 725 | { 726 | u1 tag; // 常数表数据类型 727 | u2 name_index; // 常数池中索引 728 | }; 729 | 730 | // utf8字符串 731 | struct constant_utf8_info 732 | { 733 | u1 tag; // 常数表数据类型 734 | u2 length; // 长度 735 | u1 bytes[length]; // 数据 736 | }; 737 | ``` 738 | 739 | 740 | ## 重点看看方法结构 741 | 742 | ![](img-jvm/class-method.png) 743 | 744 | ### line-number-table 745 | 746 | ![](img-jvm/class-line-table.png) 747 | 748 | ### exception-table 749 | 750 | ![](img-jvm/class-exception-table.png) 751 | 752 | 753 | ## 强大的动态调用 - BootstrapMethods属性 754 | 755 | lambda表达式的实现 756 | 757 | 758 | --- 759 | --- 760 | --- 761 | # Class 装载系统 762 | 763 | ## 装载流程 764 | 765 | ![](img-jvm/class-load.png) 766 | 767 | ### 类装载条件 768 | 769 | >参考: [Java 类主动引用和被动引用](https://blog.csdn.net/qq_33314107/article/details/79109524) 770 | 771 | 必须使用的时候才装载。使用分主动和被动。 772 | 773 | **主动**: 774 | 775 | * 遇到new、getstatic、putstatic、invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化 776 | * new = new 对象 777 | * invokestatic = 调用类的静态方法 778 | * getstatic、putstatic = 调用类中的静态成员,除了final字段 779 | * 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。 780 | * 当初始化一个类的时候,如果发现其父类还没进行过初始化,则需要先触发其父类的初始化。 781 | * 当虚拟机启动时,用户需要指定一个要执行的主类(main方法),虚拟机会先初始化这个主类。 782 | 783 | **被动使用**:(看上去会,其实不会发生初始化) 784 | 785 | * 通过子类引用父类的静态字段,不会导致子类初始化 786 | * 通过数组定义类引用类,不会触发此类的初始化 787 | * 常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化 788 | 789 | ### 加载类 790 | 791 | **过程**: 792 | 793 | 1. 通过类全名,获取类的二进制数据流 794 | 1. 一般为读入class后缀文件 795 | 2. 也可以读入zip,jar文件 796 | 3. 数据库,http链接都可以 797 | 2. 解析为方法区内的数据结构 798 | 3. 创建java.lang.Class类的实例,表示该类型 799 | 800 | 801 | ### 验证 802 | 803 | ![](img-jvm/class-load-check.png) 804 | 805 | ### 准备 806 | 807 | 分配空间,并设置初始值。 808 | 809 | >java并不支持boolean类型,内部实现是int,int的默认值是0,boolean默认值就是false。 810 | 811 | 如果类存在常量字段,会被赋上正确的值。这个赋值属于java虚拟机行为,属于变量的初始化。(就是class信息上能看到)**准备阶段,不会有任何java代码被执行**。 812 | 813 | ### 解析类 814 | 815 | 将类、接口、字段和方法的**符号引用**转换为**直接引用**。 816 | 817 | 符号引用:就是一些字面量的引用,和虚拟机的内部数据结构和内存布局无关。class类文件中,通过**常量池**进行了大量的符号引用。 818 | 819 | ![](img-jvm/class-prase.png) 820 | 821 | > string的intern方法,intern是 **拘留** 的意思。 822 | > 823 | > intern : 1. 实习生 2. 拘留 824 | 825 | 826 | ### 初始化 827 | 828 | 执行java字节码,重要工作就是执行类的初始化方法 **\** (class init),该方法由编译器自动生成,由类的 **静态成员的赋值语句** 以及 **static语句块** 合并产生的。 829 | 830 | ![](img-jvm/class-init.png) 831 | 832 | 833 | **不一定会有clinit方法**,如果只有静态的常量,就不会有。如果是变量,会有。 834 | 835 | clinit 方法是**带锁线程安全**的(但方法上并没有synchronize关键字),多线程初始化的时候,可能引起**死锁**! 836 | 837 | ### 使用 838 | 839 | ### 卸载 840 | 841 | ## ClassLoader 842 | 843 | ClassLoader负责类的加载(通过各种方式将class的二进制数据流读入系统),然后交给java虚拟机进行连接、初始化等操作,只能影响类的加载,无法改变类的连接和初始化行为。 844 | 845 | ### 4个重要方法 846 | 847 | ```java 848 | 849 | public Class loadClass(String name) throws ClassNotFoundException { 850 | return loadClass(name, false); 851 | } 852 | 853 | @Deprecated(since="1.1") 854 | protected final Class defineClass(byte[] b, int off, int len) 855 | throws ClassFormatError 856 | 857 | 858 | protected Class findClass(String name) throws ClassNotFoundException { 859 | throw new ClassNotFoundException(name); 860 | } 861 | 862 | protected final Class findLoadedClass(String name) { 863 | if (!checkName(name)) 864 | return null; 865 | return findLoadedClass0(name); 866 | } 867 | ``` 868 | 869 | ### ClassLoader的分类 870 | 871 | 虚拟机创建了3类 872 | * BootStrap ClassLoader 启动类加载器 873 | * Extension ClassLoader 扩展类加载器 874 | * App ClassLoader 应用类加载器(也叫系统类加载器 System ClassLoader) 875 | 876 | ![](img-jvm/classloader-main-2.gif) 877 | 878 | 层次结构: 879 | 880 | ![](img-jvm/classloader-main.jpg) 881 | 882 | 883 | BootStrap ClassLoader 无法获取到实例,是系统级纯C实现的。所以ExtClassLoader的getParent为null。 884 | 885 | ### 双亲委托模式 886 | 887 | **往上查找,往下加载** 888 | 889 | ![](img-jvm/classloader-double-parent.png) 890 | 891 | >-Xbootclasspath可以把指定目录加到启动的classpath中。那么这里的类就会由启动类加载器加载。但是,如果这个类没有使用过,bootstrap ClassLoader不会主动加载,你可以在自定义的ClassLoader加载。 892 | 893 | ```java 894 | /** 895 | * 使用-Xbootclasspath可以把包含 另外一份HelloLoader.class 的目录加到启动的classpath中。 896 | * 理论上应该由bootstrap ClassLoader加载。 897 | * 但我们可以先在自己的app loader里面加载他。(必须在使用他之前) 898 | */ 899 | public class FindClassLoader { 900 | 901 | public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { 902 | ClassLoader loader = FindClassLoader.class.getClassLoader(); 903 | 904 | byte[] bytes = loadClassBytes("cn.xiaowenjie.classloader.HelloLoader"); 905 | 906 | Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 907 | 908 | defineClass.setAccessible(true); 909 | defineClass.invoke(loader, bytes, 0, bytes.length); 910 | defineClass.setAccessible(false); 911 | 912 | HelloLoader helloLoader = new HelloLoader(); 913 | System.out.println(helloLoader.getClass().getClassLoader()); 914 | helloLoader.print(); 915 | } 916 | 917 | private static byte[] loadClassBytes(String s) { 918 | } 919 | } 920 | ``` 921 | 922 | > 判断类是否加载时候,应用类加载器会顺着双亲路径往上判断,直到启动类加载器。但是启动类加载器不会往下询问,这个委托路线是**单向**的。 923 | 924 | 925 | ### 双亲委托模式弊端 926 | 927 | **原因**:检查类是否加载的委托过程是单向的,顶层ClassLoader**无法访问**底层的ClassLoader。应用类访问系统类没有问题,但系统类访问应用类有问题。如接口定义和工厂方法在系统类里面,实现类在应用类里面,导致系统类ClassLoader加载的工厂方法无法创建由应用类加载器加载的接口实例。拥有这种问题的组件有很多,如 JDBC,Xml Parser等。 928 | 929 | 930 | 931 | ### 双亲委托模式的补充 932 | 933 | SPI:Service Provider Interface 934 | 935 | **线程上下文类加载器** 936 | 937 | Thread类中有两个方法 938 | ```java 939 | public ClassLoader getContextClassLoader()//获取线程中的上下文加载器 940 | public void setContextClassLoader(ClassLoader cl)//设置线程中的上下文加载器 941 | ``` 942 | 943 | 通过这两个方法,可以把一个ClassLoader置于一个线程的实例之中,使该ClassLoader成为一个相对共享的实例。这样即使是启动类加载器中的代码也可以通过这种方式访问应用类加载器中的类了 944 | 945 | ![](img-jvm/classloader-spi.png) 946 | 947 | 948 | [dubbo源码解析-spi(一)](https://www.saowen.com/a/a15dc94434a957289c16d08b0e551c68b27ad5f2926b29d9e2660faf06c148f7) 949 | 950 | [类加载器&线程上下文加载器](https://blog.csdn.net/anhuidelinger/article/details/53241967) 951 | 952 | 953 | 954 | ### 突破双亲委托模式 955 | 956 | 自定义ClassLoader,重载loadClass改变默认的双亲委托模式,先自己加载,加载不到在往上。 957 | 958 | ### 热替换 959 | 960 | > 不同的ClassLoader加载的同名类是不同类型,无法互相转换和兼容。 961 | 962 | 热加载就是重新加载,成为一个新的class对象。 963 | 964 | 965 | 966 | 967 | 968 | 969 | --- 970 | 971 | # 字节码执行 972 | 973 | 974 | 975 | ## Class字节码指令解释执行 976 | 977 | 978 | 979 | 980 | ```java 981 | package cn.xiaowenjie.bytecode; 982 | 983 | public class Calc { 984 | 985 | public int calc(){ 986 | int a = 100; 987 | int b = 200; 988 | int c = 300; 989 | return (a+b)/c; 990 | } 991 | } 992 | ``` 993 | 994 | **javap** 995 | ``` 996 | 用法: javap 997 | 其中, 可能的选项包括: 998 | -help --help -? 输出此用法消息 999 | -version 版本信息 1000 | -v -verbose 输出附加信息 1001 | -l 输出行号和本地变量表 1002 | -public 仅显示公共类和成员 1003 | -protected 显示受保护的/公共类和成员 1004 | -package 显示程序包/受保护的/公共类 1005 | 和成员 (默认) 1006 | -p -private 显示所有类和成员 1007 | -c 对代码进行反汇编 1008 | -s 输出内部类型签名 1009 | -sysinfo 显示正在处理的类的 1010 | 系统信息 (路径, 大小, 日期, MD5 散列) 1011 | -constants 显示最终常量 1012 | -classpath 指定查找用户类文件的位置 1013 | -cp 指定查找用户类文件的位置 1014 | -bootclasspath 覆盖引导类文件的位置 1015 | ``` 1016 | 1017 | 使用 `javap -v Calc.class` 查看: 1018 | 1019 | ``` 1020 | Classfile /D:/Github/xwjie/jvm/out/production/jvm/cn/xiaowenjie/bytecode/Calc.class 1021 | Last modified 2018-11-23; size 427 bytes 1022 | MD5 checksum 0edbd9bff1080c2535e9042539433146 1023 | Compiled from "Calc.java" 1024 | public class cn.xiaowenjie.bytecode.Calc 1025 | minor version: 0 1026 | major version: 55 1027 | flags: ACC_PUBLIC, ACC_SUPER 1028 | Constant pool: 1029 | #1 = Methodref #3.#19 // java/lang/Object."":()V 1030 | #2 = Class #20 // cn/xiaowenjie/bytecode/Calc 1031 | #3 = Class #21 // java/lang/Object 1032 | #4 = Utf8 1033 | #5 = Utf8 ()V 1034 | #6 = Utf8 Code 1035 | #7 = Utf8 LineNumberTable 1036 | #8 = Utf8 LocalVariableTable 1037 | #9 = Utf8 this 1038 | #10 = Utf8 Lcn/xiaowenjie/bytecode/Calc; 1039 | #11 = Utf8 calc 1040 | #12 = Utf8 ()I 1041 | #13 = Utf8 a 1042 | #14 = Utf8 I 1043 | #15 = Utf8 b 1044 | #16 = Utf8 c 1045 | #17 = Utf8 SourceFile 1046 | #18 = Utf8 Calc.java 1047 | #19 = NameAndType #4:#5 // "":()V 1048 | #20 = Utf8 cn/xiaowenjie/bytecode/Calc 1049 | #21 = Utf8 java/lang/Object 1050 | { 1051 | public cn.xiaowenjie.bytecode.Calc(); 1052 | descriptor: ()V 1053 | flags: ACC_PUBLIC 1054 | Code: 1055 | stack=1, locals=1, args_size=1 1056 | 0: aload_0 1057 | 1: invokespecial #1 // Method java/lang/Object."":()V 1058 | 4: return 1059 | LineNumberTable: 1060 | line 3: 0 1061 | LocalVariableTable: 1062 | Start Length Slot Name Signature 1063 | 0 5 0 this Lcn/xiaowenjie/bytecode/Calc; 1064 | 1065 | public int calc(); 1066 | descriptor: ()I 1067 | flags: ACC_PUBLIC 1068 | Code: 1069 | stack=2, locals=4, args_size=1 1070 | 0: bipush 100 1071 | 2: istore_1 1072 | 3: sipush 200 1073 | 6: istore_2 1074 | 7: sipush 300 1075 | 10: istore_3 1076 | 11: iload_1 1077 | 12: iload_2 1078 | 13: iadd 1079 | 14: iload_3 1080 | 15: idiv 1081 | 16: ireturn 1082 | LineNumberTable: 1083 | line 6: 0 1084 | line 7: 3 1085 | line 8: 7 1086 | line 9: 11 1087 | LocalVariableTable: 1088 | Start Length Slot Name Signature 1089 | 0 17 0 this Lcn/xiaowenjie/bytecode/Calc; 1090 | 3 14 1 a I 1091 | 7 10 2 b I 1092 | 11 6 3 c I 1093 | } 1094 | SourceFile: "Calc.java" 1095 | ``` 1096 | 1097 | 21个常量池 1098 | 1099 | 2个方法,第一个为自动生成的构造函数,自动生成的。第二个为我们编写的函数。方法体内,显示了栈大小,局部变量表大小,字节码指令,行号,局部变量表等信息。 1100 | 1101 | >stack=2, locals=4, args_size=1 1102 | 1103 | 字节码前面的数字表示字节码偏移量。 1104 | 1105 | > 0: bipush 100 1106 | 1107 | bipush 指令1个字节,接受一个1字节的参数,所以一共2字节,下面的指令从2位置开始。bipush能处理-128-127的数,第二变量b是200,所以需要用sipush指令,他支持-32768-3276,sipush 指令1个字节,接受一个双字节的参数,所以一共三字节、 1108 | 1109 | 第一条指令执行完, 操作数栈里面是100。 1110 | 1111 | ![](./img-jvm/codebyte-1.webp) 1112 | 1113 | > 2: istore_1 1114 | 1115 | 操作数栈弹出一个元素,存放到局部变量表index为1的位置。(0是this,非静态方法的时候) 1116 | 1117 | > 11: iload_1 \ 1118 | > 12: iload_2 \ 1119 | > 13: iadd 1120 | 1121 | iload_1: 把局部变量index为1的元素压入操作数栈。 1122 | 1123 | iload_2 执行完之后如下:(栈是先入的数在下面。) 1124 | 1125 | ![](./img-jvm/codebyte-2.webp) 1126 | 1127 | iadd:加法,操作数栈弹出2个数,然后做加法,结果在压回操作数栈。 1128 | 1129 | ![](./img-jvm/codebyte-3.webp) 1130 | 1131 | > 16: ireturn 1132 | 1133 | ireturn: 将当前函数操作数栈的顶层元素弹出,并将这个元素压入调用函数的操作数栈。操作数栈的其他元素会被丢弃。 1134 | 1135 | 详细看下面的贴 [Class字节码指令解释执行](https://www.jianshu.com/p/ab29c1cfdd81) 1136 | 1137 | 1138 | ## 常用指令 1139 | 1140 | i表示整数,l表示长整数,f表示浮点数,d表示双精度浮点数,a表示对象引用。 1141 | 1142 | ### 常量入栈指令 1143 | 1144 | * const系列 1145 | * iconst_m1: -1压入操作数栈 1146 | * iconst_x(x为0-5) 1147 | * lconst_x 压long 1148 | * dconst_x 压double 1149 | * fconst_x 压f 1150 | * push系列 1151 | * bipush 1152 | * sipush 1153 | * ldc指令 1154 | * 超过16位的,值会放到常量池,然后把常量池引用index传给ldc 1155 | 1156 | 测试代码: 1157 | 1158 | ```java 1159 | public void test(){ 1160 | // const 指令 => iconst_1 1161 | int a = 1; 1162 | 1163 | // bipush = > bipush 127 1164 | int b = 127; 1165 | 1166 | // sipush => sipush 128 1167 | int c = 128; 1168 | 1169 | // ldc 指令, 33333 会作为常量,常量的index会作为参数 1170 | // => ldc #2 // int 33333 1171 | int a2 = 33333; 1172 | 1173 | float f = 0f; 1174 | double d = 0d; 1175 | long l = 1L; 1176 | } 1177 | ``` 1178 | 1179 | 字节码: 1180 | 1181 | ``` 1182 | 0: iconst_1 1183 | 1: istore_1 1184 | 2: bipush 127 1185 | 4: istore_2 1186 | 5: sipush 128 1187 | 8: istore_3 1188 | 9: ldc #2 // int 33333 1189 | 11: istore 4 1190 | 13: fconst_0 1191 | 14: fstore 5 1192 | 16: dconst_0 1193 | 17: dstore 6 1194 | 19: lconst_1 1195 | 20: lstore 8 1196 | 22: return 1197 | ``` 1198 | 1199 | 常量池: 1200 | 1201 | ``` 1202 | Constant pool: 1203 | #1 = Methodref #4.#26 // java/lang/Object."":()V 1204 | #2 = Integer 33333 1205 | #3 = Class #27 // cn/xiaowenjie/bytecode/ConstDemo 1206 | #4 = Class #28 // java/lang/Object 1207 | ``` 1208 | 1209 | ### 局部变量压栈指令 1210 | 1211 | * (x)load 1212 | * (x)load_n 1213 | * (x)aload 1214 | 1215 | ### 出栈装入局部变量表指令 1216 | 1217 | store 1218 | 1219 | ### 通用型操作 1220 | 1221 | * nop 空指令 1222 | * dup 复制栈顶元素(new之后常带) 1223 | 1224 | ### 类型转换指令 1225 | 1226 | (x)2(y) 1227 | 1228 | ### 运算指令 1229 | 1230 | ### 对象/数组操作指令 1231 | 1232 | * 创建指令 1233 | * new 1234 | * newarray 1235 | * anewarray 1236 | * multianewarray 1237 | * 字段访问指令 1238 | * getfiled 1239 | * putfield 1240 | * getstatic 1241 | * putstatic 1242 | * 类型检查指令 1243 | * instanceof 1244 | * chechcast 就是强转 1245 | * 数组操作指令 1246 | * xastore 1247 | * xaload 1248 | 1249 | ### 比较控制指令 1250 | 1251 | * 比较指令 1252 | * (x)cmp(y) 1253 | * 条件跳转指令 1254 | * if(xx) 把栈顶元素弹出,测试是否满足条件,如果满足跳转给定位置 1255 | * 比较条件跳转指令 1256 | * if_(i)cmp(xx) 1257 | * 多条件分支跳转(针对switch-case) 1258 | * tableswitch(条件是连续的) 1259 | * lookupswith(条件不连续),jdk7的swith字符串,就是lookupswith hashcode 实现的。 1260 | * 无条件跳转 1261 | * goto 1262 | 1263 | ### 函数调用和返回 1264 | 1265 | * 函数调用 1266 | * invokevir 1267 | * invokeinterface 1268 | * invokespecial 1269 | * 特殊函数,如init函数 1270 | * 私有函数(没有动态,所以可以静态绑定) 1271 | * 父类方法 1272 | * invokestatic 1273 | * invokedynamic 1274 | * 1275 | * 返回指令 1276 | * (x)return 1277 | * void的时候就是return 1278 | 1279 | ### 同步控制 1280 | 1281 | * monitorenter/monitorexit 1282 | 1283 | 使用monitorenter指令请求进入,如果当前对象的监视器计数器为0,准许进入,若为1,判断是否当前线程,是则进入,否则进行等待。 1284 | 1285 | monitorenter/monitorexit 执行时,都需要在操作数栈顶压入对象,之后,monitorenter和monitorexit的锁定和释放都是针对这个对象的监视器进行的。 1286 | 1287 | synchronize加在**方法**上的时候,看不到对应的字节码,而是在方法修饰符上看到,虚拟机会自动加指令。 1288 | 1289 | ## ASM框架 1290 | 1291 | ## java agent 1292 | 1293 | 2个方法。 1294 | 1295 | ```java 1296 | public static void premain(String agentArgs, Instrumentation inst){ 1297 | inst.addTransformer(new ClassFileTransformer(){ 1298 | @Override 1299 | public byte[] transform( ClassLoader loader, 1300 | String className, 1301 | Class classBeingRedefined, 1302 | ProtectionDomain protectionDomain, 1303 | byte[] classfileBuffer) 1304 | throws IllegalClassFormatException { 1305 | return null; 1306 | } 1307 | }); 1308 | } 1309 | 1310 | 1311 | public static void agentmain(String agentArgs, Instrumentation inst){ 1312 | 1313 | } 1314 | ``` 1315 | 1316 | 1317 | ## 静态编译优化 1318 | 1319 | 优化分为2种: 1320 | 1321 | * 编译时优化(静态) 1322 | * 常量计算 1323 | * 数字计算 1324 | * 字符串拼接 1325 | * 变量字符串使用StringBuilder代替 1326 | * 基于常量条件语句裁剪 1327 | * if(FLAG) 1328 | * swith语句 1329 | * swith 1,2,5 生成 tableswitch语句,自动填充3,4 1330 | * jit(just-in-time)即时编译(运行时) 1331 | * 虚拟机3中执行方式, 1332 | * 解释执行(不做jit编译) 1333 | * java -Xint -version 开启 1334 | * 混合模式(默认) 1335 | * 超过阈值进行jit,-XX:CompileThreshold 1336 | * 编译执行(不管是否热点代码,都会编译执行) 1337 | * java -Xcomp -version 开启 1338 | * -Xint 1339 | 1340 | ### 多级编译器 1341 | 1342 | 虚拟机有2中编译系统: 1343 | 1344 | * 客户端编译器(C1) 1345 | * -client 参数 1346 | * 服务端编译器(C2) 1347 | * -server 参数 1348 | 1349 | 为了在C1和C2中平衡,分为0-4 5级。越高性能越好。使用 `-XX:+TieredCompilation -server -Xcomp` 参数 1350 | 1351 | ### OSR 栈上替换 1352 | 1353 | ### 方法内嵌 1354 | 1355 | > -XX:+InLine 开启内嵌\ 1356 | > -XX:FreqInLineSize 多小的函数可以内嵌 1357 | 1358 | ### 设置代码缓存大小 1359 | 1360 | --- 1361 | End 1362 | 1363 | 1364 | -------------------------------------------------------------------------------- /img-jvm/TLAB.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/TLAB.jpg -------------------------------------------------------------------------------- /img-jvm/base-object-lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/base-object-lock.png -------------------------------------------------------------------------------- /img-jvm/basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/basic.png -------------------------------------------------------------------------------- /img-jvm/card-table-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/card-table-2.png -------------------------------------------------------------------------------- /img-jvm/card-table-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/card-table-3.png -------------------------------------------------------------------------------- /img-jvm/card-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/card-table.png -------------------------------------------------------------------------------- /img-jvm/class-exception-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/class-exception-table.png -------------------------------------------------------------------------------- /img-jvm/class-init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/class-init.png -------------------------------------------------------------------------------- /img-jvm/class-line-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/class-line-table.png -------------------------------------------------------------------------------- /img-jvm/class-load-check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/class-load-check.png -------------------------------------------------------------------------------- /img-jvm/class-load.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/class-load.png -------------------------------------------------------------------------------- /img-jvm/class-method.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/class-method.png -------------------------------------------------------------------------------- /img-jvm/class-prase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/class-prase.png -------------------------------------------------------------------------------- /img-jvm/class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/class.png -------------------------------------------------------------------------------- /img-jvm/classloader-double-parent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/classloader-double-parent.png -------------------------------------------------------------------------------- /img-jvm/classloader-main-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/classloader-main-2.gif -------------------------------------------------------------------------------- /img-jvm/classloader-main.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/classloader-main.jpg -------------------------------------------------------------------------------- /img-jvm/classloader-spi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/classloader-spi.png -------------------------------------------------------------------------------- /img-jvm/cms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/cms.png -------------------------------------------------------------------------------- /img-jvm/codebyte-1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/codebyte-1.webp -------------------------------------------------------------------------------- /img-jvm/codebyte-2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/codebyte-2.webp -------------------------------------------------------------------------------- /img-jvm/codebyte-3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/codebyte-3.webp -------------------------------------------------------------------------------- /img-jvm/exception-table-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/exception-table-1.png -------------------------------------------------------------------------------- /img-jvm/exception-table-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/exception-table-2.png -------------------------------------------------------------------------------- /img-jvm/g1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/g1.png -------------------------------------------------------------------------------- /img-jvm/heap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/heap.jpg -------------------------------------------------------------------------------- /img-jvm/jvmargs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/jvmargs.jpg -------------------------------------------------------------------------------- /img-jvm/lock-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/lock-1.jpg -------------------------------------------------------------------------------- /img-jvm/lock-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/lock-2.jpg -------------------------------------------------------------------------------- /img-jvm/lock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/lock.jpg -------------------------------------------------------------------------------- /img-jvm/mark-word.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/mark-word.png -------------------------------------------------------------------------------- /img-jvm/stack-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/stack-1.png -------------------------------------------------------------------------------- /img-jvm/stack-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/stack-2.png -------------------------------------------------------------------------------- /img-jvm/stack-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/jvm/23165369a5bb2b4b5ddce97af2f6a2ad9d57cfb6/img-jvm/stack-3.png -------------------------------------------------------------------------------- /jvm.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /maven-source/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | 15 | [*.yml] 16 | indent_size = 2 17 | 18 | [*.sh] 19 | end_of_line = lf 20 | -------------------------------------------------------------------------------- /maven-source/.gitattributes: -------------------------------------------------------------------------------- 1 | # When shell scripts end in CRLF, bash gives a cryptic error message 2 | *.sh text eol=lf 3 | -------------------------------------------------------------------------------- /maven-source/.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # Standard Maven .gitignore 3 | # 4 | target/ 5 | pom.xml.tag 6 | pom.xml.releaseBackup 7 | pom.xml.versionsBackup 8 | pom.xml.next 9 | release.properties 10 | dependency-reduced-pom.xml 11 | buildNumber.properties 12 | .mvn/timing.properties 13 | 14 | # 15 | # IntelliJ 16 | # 17 | *.iml 18 | .idea/* 19 | !.idea/runConfigurations/ 20 | 21 | # 22 | # Visual Studio Code 23 | # 24 | .settings/ 25 | .classpath 26 | .project 27 | .vscode/ 28 | -------------------------------------------------------------------------------- /maven-source/.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: oraclejdk8 3 | after_success: 4 | - mvn coveralls:report 5 | -------------------------------------------------------------------------------- /maven-source/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | cn.xiaowenjie 5 | jvm 6 | 1.0-SNAPSHOT 7 | 8 | 1.8 9 | 1.8 10 | UTF-8 11 | 12 | 13 | 14 | junit 15 | junit 16 | 4.12 17 | test 18 | 19 | 20 | 21 | 22 | com.google.guava 23 | guava 24 | 27.0.1-jre 25 | 26 | 27 | 28 | 29 | com.typesafe.akka 30 | akka-actor_2.12 31 | 2.5.19 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-checkstyle-plugin 42 | 3.0.0 43 | 44 | 45 | com.puppycrawl.tools 46 | checkstyle 47 | 8.10 48 | 49 | 50 | com.github.ngeor 51 | checkstyle-rules 52 | 1.1.0 53 | 54 | 55 | 56 | com/github/ngeor/checkstyle.xml 57 | true 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-checkstyle-plugin 66 | 67 | 68 | 72 | 73 | org.jacoco 74 | jacoco-maven-plugin 75 | 0.8.1 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-javadoc-plugin 84 | 3.0.0 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-checkstyle-plugin 89 | 90 | com/github/ngeor/checkstyle.xml 91 | true 92 | 93 | 94 | 95 | 96 | 97 | 98 | 103 | 104 | jacoco 105 | 106 | 107 | env.TRAVIS 108 | 109 | 110 | 111 | 112 | 113 | org.jacoco 114 | jacoco-maven-plugin 115 | 116 | 117 | prepare-agent 118 | validate 119 | 120 | prepare-agent 121 | 122 | 123 | 124 | report 125 | test 126 | 127 | report 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 141 | 142 | travis 143 | 144 | 145 | env.TRAVIS 146 | 147 | 148 | 149 | 150 | 151 | org.apache.maven.plugins 152 | maven-checkstyle-plugin 153 | 154 | 155 | checkstyle 156 | test 157 | 158 | check 159 | 160 | 161 | 162 | 163 | 164 | org.eluder.coveralls 165 | coveralls-maven-plugin 166 | 4.3.0 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/App.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | /** 4 | * Hello world! 5 | */ 6 | public final class App { 7 | private App() { 8 | } 9 | 10 | /** 11 | * Says hello to the world. 12 | * @param args The arguments of the program. 13 | */ 14 | public static void main(String[] args) { 15 | System.out.println("Hello World!"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/ClassStuct.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | public class ClassStuct { 4 | public static int varStatic = 999; 5 | 6 | public static int varStatic2; 7 | 8 | static { 9 | varStatic2 = 1111; 10 | } 11 | 12 | 13 | 14 | 15 | 16 | 17 | public int varLocal = 888; 18 | 19 | public static void main(String[] args) throws Exception { 20 | System.out.println(varStatic); 21 | System.out.println(methodStatic(true)); 22 | 23 | ClassStuct cs = new ClassStuct(); 24 | System.out.println(cs.varLocal); 25 | System.out.println(cs.methodLocal(true, (byte)'a')); 26 | } 27 | 28 | public static int methodStatic(boolean flag){ 29 | return 1; 30 | } 31 | 32 | 33 | public synchronized int methodLocal(boolean flag, byte b) throws Exception{ 34 | System.out.println(b); 35 | try { 36 | return 1; 37 | } 38 | catch (Exception e){ 39 | return 2; 40 | } 41 | finally { 42 | return 3; 43 | } 44 | } 45 | } 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/FinallyDemo.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import java.util.concurrent.FutureTask; 4 | 5 | public class FinallyDemo { 6 | public static void main(String[] args) { 7 | System.out.println(testNoException()); // 3 , finally 8 | System.out.println(testWithException()); // 3 , finally 9 | } 10 | 11 | public static int testNoException(){ 12 | try{ 13 | System.out.println("====1"); 14 | byte[] b = new byte[2]; 15 | return 1; 16 | } 17 | catch (Throwable e){ 18 | System.out.println("====2"); 19 | e.printStackTrace(); 20 | return 2; 21 | } 22 | finally { 23 | System.out.println("====3"); 24 | return 3; 25 | } 26 | } 27 | 28 | public static int testWithException(){ 29 | try{ 30 | System.out.println("----1"); 31 | byte[] b = new byte[Integer.MAX_VALUE-1]; 32 | 33 | return 1; 34 | } 35 | catch (Throwable e){ 36 | System.out.println("----2"); 37 | e.printStackTrace(); 38 | return 2; 39 | } 40 | finally { 41 | System.out.println("----3"); 42 | return 3; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/GuavaFutureDemo.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.concurrent.ExecutionException; 5 | import java.util.concurrent.Executors; 6 | 7 | import com.google.common.util.concurrent.FutureCallback; 8 | import com.google.common.util.concurrent.Futures; 9 | import com.google.common.util.concurrent.ListeningExecutorService; 10 | import com.google.common.util.concurrent.MoreExecutors; 11 | 12 | public class GuavaFutureDemo { 13 | 14 | public static void main(String[] args) { 15 | ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); 16 | com.google.common.util.concurrent.ListenableFuture task = service.submit(new Callable() { 17 | public Object call() { 18 | return "回调参数success"; 19 | } 20 | }); 21 | 22 | task.addListener(() -> { 23 | try { 24 | System.out.println("done," + task.get()); 25 | } catch (InterruptedException e) { 26 | e.printStackTrace(); 27 | } catch (ExecutionException e) { 28 | e.printStackTrace(); 29 | } 30 | }, MoreExecutors.directExecutor()); 31 | 32 | 33 | Futures.addCallback(task, new FutureCallback() { 34 | @Override 35 | public void onSuccess(Object result) { 36 | System.out.println("result = " + result); 37 | } 38 | 39 | @Override 40 | public void onFailure(Throwable t) { 41 | System.out.println("t = " + t); 42 | } 43 | }, MoreExecutors.directExecutor()); // MoreExecutors.newDirectExecutorService() 44 | } 45 | } -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/IEEE754.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | public class IEEE754 { 4 | public static void main(String[] args) { 5 | float f = -5; 6 | 7 | // float的内部表示 8 | System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f))); 9 | 10 | 11 | double d = -5; 12 | 13 | // double 的内部表示 14 | System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d))); 15 | 16 | System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(Float.MIN_NORMAL))); 17 | System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(Double.MIN_NORMAL))); 18 | 19 | // 浮点数的大小比较为什么不能用等号? 20 | { 21 | float f1 = 0.1f; 22 | float f2 = 0.1f; 23 | 24 | System.out.println(f1 == f2); // true 25 | 26 | // 默认是 double 27 | System.out.println(15.1 * 100 + 0.9 * 100 == 16.0 * 100); //true 28 | 29 | // 默认是 double 30 | System.out.println(16.1 * 100 + 0.9 * 100 == 17.0 * 100); //false 31 | 32 | // float 33 | System.out.println(16.1f * 100 + 0.9f * 100 == 17.0f * 100); //true 34 | 35 | System.out.println(Float.compare(16.1f * 100 + 0.9f * 100, 17.0f * 100)); //0 36 | 37 | System.out.println((16.1 + 0.9) * 100 == 17.0 * 100); // true 38 | } 39 | 40 | { 41 | float f1 = 1f / 3; 42 | float f2 = 1f / 3; 43 | System.out.println(f1 == f2); // true 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/LocalVarAndGC.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | public class LocalVarAndGC { 4 | 5 | public static void main(String[] args) { 6 | System.out.println("-XX:+PrintGC/ PrintGCDetails"); 7 | System.out.println("--------------1"); 8 | gc(); 9 | System.out.println("-------------2"); 10 | gc2(); 11 | System.out.println("-------------3"); 12 | } 13 | 14 | public static void gc(){ 15 | { 16 | byte[] a = new byte[6*1024*1024]; 17 | } 18 | 19 | // a 虽然失效,但仍然在局部变量表,无法gc 20 | System.gc(); 21 | } 22 | 23 | public static void gc2(){ 24 | { 25 | byte[] a = new byte[6*1024*1024]; 26 | } 27 | 28 | int c = 1; 29 | 30 | // a 失效,槽位重用,a已经不存在,可以回收 31 | System.gc(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/Main.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | public class Main { 4 | 5 | public static void main(String[] args) { 6 | // write your code here 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/SemaphorePVDemo.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import java.util.concurrent.Semaphore; 4 | import java.util.concurrent.ThreadLocalRandom; 5 | 6 | /** 7 | * 利用semaphore的先V(release)再P(acquire)操作,实现线程的前置依赖关系。 8 | * semaphore的容量是0 9 | */ 10 | public class SemaphorePVDemo { 11 | 12 | 13 | public static void main(String[] args) { 14 | Semaphore semaphore1 = new Semaphore(0); 15 | Semaphore semaphore2 = new Semaphore(0); 16 | 17 | new Thread(()->{ 18 | try { 19 | Thread.sleep(ThreadLocalRandom.current().nextInt(5000)); 20 | } catch (InterruptedException e) { 21 | e.printStackTrace(); 22 | } 23 | System.out.println("线程1最先工作"); 24 | semaphore1.release(); 25 | }).start(); 26 | 27 | new Thread(()->{ 28 | try { 29 | semaphore1.acquire(); 30 | Thread.sleep(ThreadLocalRandom.current().nextInt(5000)); 31 | } catch (InterruptedException e) { 32 | e.printStackTrace(); 33 | } 34 | System.out.println("线程2然后工作"); 35 | semaphore2.release(); 36 | }).start(); 37 | 38 | new Thread(()->{ 39 | try { 40 | semaphore2.acquire(); 41 | Thread.sleep(ThreadLocalRandom.current().nextInt(5000)); 42 | } catch (InterruptedException e) { 43 | e.printStackTrace(); 44 | } 45 | System.out.println("线程3最后工作"); 46 | }).start(); 47 | 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/StampedLockDemo.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | import java.util.concurrent.locks.StampedLock; 5 | 6 | public class StampedLockDemo { 7 | private double x, y; 8 | 9 | private final StampedLock sl = new StampedLock(); 10 | 11 | // an exclusively locked method 12 | void move(double deltaX, double deltaY) { 13 | long stamp = sl.writeLock(); 14 | try { 15 | x += deltaX; 16 | y += deltaY; 17 | } finally { 18 | sl.unlockWrite(stamp); 19 | } 20 | } 21 | 22 | // 下面看看乐观读锁案例 23 | // A read-only method 24 | double distanceFromOrigin() { 25 | // 获得一个乐观读锁 26 | long stamp = sl.tryOptimisticRead(); 27 | 28 | // 将两个字段读入本地局部变量 29 | double currentX = x, currentY = y; 30 | 31 | // 检查发出乐观读锁后同时是否有其他写锁发生? 32 | if (!sl.validate(stamp)) { 33 | 34 | // 如果没有,我们再次获得一个读悲观锁 35 | stamp = sl.readLock(); 36 | 37 | try { 38 | currentX = x; // 将两个字段读入本地局部变量 39 | currentY = y; // 将两个字段读入本地局部变量 40 | } finally { 41 | sl.unlockRead(stamp); 42 | } 43 | } 44 | return Math.sqrt(currentX * currentX + currentY * currentY); 45 | } 46 | 47 | // 下面是悲观读锁案例 48 | // upgrade 49 | void moveIfAtOrigin(double newX, double newY) { 50 | // Could instead start with optimistic, not read mode 51 | long stamp = sl.readLock(); 52 | 53 | try { 54 | // 循环,检查当前状态是否符合 55 | while (x == 0.0 && y == 0.0) { 56 | 57 | // 将读锁转为写锁 58 | long ws = sl.tryConvertToWriteLock(stamp); 59 | 60 | // 这是确认转为写锁是否成功 61 | if (ws != 0L) { 62 | stamp = ws; // 如果成功 替换票据 63 | x = newX; // 进行状态改变 64 | y = newY; // 进行状态改变 65 | break; 66 | } 67 | // 如果不能成功转换为写锁 68 | else { 69 | // 我们显式释放读锁 70 | sl.unlockRead(stamp); 71 | 72 | // 显式直接进行写锁 然后再通过循环再试 73 | stamp = sl.writeLock(); 74 | } 75 | } 76 | } finally { 77 | sl.unlock(stamp); // 释放读锁或写锁 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/StringInternDemo.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class StringInternDemo { 7 | public static void main(String[] args) { 8 | List list = new ArrayList(); 9 | 10 | int i = 0; 11 | 12 | while(true){ 13 | list.add(("dd".repeat(1000)+String.valueOf(i++)).intern()); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/TestStockDeep.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | public class TestStockDeep { 4 | 5 | public static int count=0; 6 | 7 | public static void recursion(int i1, float f1, long l1, double d1){ 8 | count++; 9 | 10 | // 局部变量 11 | int i2=1; // 1 word = 32bit 12 | float f2 = 2f; // 1 word 13 | long l2 = 3L; // 2 word 14 | double d2 = 4; // 2 word 15 | 16 | Object o1= null; // 1 word 17 | Object o2= null; // 1 word 18 | 19 | recursion(i1,f1,l1, d1); 20 | } 21 | 22 | public static void main(String[] args) { 23 | // Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit 24 | byte[] bs = new byte[Integer.MAX_VALUE-1]; 25 | 26 | try { 27 | recursion(1,2,3,4); 28 | } 29 | catch (Throwable e){ 30 | System.out.println(count); 31 | e.printStackTrace(); 32 | } 33 | } 34 | 35 | // 复用槽位 36 | public static void recursion2(int i1){ 37 | { 38 | int i2 = i1; 39 | System.out.println(i2); 40 | } 41 | 42 | int i3 = 1; 43 | } 44 | 45 | // 局部变量表是4 word 46 | public static void recursion3(double d1){ 47 | { 48 | double d2 = d1; // 2 word 49 | System.out.println(d2); 50 | } 51 | 52 | int i3 = 1; // 1 word 复用 d2的第一个1槽位 53 | int i4 = 2; // 1 word 复用 d2的第二个操作 54 | } 55 | 56 | 57 | 58 | 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/ThreadWaitNotify.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | public class ThreadWaitNotify { 4 | 5 | public final static Object monitor = new Object(); 6 | 7 | public static void main(String[] args) { 8 | System.out.println("vs code"); 9 | 10 | Thread t1 = new Thread(() -> { 11 | synchronized (monitor) { 12 | System.out.println(System.currentTimeMillis() + " t1 start"); 13 | try { 14 | System.out.println(System.currentTimeMillis() + " get monitor, wait"); 15 | monitor.wait(); 16 | } catch (InterruptedException e) { 17 | e.printStackTrace(); 18 | } 19 | } 20 | 21 | System.out.println(System.currentTimeMillis() + " t1 end."); 22 | }); 23 | 24 | Thread t2 = new Thread(() -> { 25 | System.out.println(System.currentTimeMillis() + " t2 start"); 26 | 27 | synchronized (monitor) { 28 | System.out.println(System.currentTimeMillis() + " get monitor, notify"); 29 | monitor.notify(); 30 | System.out.println(System.currentTimeMillis() + " get monitor, notify end"); 31 | 32 | try { 33 | Thread.sleep(2000); 34 | } catch (InterruptedException e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | 39 | // FIXME : synchronized exit, t1 can run; 40 | 41 | System.out.println(System.currentTimeMillis() + " t2 end."); 42 | }); 43 | 44 | t1.start(); 45 | t2.start(); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/akka/sample/AkkaQuickstart.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.akka.sample; 2 | 3 | import java.io.IOException; 4 | 5 | import akka.actor.ActorRef; 6 | import akka.actor.ActorSystem; 7 | import cn.xiaowenjie.akka.sample.Greeter.Greet; 8 | import cn.xiaowenjie.akka.sample.Greeter.WhoToGreet; 9 | 10 | public class AkkaQuickstart { 11 | public static void main(String[] args) { 12 | 13 | final ActorSystem system = ActorSystem.create("helloakka"); 14 | 15 | try { 16 | // #create-actors 17 | final ActorRef printerActor = system.actorOf(Printer.props(), "printerActor"); 18 | final ActorRef howdyGreeter = system.actorOf(Greeter.props("Howdy", printerActor), "howdyGreeter"); 19 | final ActorRef helloGreeter = system.actorOf(Greeter.props("Hello", printerActor), "helloGreeter"); 20 | final ActorRef goodDayGreeter = system.actorOf(Greeter.props("Good day", printerActor), "goodDayGreeter"); 21 | // #create-actors 22 | 23 | // #main-send-messages 24 | howdyGreeter.tell(new WhoToGreet("Akka"), ActorRef.noSender()); 25 | howdyGreeter.tell(new Greet(), ActorRef.noSender()); 26 | 27 | howdyGreeter.tell(new WhoToGreet("Lightbend"), ActorRef.noSender()); 28 | howdyGreeter.tell(new Greet(), ActorRef.noSender()); 29 | 30 | helloGreeter.tell(new WhoToGreet("Java"), ActorRef.noSender()); 31 | helloGreeter.tell(new Greet(), ActorRef.noSender()); 32 | 33 | goodDayGreeter.tell(new WhoToGreet("Play"), ActorRef.noSender()); 34 | goodDayGreeter.tell(new Greet(), ActorRef.noSender()); 35 | // #main-send-messages 36 | 37 | System.out.println(">>> Press ENTER to exit <<<"); 38 | System.in.read(); 39 | } catch (IOException ioe) { 40 | ioe.printStackTrace(); 41 | } finally { 42 | system.terminate(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/akka/sample/Greeter.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.akka.sample; 2 | 3 | import akka.actor.AbstractActor; 4 | import akka.actor.ActorRef; 5 | import akka.actor.Props; 6 | import cn.xiaowenjie.akka.sample.Printer.Greeting; 7 | 8 | //#greeter-messages 9 | public class Greeter extends AbstractActor { 10 | // #greeter-messages 11 | static public Props props(String message, ActorRef printerActor) { 12 | return Props.create(Greeter.class, () -> new Greeter(message, printerActor)); 13 | } 14 | 15 | // #greeter-messages 16 | static public class WhoToGreet { 17 | public final String who; 18 | 19 | public WhoToGreet(String who) { 20 | this.who = who; 21 | } 22 | } 23 | 24 | static public class Greet { 25 | public Greet() { 26 | } 27 | } 28 | // #greeter-messages 29 | 30 | private final String message; 31 | private final ActorRef printerActor; 32 | private String greeting = ""; 33 | 34 | public Greeter(String message, ActorRef printerActor) { 35 | this.message = message; 36 | this.printerActor = printerActor; 37 | } 38 | 39 | @Override 40 | public Receive createReceive() { 41 | return receiveBuilder().match(WhoToGreet.class, wtg -> { 42 | this.greeting = message + ", " + wtg.who; 43 | }).match(Greet.class, x -> { 44 | // #greeter-send-message 45 | printerActor.tell(new Greeting(greeting), getSelf()); 46 | // #greeter-send-message 47 | }).build(); 48 | } 49 | // #greeter-messages 50 | } 51 | // #greeter-messages 52 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/akka/sample/Printer.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.akka.sample; 2 | 3 | import akka.actor.AbstractActor; 4 | import akka.actor.Props; 5 | import akka.event.Logging; 6 | import akka.event.LoggingAdapter; 7 | 8 | //#printer-messages 9 | public class Printer extends AbstractActor { 10 | // #printer-messages 11 | static public Props props() { 12 | return Props.create(Printer.class, () -> new Printer()); 13 | } 14 | 15 | // #printer-messages 16 | static public class Greeting { 17 | public final String message; 18 | 19 | public Greeting(String message) { 20 | this.message = message; 21 | } 22 | } 23 | // #printer-messages 24 | 25 | private LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this); 26 | 27 | public Printer() { 28 | } 29 | 30 | @Override 31 | public Receive createReceive() { 32 | return receiveBuilder().match(Greeting.class, greeting -> { 33 | log.info(greeting.message); 34 | }).build(); 35 | } 36 | // #printer-messages 37 | } 38 | // #printer-messages 39 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/bytecode/Calc.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.bytecode; 2 | 3 | public class Calc { 4 | 5 | public int calc(){ 6 | int a = 100; 7 | int b = 200; 8 | int c = 300; 9 | return (a+b)/c; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/bytecode/ConstDemo.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.bytecode; 2 | 3 | public class ConstDemo { 4 | 5 | public void test(){ 6 | // const 指令 => iconst_1 7 | int a = 1; 8 | 9 | // bipush = > bipush 127 10 | int b = 127; 11 | 12 | // sipush => sipush 128 13 | int c = 128; 14 | 15 | // ldc 指令, 33333 会作为常量,常量的index会作为参数 16 | // => ldc #2 // int 33333 17 | int a2 = 33333; 18 | 19 | float f = 0f; 20 | double d = 0d; 21 | long l = 1L; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/classloader/FindClassLoader.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.classloader; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | 6 | /** 7 | * 使用-Xbootclasspath可以把包含 另外一份HelloLoader.class 的目录加到启动的classpath中。 8 | * 理论上应该有bootstrap ClassLoader加载。 9 | * 但我们可以先在自己的app loader里面加载他。(必须在使用他之前) 10 | */ 11 | public class FindClassLoader { 12 | 13 | public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { 14 | ClassLoader loader = FindClassLoader.class.getClassLoader(); 15 | 16 | byte[] bytes = loadClassBytes("cn.xiaowenjie.classloader.HelloLoader"); 17 | 18 | Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); 19 | 20 | defineClass.setAccessible(true); 21 | defineClass.invoke(loader, bytes, 0, bytes.length); 22 | defineClass.setAccessible(false); 23 | 24 | HelloLoader helloLoader = new HelloLoader(); 25 | System.out.println(helloLoader.getClass().getClassLoader()); 26 | helloLoader.print(); 27 | } 28 | 29 | private static byte[] loadClassBytes(String s) { 30 | return null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/classloader/HelloLoader.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.classloader; 2 | 3 | public class HelloLoader { 4 | public void print(){ 5 | System.out.println("i am in app class loader"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/completablefuturedemos/Demo1.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.completablefuturedemos; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | import java.util.concurrent.ExecutionException; 5 | 6 | public class Demo1 { 7 | 8 | public static void main(String[] args) throws InterruptedException { 9 | CompletableFuture future = new CompletableFuture<>(); 10 | 11 | System.out.println("start..."); 12 | 13 | new Thread(() -> { 14 | try { 15 | System.out.println(future.get()); 16 | } catch (InterruptedException | ExecutionException e) { 17 | e.printStackTrace(); 18 | } 19 | }).start(); 20 | 21 | // 模拟耗时 22 | Thread.sleep(1000); 23 | 24 | // 告知完成 25 | future.complete("我做完了"); 26 | } 27 | } -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/completablefuturedemos/Demo2.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.completablefuturedemos; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | import java.util.concurrent.ExecutionException; 5 | 6 | public class Demo2 { 7 | 8 | public static void main(String[] args) throws InterruptedException, ExecutionException { 9 | System.out.println("start..."); 10 | 11 | CompletableFuture future = CompletableFuture.supplyAsync(()->{ 12 | try { 13 | Thread.sleep(2000); 14 | } catch (InterruptedException e) { 15 | e.printStackTrace(); 16 | } 17 | return "开启异步任务"; 18 | }); 19 | 20 | System.out.println("end..." + future.get()); 21 | } 22 | } -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/completablefuturedemos/Demo3.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.completablefuturedemos; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | import java.util.concurrent.ExecutionException; 5 | 6 | public class Demo3 { 7 | 8 | public static String test() { 9 | try { 10 | Thread.sleep(2000); 11 | } catch (InterruptedException e) { 12 | e.printStackTrace(); 13 | } 14 | return "我完成了"; 15 | } 16 | 17 | public static void main(String[] args) throws InterruptedException, ExecutionException { 18 | System.out.println("start..."); 19 | 20 | CompletableFuture future = CompletableFuture.supplyAsync(() -> test()) 21 | .thenApply(s -> s + "函数接口,加个尾巴") 22 | .thenAccept(System.out::println); 23 | 24 | System.out.println("end..." + future.get()); 25 | } 26 | } -------------------------------------------------------------------------------- /maven-source/src/main/java/cn/xiaowenjie/completablefuturedemos/Demo4.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.completablefuturedemos; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | import java.util.concurrent.ExecutionException; 5 | 6 | public class Demo4 { 7 | 8 | public static int test(int i) { 9 | return i / 0; 10 | } 11 | 12 | public static void main(String[] args) throws InterruptedException, ExecutionException { 13 | System.out.println("start..."); 14 | 15 | CompletableFuture future = CompletableFuture.supplyAsync(() -> test(2)) 16 | .exceptionally(e -> { 17 | e.printStackTrace(); 18 | return -1; 19 | }) 20 | .thenApply(s -> s + "函数接口,加个尾巴") 21 | .thenAccept(System.out::println); 22 | 23 | System.out.println("end..." + future.get()); 24 | } 25 | } -------------------------------------------------------------------------------- /maven-source/src/test/java/cn/xiaowenjie/AppTest.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest { 11 | /** 12 | * Rigorous Test. 13 | */ 14 | @Test 15 | public void testApp() { 16 | assertTrue(true); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /实战java高并发程序设计.md: -------------------------------------------------------------------------------- 1 | # 实战java高并发程序设计 2 | 3 | 葛一鸣 4 | 5 | > 由于大部分知识我都已经掌握,这里只记录一些关键点,加深印象。本篇笔记并不适合初学者。 6 | 7 | # 线程 8 | 9 | ## 三种地方可能会导致指令重排: 10 | 11 | * 编译器优化的重排序 12 | * 指令级并行的重排序(指令级并行技术(Instruction-Level 13 | Parallelism, ILP)来将多条指令重叠执行) 14 | * 内存系统的重排序(处理器使用缓存和读/写缓冲区) 15 | 16 | cpu的重排是因为cpu指令流水线的存在。 17 | 18 | ## 线程的stop函数可能导致数据不一致问题 19 | 20 | stop会直接终止线程,并立即释放线程持有的锁。假如你代码里面获取锁然后做几个步骤,如果调用stop之后,可能里面的几个步骤只执行了其中几个就结束了! 21 | 22 | 线程退出的正确方法是 run 函数执行结束。一般加个 volatile 的标志位,其他地方修改。 23 | 24 | 或者使用中断检测,也是标志位。 25 | 26 | ## 线程的yield函数 27 | 28 | > yield: 放弃,屈服; 生利; 退让,退位; 29 | 30 | Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。 31 | 32 | > 使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。 33 | 34 | “我已经完成一些重要的工作了,我休息一下,给你们一个机会。” 35 | 36 | ## 线程中断 interrupt 37 | 38 | 只是给线程发送一个通知,告知目标线程,有人希望你退出,至于目标线程是否退出,完全有目标线程决定。 39 | 40 | ```java 41 | public void interrupt() // 中断线程 42 | public boolean isInterrupted() // 判断是否被中断 43 | public boolean interrupted() // 判断是否被中断。被清除中断状态 44 | 45 | ``` 46 | 47 | ## 线程 wait 和 notify 48 | 49 | 注意不是线程的方法,而是对象的方法。是在monitor上面调用的。 50 | 51 | > 不要调用线程对象的 wait和notify,可能会影响系统api工作。 52 | 53 | wait: 释放监视器,然后等待监视器,拿到监视器之后再继续执行。 54 | 55 | notify:释放监视器。但是,需要等待synchronize块结束,其他线程才能得到,不是一调用其他线程就可以得到monitor。 56 | 57 | ## 线程的join 58 | 59 | jdk实现 while (isAlive()) { wait(0); } 60 | 61 | ```java 62 | public final synchronized void join(long millis) 63 | throws InterruptedException { 64 | long base = System.currentTimeMillis(); 65 | long now = 0; 66 | 67 | if (millis < 0) { 68 | throw new IllegalArgumentException("timeout value is negative"); 69 | } 70 | 71 | if (millis == 0) { 72 | while (isAlive()) { 73 | wait(0); 74 | } 75 | } else { 76 | while (isAlive()) { 77 | long delay = millis - now; 78 | if (delay <= 0) { 79 | break; 80 | } 81 | wait(delay); 82 | now = System.currentTimeMillis() - base; 83 | } 84 | } 85 | } 86 | ``` 87 | 88 | ## 线程组 89 | 90 | 统一管理,统一命名。 91 | 92 | ```java 93 | public int activeCount() 94 | ``` 95 | 96 | ## hashmap jdk8 已经修复并发时的死循环问题,只会导致数据不一致不会死循环 97 | 98 | 99 | # jdk 并发包 100 | 101 | ## 重入锁 102 | 103 | ### 中断响应 lockInterruptibly() 解决死锁问题 104 | 105 | ### tryLock 获取不到立刻返回false / tryLock(time) 106 | 107 | ```java 108 | if(lock.tryLock()){ 109 | 110 | } 111 | ``` 112 | 113 | ### 公平锁 114 | 115 | ### Condition 看 ArrayBlockingQueue 代码 116 | 117 | 118 | ## 信号量 119 | 120 | 关注重要的方法和构造函数。 121 | 122 | ## 读写锁 123 | 124 | 多个读,一个写 125 | 126 | ```java 127 | private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); 128 | 129 | private static Lock readLock = lock.readLock(); 130 | private static Lock writeLock = lock.writeLock(); 131 | 132 | ``` 133 | 134 | ## 倒计数器 CountDownLatch 135 | 136 | 主线程在 CountDownLatch上等待,所有准备好后继续执行 137 | 138 | ## 循环栅栏 CyclicBarrier 139 | 140 | CyclicBarrier是执行线程内等待。CountDownLatch是线程外(主线程)等待 141 | 142 | 143 | ## LockSupport 上的重要方法 144 | 145 | ## Guava 和 RateLimiter 限流 146 | 147 | - 漏桶算法 148 | - 先缓存请求到缓存区,然后固定流速留出缓存区 149 | - 令牌桶算法 150 | - 缓存区缓存的不是请求,而是令牌。算法每一个单位时间产生令牌放到缓存区,处理程序拿到令牌后才能处理。 151 | - 没有令牌就丢弃或者等待。 152 | 153 | ```java 154 | static RateLimiter limiter = RateLimiter.create(2);// 每秒2个令牌(处理2个请求) 155 | 156 | for(int i = 0; i<50; i++){ 157 | // 等待 158 | limiter.acquire(); 159 | // dosomething 160 | } 161 | 162 | 163 | for(int i = 0; i<50; i++){ 164 | // 丢弃 165 | if(limiter.tryAcquire()){ 166 | continue; 167 | } 168 | // dosomething 169 | } 170 | 171 | ``` 172 | 173 | ## 线程池 174 | 175 | ### 重点方法 176 | 177 | ### 任务提交逻辑 178 | 179 | - 小于coresize,提交任务分配线程执行 addWorker(command, true) 180 | - 大于coresize,提交队列执行 workQueue.offer(command) 181 | - 成功,等待执行 182 | - 失败 183 | - 提交线程池 addWorker(command, false) ,是否达到max 线程 184 | - 没达到,分配线程执行 185 | - 达到:执行拒绝策略 186 | 187 | ### 四种拒绝策略 188 | 189 | ### 线程池扩展 190 | 191 | beforeExecute、afterExecute、terminated 192 | 193 | ### 线程池的异常 194 | 195 | 如果用submit提交任务,任务异常的时候,不调用get是不会知道的 196 | 197 | 调用Execute则可以。 198 | 199 | ```java 200 | 201 | public void execute(Runnable command) 202 | 203 | public Future submit(Runnable task) 204 | 205 | public Future submit(Runnable task, T result) 206 | 207 | public Future submit(Callable task) 208 | 209 | ``` 210 | 211 | ### guava 的 MoreExecutors 扩展线程池 212 | 213 | [guava异步增强——ListenableFuture](https://www.jianshu.com/p/9c57aa5e34af) 214 | 215 | [Guava并发:ListenableFuture与RateLimiter示例](https://my.oschina.net/cloudcoder/blog/359598) 216 | 217 | 218 | ## ConurrentLinkedQueue 219 | 220 | ## SkipList 跳表是有序的 221 | 222 | 223 | 224 | # JMH 性能测试工具使用 225 | 226 | # 锁优化 227 | 228 | ## AtomicStampedReference ABA 问题 229 | 230 | 为了解决ABA问题,伟大的java为我们提供了AtomicMarkableReference和AtomicStampedReference类,为我们解决了问题 231 | 232 | 233 | [Java多线程:AtomicReference AtomicStampedReference AtomicMarkableReference 原子更新引用类型](https://www.cnblogs.com/2015110615L/p/6749608.html) 234 | 235 | [关于AtomicStampedReference使用的坑](https://blog.csdn.net/xybz1993/article/details/79992120) 236 | 237 | ## 数组无锁 AtmoicIntergerArray 238 | 239 | ## 普通变量享受原子操作 AtomicIntegerFieldUpdater 240 | 241 | AtomicIntegerFieldUpdater: 基于反射的工具,可用CompareAndSet对volatile int进行原子更新: 242 | 243 | [AtomicIntegerFieldUpdater](https://blog.csdn.net/liyantianmin/article/details/53144939) 244 | 245 | [AtomicIntegerFieldUpdater使用](http://www.cnblogs.com/hithlb/p/4516078.html) 246 | 247 | 248 | 249 | 250 | # 并行模式与算法 251 | 252 | ## 单例模式 253 | 254 | 内部类 255 | 256 | ## 不变模式 257 | 258 | final class, 只有get方法,构造函数赋值。 259 | 260 | ## 生产消费者模式 261 | 262 | 使用 blockingqueue 实现 263 | 264 | ## 高性能的生产-消费者模式:无锁实现 265 | 266 | - ConcurrentLinkedQueue 267 | - Disruptor 268 | - RingBuffer, 环形,不需要head和tail,只需要一个cursor 269 | - 读写数据使用CAS 270 | - 完全内存复用,不会分配和回收内存 271 | 272 | 273 | [高性能队列Disruptor的使用](https://www.jianshu.com/p/8473bbb556af) 274 | 275 | ```java 276 | // 发布事件; 277 | RingBuffer ringBuffer = disruptor.getRingBuffer(); 278 | long sequence = ringBuffer.next();//请求下一个事件序号; 279 | 280 | try { 281 | LongEvent event = ringBuffer.get(sequence);//获取该序号对应的事件对象; 282 | long data = getEventData();//获取要通过事件传递的业务数据; 283 | event.set(data); 284 | } finally{ 285 | ringBuffer.publish(sequence);//发布事件; 286 | } 287 | ``` 288 | 289 | ## cpu cache 伪共享 290 | 291 | ## Future 模式 292 | 293 | 订单/契约 294 | 295 | [Java多线程 - Future模式](https://www.jianshu.com/p/949d44f3d9e3) 296 | 297 | ## guava 对 future的支持 298 | 299 | ### 增加回调 300 | 301 | [Guava包中的ListenableFuture详情解析](https://blog.csdn.net/qq496013218/article/details/77522820) 302 | 303 | ```java 304 | public class GuavaFutureDemo { 305 | 306 | public static void main(String[] args) { 307 | ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); 308 | com.google.common.util.concurrent.ListenableFuture task = service.submit(new Callable() { 309 | public Object call() { 310 | return "回调参数success"; 311 | } 312 | }); 313 | 314 | task.addListener(() -> { 315 | try { 316 | System.out.println("done," + task.get()); 317 | } catch (InterruptedException e) { 318 | e.printStackTrace(); 319 | } catch (ExecutionException e) { 320 | e.printStackTrace(); 321 | } 322 | }, MoreExecutors.directExecutor()); 323 | 324 | 325 | Futures.addCallback(task, new FutureCallback() { 326 | @Override 327 | public void onSuccess(Object result) { 328 | System.out.println("result = " + result); 329 | } 330 | 331 | @Override 332 | public void onFailure(Throwable t) { 333 | System.out.println("t = " + t); 334 | 335 | } 336 | }, MoreExecutors.directExecutor()); // MoreExecutors.newDirectExecutorService() 337 | } 338 | } 339 | ``` 340 | 341 | [通过实例理解 JDK8 的 CompletableFuture](https://www.ibm.com/developerworks/cn/java/j-cf-of-jdk8/index.html) 342 | 343 | ## 并行 344 | 345 | ### 希尔排序 346 | 347 | 希尔排序也是一种`插入排序`,它是简单插入排序经过改进之后的一个更高效的版本,也称为`缩小增量排序`,同时该算法是冲破O(n2)的第一批算法之一。 348 | 349 | [图解排序算法(二)之希尔排序](https://www.cnblogs.com/chengxiao/p/6104371.html) 350 | 351 | > 由于分组,可以并行 352 | 353 | ## 准备好了通知我: NIO 354 | 355 | ## 读完了在通知我:AIO 356 | 357 | NIO还是同步的,AIO则是异步。 358 | 359 | ## CompletableFuture 360 | 361 | ### 完成了就通知我 362 | 363 | future.complete(结果) 364 | 365 | ```java 366 | public static void main(String[] args) throws InterruptedException { 367 | CompletableFuture future = new CompletableFuture<>(); 368 | 369 | System.out.println("start..."); 370 | 371 | new Thread(() -> { 372 | try { 373 | System.out.println(future.get()); 374 | } catch (InterruptedException | ExecutionException e) { 375 | e.printStackTrace(); 376 | } 377 | }).start(); 378 | 379 | // 模拟耗时 380 | Thread.sleep(1000); 381 | 382 | // 告知完成 383 | future.complete("我做完了"); 384 | } 385 | ``` 386 | 387 | ### 异步执行 388 | 389 | 390 | 391 | ```java 392 | public static void main(String[] args) throws InterruptedException, ExecutionException { 393 | System.out.println("start..."); 394 | 395 | CompletableFuture future = CompletableFuture.supplyAsync(()->{ 396 | try { 397 | Thread.sleep(2000); 398 | } catch (InterruptedException e) { 399 | e.printStackTrace(); 400 | } 401 | return "开启异步任务"; 402 | }); 403 | 404 | System.out.println("end..." + future.get()); 405 | } 406 | ``` 407 | 408 | 409 | ```java 410 | // 有返回值,输入提供者 411 | public static CompletableFuture supplyAsync(Supplier supplier) 412 | public static CompletableFuture supplyAsync(Supplier supplier, Executor executor) 413 | 414 | // runAsync 没有返回值,传入 runnable 415 | public static CompletableFuture runAsync(Runnable runnable, Executor executor) 416 | public static CompletableFuture runAsync(Runnable runnable) 417 | ``` 418 | 419 | ### 流式调用 420 | 421 | then。。。 422 | 423 | ```java 424 | public static void main(String[] args) throws InterruptedException, ExecutionException { 425 | System.out.println("start..."); 426 | 427 | CompletableFuture future = CompletableFuture.supplyAsync(() -> test()) 428 | .thenApply(s -> s + "函数接口,加个尾巴") 429 | .thenAccept(System.out::println); 430 | 431 | System.out.println("end..." + future.get()); 432 | } 433 | ``` 434 | 435 | ### 异常处理 436 | 437 | exceptionally 处理异常,返回(默认)值 438 | 439 | ```java 440 | public class Demo4 { 441 | 442 | public static int test(int i) { 443 | return i / 0; 444 | } 445 | 446 | public static void main(String[] args) throws InterruptedException, ExecutionException { 447 | System.out.println("start..."); 448 | 449 | CompletableFuture future = CompletableFuture.supplyAsync(() -> test(2)) 450 | .exceptionally(e -> { 451 | e.printStackTrace(); 452 | return -1; 453 | }) 454 | .thenApply(s -> s + "函数接口,加个尾巴") 455 | .thenAccept(System.out::println); 456 | 457 | System.out.println("end..." + future.get()); 458 | } 459 | } 460 | ``` 461 | 462 | ### 组合多个 CompletableFuture 463 | 464 | ```java 465 | 466 | public CompletableFuture thenCompose( 467 | Function> fn) { 468 | return uniComposeStage(null, fn); 469 | } 470 | 471 | public CompletableFuture thenComposeAsync( 472 | Function> fn) { 473 | return uniComposeStage(asyncPool, fn); 474 | } 475 | 476 | public CompletableFuture thenComposeAsync( 477 | Function> fn, 478 | Executor executor) { 479 | return uniComposeStage(screenExecutor(executor), fn); 480 | } 481 | 482 | public CompletableFuture thenCombine( 483 | CompletionStage other, 484 | BiFunction fn) { 485 | return biApplyStage(null, other, fn); 486 | } 487 | 488 | public CompletableFuture thenCombineAsync( 489 | CompletionStage other, 490 | BiFunction fn) { 491 | return biApplyStage(asyncPool, other, fn); 492 | } 493 | 494 | public CompletableFuture thenCombineAsync( 495 | CompletionStage other, 496 | BiFunction fn, Executor executor) { 497 | return biApplyStage(screenExecutor(executor), other, fn); 498 | } 499 | 500 | ``` 501 | 502 | ### 支持timeout (jkd9) 503 | 504 | orTimeOut 505 | 506 | 507 | 508 | ## 读写锁的改进: StampedLock 509 | 510 | 读写锁:悲观锁,读锁会阻塞写锁。可能引起写线程的“饥饿”。 511 | 512 | StampedLock 乐观的读策略。 513 | 514 | 先 `long stamp = sl.tryOptimisticRead()` 获取乐观读, 然后校验 `sl.validate(stamp)`, 失败则 `sl.readLock()` 获取悲观读。 515 | 516 | ```java 517 | public class StampedLockDemo { 518 | private double x, y; 519 | 520 | private final StampedLock sl = new StampedLock(); 521 | 522 | // an exclusively locked method 523 | void move(double deltaX, double deltaY) { 524 | long stamp = sl.writeLock(); 525 | try { 526 | x += deltaX; 527 | y += deltaY; 528 | } finally { 529 | sl.unlockWrite(stamp); 530 | } 531 | } 532 | 533 | // 下面看看乐观读锁案例 534 | // A read-only method 535 | double distanceFromOrigin() { 536 | long stamp = sl.tryOptimisticRead(); // 获得一个乐观读锁 537 | 538 | double currentX = x, currentY = y; // 将两个字段读入本地局部变量 539 | 540 | // 检查发出乐观读锁后同时是否有其他写锁发生? 541 | if (!sl.validate(stamp)) { 542 | // 如果没有,我们再次获得一个读悲观锁 543 | stamp = sl.readLock(); 544 | 545 | try { 546 | currentX = x; // 将两个字段读入本地局部变量 547 | currentY = y; // 将两个字段读入本地局部变量 548 | } finally { 549 | sl.unlockRead(stamp); 550 | } 551 | } 552 | 553 | return Math.sqrt(currentX * currentX + currentY * currentY); 554 | } 555 | 556 | // 下面是悲观读锁案例 557 | // upgrade 558 | void moveIfAtOrigin(double newX, double newY) { 559 | // Could instead start with optimistic, not read mode 560 | long stamp = sl.readLock(); 561 | 562 | try { 563 | // 循环,检查当前状态是否符合 564 | while (x == 0.0 && y == 0.0) { 565 | 566 | // 将读锁转为写锁 567 | long ws = sl.tryConvertToWriteLock(stamp); 568 | 569 | // 这是确认转为写锁是否成功 570 | if (ws != 0L) { 571 | stamp = ws; // 如果成功 替换票据 572 | x = newX; // 进行状态改变 573 | y = newY; // 进行状态改变 574 | break; 575 | } 576 | // 如果不能成功转换为写锁 577 | else { 578 | sl.unlockRead(stamp); // 我们显式释放读锁 579 | stamp = sl.writeLock(); // 显式直接进行写锁 然后再通过循环再试 580 | } 581 | } 582 | } finally { 583 | sl.unlock(stamp); // 释放读锁或写锁 584 | } 585 | } 586 | } 587 | ``` 588 | 589 | [Java 8新特性探究(十)StampedLock将是解决同步问题的新宠](http://www.importnew.com/14941.html) 590 | 591 | [Java并发编程之StampedLock锁源码探究](https://juejin.im/entry/5b28a4c46fb9a00e8a3e534a) 592 | 593 | - CLH 锁,自旋锁 594 | - FIFO 595 | - 不可重入 596 | 597 | ## 原子类增强 598 | 599 | ### 更快的原子类:LongAdder 600 | 601 | ### LongAdder功能的增强版:LongAccumulator 602 | 603 | 604 | ## ConcurrentHashMap 的增强 605 | 606 | ### 一堆 foreach 函数 607 | 608 | ### 一堆 reduce 操作 609 | 610 | ### 条件插入 computeIfAbsent 611 | 612 | ### 一堆 search 操作 613 | 614 | ### mappingCount 615 | 616 | - 返回 long,size 返回 int 617 | - 并发的时候不一定精确 618 | 619 | ### newKeySet 返回一个set 620 | 621 | 622 | 623 | # Akka 构建高并发程序 624 | 625 | Scala语音 626 | 627 | ## 新并发模型 actor 628 | 629 | 基本执行单元。比喻成一个人。 630 | 631 | 632 | - 面向对象是把所有的东西都抽象成对象。 633 | - Actor是把所有的消息传递抽象成一种抽象的模式。 634 | 635 | [入门demo](https://developer.lightbend.com/guides/akka-quickstart-java/) 636 | 637 | > Collections.unmodifiableList(new ArrayList(values)); 638 | 639 | ## Benefits of using the Actor Model 640 | 641 | The following characteristics of Akka allow you to solve difficult concurrency and scalability challenges in an intuitive way: 642 | 643 | - Event-driven model — Actors perform work in response to messages. Communication between Actors is asynchronous, allowing Actors to send messages and continue their own work without blocking to wait for a reply. 644 | - Strong isolation principles — Unlike regular objects in Java, an Actor does not have a public API in terms of methods that you can invoke. Instead, its public API is defined through messages that the actor handles. This prevents any sharing of state between Actors; the only way to observe another actor’s state is by sending it a message asking for it. 645 | - Location transparency — The system constructs Actors from a factory and returns references to the instances. Because location doesn’t matter, Actor instances can start, stop, move, and restart to scale up and down as well as recover from unexpected failures. 646 | - Lightweight — Each instance consumes only a few hundred bytes, which realistically allows millions of concurrent Actors to exist in a single application. 647 | 648 | # 调试技巧 649 | 650 | - 有条件断点 651 | - 可以选择挂起线程还是jvm(suspend thread / suspend jvm) 652 | 653 | # jetty 654 | 655 | [jetty9优化的两处地方](https://www.cnblogs.com/LBSer/p/3637387.html) 656 | 657 | [jetty-server高性能,多线程特性的源码分析](https://www.jianshu.com/p/695be1d07c38) --------------------------------------------------------------------------------