上一节说到方法区和堆是有大小的,内容太多不够的处理,也就是今天学习的内容——垃圾回收
在学习垃圾回收之前,先学习JVM内存模型。
JVM内存模型
JVM内存模型又称内存区域、运行时数据区域。就是我们上一节所说的Runtime Data Area,最主要的内存区域就是方法区和堆内存。
先来分析一下new ThreadDemo()这个对象
- 首先,所有刚创建出来的对象一般都在Eden区。先在Young区的Eden区中分配ThreadDemo对象。
- 如果经过了一次Young GC,ThreadDemo对象依然没有被回收掉,此时,ThreadDemo对象进入Survivor区中S0或S1其中一块。(假如进入S0)
- 再次进行Young GC,ThreadDemo对象依然没有被回收掉,ThreadDemo对象进入S1。
- 再次进行Young GC,ThreadDemo对象依然没有被回收掉,ThreadDemo对象进入S0。
- 每次进行GC 对象没有被回收掉,年龄age+1。
- 如果ThreadDemo对象一直没有被回收掉,并且age>15,就会进去Old区
方法区回收比较少,主要是堆内存的回收
Full GC= Metaspace GC + Old GC + Young GC
Young GC后依然有空间碎片的问题,如果想要分配一个大一点的对象,发现没有连续空间,把Eden区中存活的对象复制到survivor(S0/S1)再把Eden区清空
特殊情况
- 对象很大,超过1/2Eden区大小,对象直接在Old区分配;Eden区空间不够ThreadDemo对象分配内存,会触发一次Young GC。
- S0区域不够
- 触发Young GC(Minor GC)
- 向Old区借一点空间给存活的对象使用(担保机制)
- Old区不够
- 触发Old GC(Major GC)
- 还是不够,抛出OOM(OutOfMemoryError)
什么时候进行垃圾回收
- Eden区或者S区不够,触发Young GC
- Old区不够,触发Old GC
- Metaspace不够,触发Metaspace GC
- System.gc() 通知建议JVM垃圾回收
- Full GC触发时机
- Major GC触发Minor GC
垃圾收集器优化
优化思路
- 回收次数尽可能的少
- 垃圾回收的时候,停顿时间要短(垃圾回收线程暂停业务代码线程的时间)
- 吞吐量要高(业务代码执行时间/(业务代码执行时间+垃圾回收时间))
垃圾收集器
- Serial Collector
- 单线程收集,适合于单核CPU场景,适用于内存小的场景。例如100MB
- 使用:-XX:+UseSerialGC
- 会暂停业务代码的线程(stop the world)
- 新生代的serial:复制算法;老年代的serial:标记-整理算法
- Parallel Collector
- 更加关注吞吐量,多线程进行垃圾收集,也会stw
- 使用:-XX:+UseParallelGC
- 新生代:复制算法;老年代:标记-整理算法
- CMS、G1、ZGC
- 三者都是关注停顿时间,停顿时间越来越短(并没有完全暂停用户代码的线程)
垃圾回收算法
- 复制算法
Eden区-->Survivor区,解决了我们前面说到的YoungGC后空间碎片问题
新生代区域适合使用复制算法:因为新生代对象生命周期比较短,大部分对象会被回收掉。所以我们可以把少部分对象复制到保留的空间,然后清除已使用空间。
- 标记-清除算法
- 扫描整个内存区域,并且标记清除之后,会存在空间碎片问题。
- 标记-整理算法
- 扫描整个内存区域,整理比较耗时,但会解决空间碎片问题。
老年代区域使用标记-整理 | 标记-清除算法,因为老年代渔区对象生命周期长,如果用复制算法比较耗时,不如先做一个标记到后面在选择删除或者整理。
了解完垃圾回收算法之后,那么什么样的对象可以成为垃圾对象呢?
什么样的对象可以称为垃圾对象
我们通过判断ThreadDemo对象是否有用,怎么判断呢?
- 可达性分析(从GC Root出发)
- GC Root是什么样的对象或者变量才可以呢??
- 局部变量表中的局部变量
- 生态城成员变量
- 方法区中的常量
- Thread进程
- ClassLoader加载的一些对象或者变量
- GC Root是什么样的对象或者变量才可以呢??
- 引用计数
- 看是否有引用指向它
- 但是也会有问题——循环引用
- 看是否有引用指向它
JVM参数
-
-X
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
- -Xint 解释执行
- -Xcomp 编译执行
- -Xmixed 解释+编译
-
-XX
- -XX:name = value
- -XX: InitialHeapSize = 100M 堆内存大小 -Xms 100M
- -XX: MaxHeapSize = 100M 堆内存最大的大小 -Xmx 100M
- -XX:ThreadStackSize = 100 Java虚拟机栈的深度 -Xss 100
- -XX:+ -XX:-
- -XX:+/-UseG1GC 开启/关闭
- -XX:name = value
-
其他参数
- -version
- -cp
- -help
设置JVM参数
-
开发工具
-
修改默认参数:
window-->preferences-->java-->Installed JREs-->Edit-->Default VM arguments -
修改运行时参数
Run As -->Run Configurations-->Arguments-->VM arguments -
修改eclipse配置文件 eclipse.ini。
-
-
启动jar包时
java -jar xxx.jar -XX:+UseG1GC -
中间件
有些中间件是以JVM来运行的,可以再bin/启动中间件 设置。 -
实时修改某些jvm参数的值
- 也不是所有的值都是可以实时修改的噢 利用-XX:+PrintFlagsFinal 打印jvm参数,{manageable}才是可以实时修改的。
- jinfo -flag -XX:name = value pid 修改指定进程(pid)的指定参数的值。
常见的JDK命令
- 查看进程 jps -l
- jinfo 查看虚拟机配置参数信息,也可以用来调整参数
- jinfo pid
- jinfo -flag name pid
- jinfo -flag name=value pid
- jinfo -flag +/- name pid
- jstack 查看指定进程中线程的情况
- jstat
- jstat -class pid 查看某个java进程中类装载的信息
- jstat -gc pid 查看JVM中堆的垃圾收集情况统计(以下是各参数及含义)
- S0U(S0的大小) S1C(S1的大小) EC(Eden区大小)
- S0U(S0使用大小) S1U(S1使用大小) EU(Eden区使用大小)
- OC(Old区的大小) OU(Old区使用大小) MC(方法区的大小)MU(方法区使用大小)
- CCSC(压缩类空间大小) CCSU(压缩类空间使用大小)
- YGC(Young GC回收次数)YGCT(Young GC回收消耗时间)
- FGC(Old GC回收次数) FGCT(Old GC回收消耗时间)
- GCT(垃圾回收消耗总时间)
- jmap
- jamp -heap pid 查看堆内存详情的快照信息
- jmap -dump :[live] format=b, file= 生成Java虚拟机的堆快转储快照dump文件,live参数可选,如果指定,则只转储堆中的活动对象,如果没有指定,转储堆中所有对象jmap -dump:live,format=b,file=d:\hprof pid
- 通过java jvisual VM 打开,自动分析快照文件
常见的JVM分析工具
JDK自带的
- jvisualvm
- 在jdk\bin\jvisualvm.exe
- jconsole
第三方工具
- arthas.jar
堆dump文件分析工具
- MAT
- heaphero
- perfma
GC日志查看相关的
生成GC日志命令
- -XX:+PrintGC 输出GC日志
- -XX:+PrintGCDetails 输出GC的详细日志
- -XX:+PrintGCTimeStamps 输出GC的时间戳
- -Xloggc:gc.log 日志文件的输出路径
分析日志文件的工具
- viewergc.jar
- gceasy.io