-XX:+UseSerialGC = Serial New (DefNew) + Serial Old
-XX:+UseParNewGC = ParNew + SerialOld
-XX:+UseConcMarkSweepGC = ParNew + CMS + Serial Old
-XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】
-XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old
-XX:+UseG1GC = G1
Linux中没找到默认GC的查看方法,而windows中会打印UseParallelGC
Linux下1.8版本默认的垃圾回收器到底是什么?
-Xmn -Xms -Xmx -Xss
年轻代 最小堆 最大堆 栈空间
-XX:+UseTLAB
使用TLAB,默认打开,(一般不需要调整)
-XX:+PrintTLAB
打印TLAB的使用情况,(一般不需要调整)
-XX:TLABSize
设置TLAB大小,(一般不需要调整)
-XX:+DisableExplictGC,要默认打开,禁用手动频繁调用gc
System.gc()不管用 ,FGC
-XX:+PrintGC
打印出GC信息
-XX:+PrintGCDetails
打印出GC详细信息
-XX:+PrintGCCauses
打印GC和产生的原因
-XX:+PrintHeapAtGC
-XX:+PrintGCTimeStamps
打印GC详细时间
-XX:+PrintGCApplicationConcurrentTime (低)
打印应用程序时间
-XX:+PrintGCApplicationStoppedTime (低)
打印暂停时长
-XX:+PrintReferenceGC (重要性低)
记录回收了多少种不同引用类型的引用
-verbose:class
类加载详细过程
-XX:+PrintVMOptions
打印运行时参数
-XX:+PrintFlagsFinal -XX:+PrintFlagsInitial
必须会用,打印参数

-Xloggc:opt/log/gc.log
设置gc文件
-XX:MaxTenuringThreshold
升代年龄,最大值15,cms默认6,其他默认15
锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 …
这些不建议设置
所谓调优,首先确定,追求啥?吞吐量优先,还是响应时间优先?还是在满足一定的响应时间的情况下,要求达到多大的吞吐量…
吞吐量优先场景:科学计算、数据挖掘。垃圾回收器一般:(PS + PO)
响应时间优先场景:网站 GUI API 。垃圾回收器一般:(1.8 G1)
步骤:
熟悉业务场景(没有最好的垃圾回收器,只有最合适的垃圾回收器)
选择回收器组合
计算内存需求(经验值 1.5G 16G)
一般只能算个大概,然后要经过压测
选定CPU(越高越好)
设定年代大小、升级年龄
设定日志参数
eg: -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
解释:-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log:日志文件,一般是系统名+时间;UseGCLogFileRotation:循环使用;XX:NumberOfGCLogFiles有多少个日志文件;XX:GCLogFileSize:每个日志文件大小。可以每天产生一个日志文件。
观察日志情况
每种垃圾回收器的日志格式是不同的!

用这个参数运行HelloGC
java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGCDetails HelloGC
查看打印日志


其中heap dump部分:
eden space 5632K, 94% used [0x00000000ff980000,0x00000000ffeb3e28,0x00000000fff00000)
后面的内存地址指的是,起始地址,使用空间结束地址,整体空间结束地址

