diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 0d36467..3e7bcd3 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -1,8 +1,12 @@
-
+
+
+
+
+
@@ -66,11 +70,11 @@
+
-
@@ -79,26 +83,13 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
+
@@ -106,12 +97,12 @@
-
-
+
+
-
+
@@ -119,12 +110,12 @@
-
-
+
+
-
+
@@ -132,12 +123,12 @@
-
-
+
+
-
+
@@ -146,11 +137,11 @@
-
+
-
+
@@ -199,11 +190,11 @@
-
+
-
-
+
+
@@ -264,9 +255,21 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+ 1581156795852
+
+
+
+ 1581156795852
1581270301073
@@ -590,28 +593,21 @@
1585234195148
-
- 1585657673868
+
+ 1585377224497
- 1585657673869
+ 1585377224497
-
- 1585658308922
+
+ 1586355292587
- 1585658308922
-
-
- 1585658338742
-
-
-
- 1585658338743
+ 1586355292587
-
+
@@ -687,9 +683,9 @@
-
-
-
+
+
+
@@ -700,92 +696,92 @@
-
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
+
-
+
+
+
+
+
-
+
-
+
-
+
@@ -794,9 +790,9 @@
-
+
-
+
\ No newline at end of file
diff --git a/Rocket.md b/Rocket.md
index 9e5f8db..d0c9c8d 100644
--- a/Rocket.md
+++ b/Rocket.md
@@ -212,6 +212,16 @@ HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
+### 动态年龄计算
+
+Hotspot在遍历所有对象时,按照年龄从小到大对其所占用的大小进行累积,当累积的某个年龄大小超过了survivor区的一半时,取这个年龄和MaxTenuringThreshold中更小的一个值,作为新的晋升年龄阈值。
+
+JVM引入动态年龄计算,主要基于如下两点考虑:
+
+1. 如果固定按照MaxTenuringThreshold设定的阈值作为晋升条件: a)MaxTenuringThreshold设置的过大,原本应该晋升的对象一直停留在Survivor区,直到Survivor区溢出,一旦溢出发生,Eden+Svuvivor中对象将不再依据年龄全部提升到老年代,这样对象老化的机制就失效了。 b)MaxTenuringThreshold设置的过小,“过早晋升”即对象不能在新生代充分被回收,大量短期对象被晋升到老年代,老年代空间迅速增长,引起频繁的Major GC。分代回收失去了意义,严重影响GC性能。
+2. 相同应用在不同时间的表现不同:特殊任务的执行或者流量成分的变化,都会导致对象的生命周期分布发生波动,那么固定的阈值设定,因为无法动态适应变化,会造成和上面相同的问题。
+
+
### 常见的垃圾回收机制
1. 引用计数法:引用计数法是一种简单但速度很慢的垃圾回收技术。每个对象都含有一个引用计数器,当有引用连接至对象时,引用计数加1。当引用离开作用域或被置为null时,引用计数减1。虽然管理引用计数的开销不大,但这项开销在整个程序生命周期中将持续发生。垃圾回收器会在含有全部对象的列表上遍历,当发现某个对象引用计数为0时,就释放其占用的空间。
@@ -270,7 +280,6 @@ HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分
双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。
-
### 双亲委派模型的"破坏"
一个典型的例子便是JNDI服务,JNDI现在已经是Java的标准服务,它的代码由启动类加载器去加载(在JDK 1.3时放进去的rt.jar),但JNDI的目的就是对资源进行集中管理和查找,它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者(SPI,Service Provider Interface)的代码,但启动类加载器不可能“认识”这些代码那该怎么办?
@@ -318,21 +327,78 @@ HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分
* JConsole
* VisualVM
-
-### JVM调优实战
-
-1. 外部命令导致系统缓慢
+
+### JVM常见参数
+
+1. -Xms20M:表示设置JVM启动内存的最小值为20M,必须以M为单位
+2. -Xmx20M:表示设置JVM启动内存的最大值为20M,必须以M为单位。将-Xmx和-Xms设置为一样可以避免JVM内存自动扩展。大的项目-Xmx和-Xms一般都要设置到10G、20G甚至还要高
+3. -verbose:gc:表示输出虚拟机中GC的详细情况
+4. -Xss128k:表示可以设置虚拟机栈的大小为128k
+5. -Xoss128k:表示设置本地方法栈的大小为128k。不过HotSpot并不区分虚拟机栈和本地方法栈,因此对于HotSpot来说这个参数是无效的
+6. -XX:PermSize=10M:表示JVM初始分配的永久代(方法区)的容量,必须以M为单位
+7. -XX:MaxPermSize=10M:表示JVM允许分配的永久代(方法区)的最大容量,必须以M为单位,大部分情况下这个参数默认为64M
+8. -Xnoclassgc:表示关闭JVM对类的垃圾回收
+9. -XX:+TraceClassLoading表示查看类的加载信息
+10. -XX:+TraceClassUnLoading:表示查看类的卸载信息
+11. -XX:NewRatio=4:表示设置年轻代(包括Eden和两个Survivor区)/老年代 的大小比值为1:4,这意味着年轻代占整个堆的1/5
+12. -XX:SurvivorRatio=8:表示设置2个Survivor区:1个Eden区的大小比值为2:8,这意味着Survivor区占整个年轻代的1/5,这个参数默认为8
+13. -Xmn20M:表示设置年轻代的大小为20M
+14. -XX:+HeapDumpOnOutOfMemoryError:表示可以让虚拟机在出现内存溢出异常时Dump出当前的堆内存转储快照
+15. -XX:+UseG1GC:表示让JVM使用G1垃圾收集器
+16. -XX:+PrintGCDetails:表示在控制台上打印出GC具体细节
+17. -XX:+PrintGC:表示在控制台上打印出GC信息
+18. -XX:PretenureSizeThreshold=3145728:表示对象大于3145728(3M)时直接进入老年代分配,这里只能以字节作为单位
+19. -XX:MaxTenuringThreshold=1:表示对象年龄大于1,自动进入老年代,如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活时间,增加在年轻代被回收的概率。
+20. -XX:CompileThreshold=1000:表示一个方法被调用1000次之后,会被认为是热点代码,并触发即时编译
+21. -XX:+PrintHeapAtGC:表示可以看到每次GC前后堆内存布局
+22. -XX:+PrintTLAB:表示可以看到TLAB的使用情况
+23. -XX:+UseSpining:开启自旋锁
+24. -XX:PreBlockSpin:更改自旋锁的自旋次数,使用这个参数必须先开启自旋锁
+25. -XX:+UseSerialGC:表示使用jvm的串行垃圾回收机制,该机制适用于单核cpu的环境下
+26. -XX:+UseParallelGC:表示使用jvm的并行垃圾回收机制,该机制适合用于多cpu机制,同时对响应时间无强硬要求的环境下,使用-XX:ParallelGCThreads=设置并行垃圾回收的线程数,此值可以设置与机器处理器数量相等。
+27. -XX:+UseParallelOldGC:表示年老代使用并行的垃圾回收机制
+28. -XX:+UseConcMarkSweepGC:表示使用并发模式的垃圾回收机制,该模式适用于对响应时间要求高,具有多cpu的环境下
+29. -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
+30. -XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时,一直打开
+
+### JVM调优目标-何时需要做jvm调优
+
+1. heap 内存(老年代)持续上涨达到设置的最大内存值;
+2. Full GC 次数频繁;
+3. GC 停顿时间过长(超过1秒);
+4. 应用出现OutOfMemory 等内存异常;
+5. 应用中有使用本地缓存且占用大量内存空间;
+6. 系统吞吐量与响应性能不高或下降。
+
+### JVM调优实战
+
+1. Major GC和Minor GC频繁
+
+ 首先优化Minor GC频繁问题。通常情况下,由于新生代空间较小,Eden区很快被填满,就会导致频繁Minor GC,因此可以通过增大新生代空间来降低Minor GC的频率。例如在相同的内存分配率的前提下,新生代中的Eden区增加一倍,Minor GC的次数就会减少一半。
+
+ 扩容Eden区虽然可以减少Minor GC的次数,但会增加单次Minor GC时间么?扩容后,Minor GC时增加了T1(扫描时间),但省去T2(复制对象)的时间,更重要的是对于虚拟机来说,复制对象的成本要远高于扫描成本,所以,单次Minor GC时间更多取决于GC后存活对象的数量,而非Eden区的大小。因此如果堆中短期对象很多,那么扩容新生代,单次Minor GC时间不会显著增加。
+
+2. 请求高峰期发生GC,导致服务可用性下降
+
+ 由于跨代引用的存在,CMS在Remark阶段必须扫描整个堆,同时为了避免扫描时新生代有很多对象,增加了可中断的预清理阶段用来等待Minor GC的发生。只是该阶段有时间限制,如果超时等不到Minor GC,Remark时新生代仍然有很多对象,我们的调优策略是,通过参数强制Remark前进行一次Minor GC,从而降低Remark阶段的时间。
+ 另外,类似的JVM是如何避免Minor GC时扫描全堆的? 经过统计信息显示,老年代持有新生代对象引用的情况不足1%,根据这一特性JVM引入了卡表(card table)来实现这一目的。卡表的具体策略是将老年代的空间分成大小为512B的若干张卡(card)。卡表本身是单字节数组,数组中的每个元素对应着一张卡,当发生老年代引用新生代时,虚拟机将该卡对应的卡表元素设置为适当的值。如上图所示,卡表3被标记为脏(卡表还有另外的作用,标识并发标记阶段哪些块被修改过),之后Minor GC时通过扫描卡表就可以很快的识别哪些卡中存在老年代指向新生代的引用。这样虚拟机通过空间换时间的方式,避免了全堆扫描。
+
+3. 外部命令导致系统缓慢
一个数字校园应用系统,发现请求响应时间比较慢,通过操作系统的mpstat工具发现CPU使用率很高,并且系统占用绝大多数的CPU资 源的程序并不是应用系统本身。每个用户请求的处理都需要执行一个外部shell脚本来获得系统的一些信息,执行这个shell脚本是通过Java的 Runtime.getRuntime().exec()方法来调用的。这种调用方式可以达到目的,但是它在Java 虚拟机中是非常消耗资源的操作,即使外部命令本身能很快执行完毕,频繁调用时创建进程 的开销也非常可观。Java虚拟机执行这个命令的过程是:首先克隆一个和当前虚拟机拥有一 样环境变量的进程,再用这个新的进程去执行外部命令,最后再退出这个进程。如果频繁执 行这个操作,系统的消耗会很大,不仅是CPU,内存负担也很重。用户根据建议去掉这个Shell脚本执行的语句,改为使用Java的API去获取这些信息后, 系统很快恢复了正常。
+
+4. STW过长的GC
-2. 由Windows虚拟内存导致的长时间停顿
+ 对于性能要求很高的服务,建议将MaxPermSize和MinPermSize设置成一致(JDK8开始,Perm区完全消失,转而使用元空间。而元空间是直接存在内存中,不在JVM中),Xms和Xmx也设置为相同,这样可以减少内存自动扩容和收缩带来的性能损失。虚拟机启动的时候就会把参数中所设定的内存全部化为私有,即使扩容前有一部分内存不会被用户代码用到,这部分内存在虚拟机中被标识为虚拟内存,也不会交给其他进程使用。
- 一个带心跳检测功能的GUI桌面程序,每15秒会发送一次心跳检测信号,如果 对方30秒以内都没有信号返回,那就认为和对方程序的连接已经断开。程序上线后发现心跳 检测有误报的概率,查询日志发现误报的原因是程序会偶尔出现间隔约一分钟左右的时间完 全无日志输出,处于停顿状态。
+5. 由Windows虚拟内存导致的长时间停顿
+ 一个带心跳检测功能的GUI桌面程序,每15秒会发送一次心跳检测信号,如果 对方30秒以内都没有信号返回,那就认为和对方程序的连接已经断开。程序上线后发现心跳 检测有误报的概率,查询日志发现误报的原因是程序会偶尔出现间隔约一分钟左右的时间完 全无日志输出,处于停顿状态。
+
因为是桌面程序,所需的内存并不大(-Xmx256m),所以开始并没有想到是GC导致的 程序停顿,但是加入参数-XX:+PrintGCApplicationStoppedTime-XX:+PrintGCDateStamps- Xloggc:gclog.log后,从GC日志文件中确认了停顿确实是由GC导致的,大部分GC时间都控 制在100毫秒以内,但偶尔就会出现一次接近1分钟的GC。
从GC日志中找到长时间停顿的具体日志信息(添加了-XX:+PrintReferenceGC参数), 找到的日志片段如下所示。从日志中可以看出,真正执行GC动作的时间不是很长,但从准 备开始GC,到真正开始GC之间所消耗的时间却占了绝大部分。
-
+
除GC日志之外,还观察到这个GUI程序内存变化的一个特点,当它最小化的时候,资源 管理中显示的占用内存大幅度减小,但是虚拟内存则没有变化,因此怀疑程序在最小化时它 的工作内存被自动交换到磁盘的页面文件之中了,这样发生GC时就有可能因为恢复页面文 件的操作而导致不正常的GC停顿。在Java的GUI程序中要避免这种现象,可以 加入参数“-Dsun.awt.keepWorkingSetOnMinimize=true”来解决。
## Java基础
@@ -377,6 +443,10 @@ CAS是英文单词CompareAndSwap的缩写,中文意思是:比较并替换。
AQS内部有3个对象,一个是state(用于计数器,类似gc的回收计数器),一个是线程标记(当前线程是谁加锁的),一个是阻塞队列。
+AQS是自旋锁,在等待唤醒的时候,经常会使用自旋的方式,不停地尝试获取锁,直到被其他线程获取成功。
+
+AQS有两个队列,同步对列和条件队列。同步队列依赖一个双向链表来完成同步状态的管理,当前线程获取同步状态失败后,同步器会将线程构建成一个节点,并将其加入同步队列中。通过signal或signalAll将条件队列中的节点转移到同步队列。
+
### 如何指定多个线程的执行顺序
1. 设定一个 orderNum,每个线程执行结束之后,更新 orderNum,指明下一个要执行的线程。并且唤醒所有的等待线程。