标记整理算法
- 对于老年代,回收的垃圾较少时,如果采用复制算法,则效率较低。标记整理算法的标记操作和“标记-清除”算法一致,后续操作不只是直接清理对象,而是在清理无用对象完成后让所有存活的对象都向一端移动,并更新引用其对象的指针。
- 很显然,整理这一下需要时间,所以与标记清除算法相比,这一步花费了不少时间,但从长远来看,这一步还是很有必要的。
分代收集算法 - 针对不同的年代进行不同算法的垃圾回收,针对新生代选择复制算法,对老年代选择标记整理算法。
垃圾收集器 - Java的应用很广,内存区域也很多,可以使用不同的垃圾收集器。
Serial收集器 - 单线程垃圾收集器、最基本、发展最悠久。它的单线程的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。偶尔用在桌面应用中。
ParNew收集器
可多线程收集垃圾,收集新生代,使用收集算法
Parallel收集器 - 多线程收集垃圾,收集新生代,使用收集算法。Parallel收集器更关注系统的吞吐量,可以通过参数来打开自适应调节策略。
- 吞吐量:CPU用于运行用户代码的时间与CPU消耗的总时间的比值。
吞吐量 = (执行用户代码时间)/(执行用户代码时间+垃圾回收占用时间) - -XX:MaxGCPauseMillis 垃圾收集器最大停顿的时间,但最大停顿时间过短必然会导致新生代的内存大小变小,垃圾回收频率变高,效率可能降低。
-XX:CGTIMERatio 吞吐量大小(0-100),默认为99。
CMS收集器 - Concurrent Mark Sweep,采用标记-清除算法,用于老年代,常与ParNew协同工作。优点在于并发收集与低停顿。
注:并行是指同一时刻同时做多件事情,而并发是指同一时间间隔内做多件事情
工作过程
- 初始标记
标记老年代中所有的GC Roots对象和年轻代中活着的对象引用到的老年代的对象,时间短; - 并发标记
从“初始标记”阶段标记的对象开始找出所有存活的对象; - 重新标记
用来处理前一个阶段因为引用关系改变导致没有标记到的存活对象,时间短; - 并发清理
清除那些没有标记的对象并且回收空间。 - 缺点:占用大量的cpu资源、无法处理浮点垃圾、出现Concurrent MarkFailure、空间碎片。
G1收集器
- G1(Garbage First)垃圾收集器是当今垃圾回收技术最前沿的成果之一,早在JDK7就已加入JVM的收集器大家庭中,成为HotSpot重点发展的垃圾回收技术。
优势:并行(多核CPU)与并发;
分代收集(新生代和老年代区分不明显);
空间整合;
限制收集范围,可预测的停顿。
步骤:初始标记、并发标记、最终标记和筛选回收。
内存分配
原则:
- 优先分配到Eden
\2. 大对象直接分配到老年代
\3. 长期存活的对象分配到老年代
\4. 空间分配担保
\5. 动态对象的年龄判断
Eden区域
- -verbose:gc -XX:+PrintGCDetails 表示输出虚拟机中GC的详细情况。
- 默认使用Parallel收集器(服务器),Serial收集器(客户端),服务器和客户端可以通过java
-version查看。也可以通过-XX:+UseSerialGC设置收集器。 -Xms20M -Xmx20M -Xmn10M
设置内存大小大小为20M,新生代大小为10M。-XX:SurvivorRatio=8
设置eden 与survuvor 的比值大小 8:1
public class Eden {
public static void main(String[] args) {
// -verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
/*给大家推荐一个免费的学习交流君样:826021115 */
byte[] b1 = new byte[2 * 1024 * 1024];
byte[] b2 = new byte[2 * 1024 * 1024];
byte[] b3 = new byte[2 * 1024 * 1024];
byte[] b4 = new byte[4 * 1024 * 1024]; //第一次Minor回收
/*给大家推荐一个免费的学习交流君样:826021115 */
System.gc();
}
}
GC日志:
- JVM优先把对象放入Eden区,当Eden区放不下了后(2 * 3 = 6M),通过分配担保机制放入老年代6M(Minor
GC),再把最后一个4M放入新生代。
大对象直接分配到老年代
我们认为大对象不是朝生夕死的,如果放在新生代,则需要不断移动,性能较差。
-XX:PretenureSizeThreshold=6M
设置大文件大小。
public class Old {
public static void main(String[] args) {
byte[] b1 = new byte[7 * 1024 * 1024];
} /*给大家推荐一个免费的学习交流君样:826021115 */
}
- 7M大于设置的大文件的大小(6M),直接放入老年代。
长期存活的对象分配到老年代
-XX:MaxTenuringThreshold`最大年龄,默认为15;
Age 1 + 1 + 1 使用年龄计数器。
空间分配担保 -XX:+HandlePromotionFailure
开启-XX:-HandlePromotionFailure
禁用- 取之前每一次回收晋升到老年代对象容量的平均值大小作为经验值,与老年代的剩余空间进行比较,决定是否FullGC来让老年代腾出更多空间。
逃逸分析与栈上分配
- 逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中,称为方法逃逸。
public static StringBuffer craeteStringBuffer(String s1, String s2) {
StringBuffer sb = new StringBuffer();
/*给大家推荐一个免费的学习交流君样:826021115 */
sb.append(s1);
sb.append(s2);
return sb;
}