执行命令:java -Xms20M -Xmx20M -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC com.mashibing.jvm.gc.T15_FullGC_Problem01
[GC (Allocation Failure) [ParNew: 6144K->640K(6144K), 0.0265885 secs] 6585K->2770K(19840K), 0.0268035 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]
ParNew:年轻代收集器
6144->640:收集前后的对比
(6144):整个年轻代容量
6585 -> 2770:整个堆的情况
(19840):整个堆大小
[GC (CMS Initial Mark) [1 CMS-initial-mark: 8511K(13696K)] 9866K(19840K), 0.0040321 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
//8511 (13696) : 老年代使用(最大)
//9866 (19840) : 整个堆使用(最大)
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.018/0.018 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
//这里的时间意义不大,因为是并发执行
[CMS-concurrent-preclean-start][CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
//标记Card为Dirty,也称为Card Marking
[GC (CMS Final Remark) [YG occupancy: 1597 K (6144 K)][Rescan (parallel) , 0.0008396 secs][weak refs processing, 0.0000138 secs][class unloading, 0.0005404 secs][scrub symbol table, 0.0006169 secs][scrub string table, 0.0004903 secs][1 CMS-remark: 8511K(13696K)] 10108K(19840K), 0.0039567 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
//STW阶段,YG occupancy:年轻代占用及容量
//[Rescan (parallel):STW下的存活对象标记
//weak refs processing: 弱引用处理
//class unloading: 卸载用不到的class
//scrub symbol(string) table:
//cleaning up symbol and string tables which hold class-level metadata and
//internalized string respectively
//CMS-remark: 8511K(13696K): 阶段过后的老年代占用及容量
//10108K(19840K): 阶段过后的堆占用及容量
[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.005/0.005 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
//标记已经完成,进行并发清理
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
//重置内部结构,为下次GC做准备
[GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0015790 secs]
//young -> 年轻代 Evacuation-> 复制存活对象
//initial-mark 混合回收的阶段,这里是YGC混合老年代回收
[Parallel Time: 1.5 ms, GC Workers: 1] //一个GC线程
[GC Worker Start (ms): 92635.7]
[Ext Root Scanning (ms): 1.1]
[Update RS (ms): 0.0]
[Processed Buffers: 1]
[Scan RS (ms): 0.0]
[Code Root Scanning (ms): 0.0]
[Object Copy (ms): 0.1]
[Termination (ms): 0.0]
[Termination Attempts: 1]
[GC Worker Other (ms): 0.0]
[GC Worker Total (ms): 1.2]
[GC Worker End (ms): 92636.9]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.0 ms]
[Other: 0.1 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.0 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.0 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.0 ms]
[Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
//以下是混合回收其他阶段
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0000078 secs]
[GC concurrent-mark-start]
//无法evacuation,进行FGC
[Full GC (Allocation Failure) 18M->18M(20M), 0.0719656 secs]
[Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)], [Metaspace: 38
76K->3876K(1056768K)] [Times: user=0.07 sys=0.00, real=0.07 secs]
需要调优的情况一般是运维团队首先受到报警信息(CPU Memory)
试验用程序:
import java.util.List;
import java.util.LinkedList;
public class HelloGC {
public static void main(String[] args) {
System.out.println("HelloGC!");
List list = new LinkedList();
for(;;) {
byte[] b = new byte[1024*1024];
list.add(b);
}
}
}
区分概念:内存泄漏memory leak,内存溢出out of memory
java -XX:+PrintCommandLineFlags HelloGC
java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC
(相关参数含义查看上文)

GC默认就是YGC,清理年轻代
(打印信息没截完)
java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags HelloGC


(打印信息没能截完)
用UseConcMarkSweepGC 垃圾回收次数会多很多
java -XX:+PrintFlagsInitial 默认参数值
java -XX:+PrintFlagsFinal 最终参数值
java -XX:+PrintFlagsFinal | grep xxx 找到对应的参数
java -XX:+PrintFlagsFinal -version |grep GC
1、代码
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 从数据库中读取信用数据,套用模型,并把结果进行记录和传输
*/
public class T15_FullGC_Problem01 {
private static class CardInfo {
BigDecimal price = new BigDecimal(0.0);
String name = "张三";
int age = 5;
Date birthdate = new Date();
public void m() {}
}
private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
new ThreadPoolExecutor.DiscardOldestPolicy());
public static void main(String[] args) throws Exception {
executor.setMaximumPoolSize(50);
for (;;){
modelFit();
Thread.sleep(100);
}
}
private static void modelFit(){
List taskList = getAllCardInfo();
taskList.forEach(info -> {
// do something
executor.scheduleWithFixedDelay(() -> {
//do sth with info
info.m();
}, 2, 3, TimeUnit.SECONDS);
});
}
private static List getAllCardInfo(){
List taskList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
CardInfo ci = new CardInfo();
taskList.add(ci);
}
return taskList;
}
}
2、运行程序:java -Xms200M -Xmx200M -XX:+PrintGC com.mashibing.jvm.gc.T15_FullGC_Problem01
3、出现报警信息后,top命令观察到问题:内存不断增长 CPU占用率居高不下

(环境问题,假如Java进程出问题了,且pid是16222)
4、top -Hp 观察进程中的线程,哪个线程CPU和内存占比高

5、jps定位具体java进程
6、jstack 定位线程状况,重点关注:WAITING BLOCKED
这里Java进程有问题,且pid是1591(环境一致的话上面top命令能查出占用资源多的pid是1591)

waiting on <0x0000000088ca3310> (a java.lang.Object)
说明出现锁等待了
这里就能说明为什么阿里规范里规定,线程的名称(尤其是线程池)都要写有意义的名称,有意义的名称能快速定位问题。
6、jinfo pid 打印进程的相关信息

(没截全)
7、jstat -gc 动态观察gc情况
jstat -gc 4655 500 : 每个500个毫秒打印GC的情况

阅读GC日志发现频繁GC,查看GC dump日志工具:
arthas观察 / jconsole/jvisualVM/ Jprofiler(收费,最好用)
8、jmap - histo 4655 | head -20,查找有多少对象产生,查出前20个(面试时可以说用这个)

9、jmap -dump:format=b,file=xxx pid :(面试时不要说用这个)
线上系统,内存特别大,jmap执行期间会对进程产生很大影响,甚至卡顿(电商不适合)
1:设定了参数HeapDump,OOM的时候会自动产生堆转储文件
2:很多服务器备份(高可用),停掉这台服务器对其他服务器不影响
3:在线定位(一般小点儿公司用不到)
除了使用jmap命令,还可以通过以下方式:
java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError com.mashibing.jvm.gc.T15_FullGC_Problem0110、获取到dump文件之后可以使用MAT / jhat /jvisualvm 进行dump文件分析,参考这博客:https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html
使用 jhat -J-mx512M xxx.dump 解析后,访问 http://ip:7000,访问分析dump文件,
可以使用OQL查找特定问题对象
11、找到代码的问题