jvm gc相关资料

  1. 垃圾收集器分为,查找->标记->回收->整理,这个整理不是必须的,只有分配内存不够了,才会触发整理碎片,减少小内存碎片, 标记后不会立即清理,标记的规则以前是有个引用计数器,达到0的时候代表没有引用,不过如果发生了环路引用,就会导致一直不会被标记,后面改成了引用可达性, 引用可达分为几种类型,强可达,软可达,弱可达,虚可达,不可达,如果是不可达,就会被标记

  1. 一般可分配内存不足了,会触发full gc,全部检查一遍,full gc,执行的过程是最长的,在以前还会造成程序的停顿,所以尽量不要让程序执行full gc

  1. System.gc() 主动建议jvm去触发 full gc,不过这个东西尽量别用它,-XX:+ DisableExplicitGC=false 可以禁止调用full gc,防止有人无聊写主动触发gc的代码

  1. 通常在需要导出堆转存文件之前 触发一次gc是比较好的

  1. jcmd [pid] GC.run 或者 jconsole 上点击gc 触发

  1. jstat -gcutil process_id 1000 这里的-gcutil 可以换成其它的, 这句主要是一直打印消耗在GC上的时间

  1. Survivor 空间 是为了让刚创建的对象再新生代能存货更长时间,避免直接晋级到老年代,导致老年代经常gc,-XX:InitialSurvivorRatio 控制这个空间的初始大小,也有固定大小的方法

  1. 对需要分配大量大型对象的应用,TLAB 空间的调整就变得必不可少(不过,通常情况下,我们更推荐在应用程序中使用小型对象的做法)

两种gc类型

  1. Full gc,是对整个堆进行整理,不管是,新生代,老年代,永久代(永久代在jdk8被移除了)

  1. Mionr gc 新生代GC ,整个gc是比较频繁,主要是去整理那些临时被创建的对象,像那种多次经历过mionr gc都没有被移除的对象,会被转到老年代里面去

不同的垃圾收集器

  1. Serial垃圾收集器

  • 配置低的机器使用的,例如32位Windows, XX:+UseSerialGC 可以启动这个收集器

  • 通常仅用于单cpu机器上

  • Serial 收集器最适用于应用程序的内存使用少于 100 MB 的场景

  1. Throughput垃圾收集器

  • Throughput 收集器是 Server 级虚拟机(多 CPU 的 Unix 机器以及任何 64 位虚拟机)的默认收集器

  • Jdk7默认收集器,进行垃圾回收的时候会暂停应用线程

  • 多线程的方式进行回收

  • -XX:+UseParallelOldGC,-XX:+UseParallelGC 启用

  • 它能最大化应用程序的总吞吐量,但是有些操作可能遭遇较长的停顿,在cpu差的情况下可以尝试

  • XX:MaxGCPauseMillis 设置gc时能接受的停顿最大时间,注意设置的越小 gc次数越多,虽然每次都比较快,标志的优先级最高:如果设置了这个值,新生代和老年代会随之进行调整

  • -XX:GCTimeRatio 设置线程花多少时间去执行gc,这是个百分比设置,与比如99,百分之1的线程时间花在gc上,百分之99花在应用线程,百分比只是最多花这些时间,而不是一定花这么多时间

  1. CMS收集器

  • 为了消除执行gc的时候造成的应用线程完全停止的问题

  • 多线程的方式回收,mionr gc的时候会暂停应用线程,full gc不会停止应用线程

  • -XX:+UseConcMarkSweepGC 、-XX:+UseParNewGC 启动

  • 比较依赖cpu资源,常用用于 多cpu的情况下

  • CMS 收集器能够在应用线程运行的同时并行地对老年代的垃圾进行收集。如果 CPU 的计算能力足以支撑后台垃圾收集线程的运行,该算法能避免应用程序发生 Full GC

  • 要处理并发gc时失效是比较重要的,主要是新生代晋级到老年代,老年代却存不下了,导致老年代的gc,一方面可以提高堆内存,也可以 CMSInitiatingOccupancyFraction,UseCMSInitiatingOccupancyOnly参数来设置,让它对老年代达到指定存量的时候就开始出发gc

  • Cms默认不会对永久代进行gc -XX:+CMSPermGenSweepingEnabled,-XX:CMSInitiatingPermOccupancyFraction,-XX:+CMSClassUnloadingEnabled开启永久代与老年代一样的gc

  1. G1

  • 尽量缩短处理超大堆(大于4g)时产生的停顿,将堆划分为若干个区域

  • Mionr gc依然会暂停应用线程,会将存活下来的对象看情况移动到老年代,不移动到老年代的对象就属于Survivor(幸存者)依然存储在新生代

  • 多线程方式回收,full gc大多数下也是不需要暂停应用线程,由于是将老年代划分为多个区域,通常在回收的时候是将幸存下来的对象都移动到另一个区域,这样的好处是,在进行回收的时候也顺便整理的内存空间,不易产生内存碎片

  • -XX:+UseG1GC 启动

  • G1 收集器也能在应用线程运行的同时并发地对老年代的垃圾进行收集,在某种程度上能够减少发生 Full GC 的风险。G1 的设计理念使得它比 CMS 更不容易遭遇 Full GC

收集器的选择

  1. 一般情况下,堆空间小于4g,CMS的性能要比G1好,因为CMS的算法比G1简单,适合堆小一点的情况下,因为G1会分割大堆,所以它更适合4G以上的堆内存下选择

  1. Throughput 年代比较久,基本上很成熟,G1比较新也许会碰到问题

  1. 堆小于100m,选Serial

jvm调优参数

-Xms	初始堆大小。如:-Xms256m
-Xmx	最大堆大小。如:-Xmx512m
-Xmn	新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%
-Xss	JDK1.5+ 每个线程堆栈大小为 1M,一般来说如果栈不是很深的话, 1M 是绝对够用了的。
-XX:NewRatio	新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3
-XX:NewSize 新生代空间初始大小
-XX:MaxNewSize 新生代空间最大,大小 优先级高于 NewRatio 
-XmnN 同时设置 NewSize,MaxNewSize 值一样
-XX:SurvivorRatio	新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10
-XX:PermSize	永久代(方法区)的初始大小 jdk1.8中已经不存在了,取而代之的是metaspace元空间,元空间没有大小限制
-XX:MaxPermSize	永久代(方法区)的最大值 jdk1.8中已经不存在了,取而代之的是metaspace元空间,元空间没有大小限制
-XX:+PrintGCDetails	打印 GC 信息
-XX:+PrintGCTimeStamps  GC操作之间的时间
-Xloggc gc日志文件输出,可以通过另外的参数设置GC日志打印的循环,防止GC文件太大了
-XX:+HeapDumpOnOutOfMemoryError	让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析用
-XX:ParallelGCThreads 垃圾回收处理线程,为了尽可能的利用多cpu,这些线程会创建在不同的cpu上,不要盲目设置太多
-XX:-UseAdaptiveSizePolicy 关闭自适应调整,程序默认会进行自适应调整,对一些内存大小的设置也许在不关闭的情况下,不起作用
-XX:+PrintAdaptiveSizePolicy 查看自适应调整情况

注意:PermSize永久代的概念在jdk1.8中已经不存在了,取而代之的是metaspace元空间,元空间没有大小限制,当认为执行永久代的初始大小以及最大值是jvm会给出如此下提示:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=30m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=30m; support was removed in 8.0

gc日志分析

  1. -XX:+PrintGCDetails=true 开启打印GC日志

  1. 可以使用工具 jvisualvm 然后在菜单上选择 tools -> Pulgins 添加Visual GC 有的可能是中文,然后就可以选择监控程序后,多了一栏 Visual GC,用于在线查看gc

下面是不使用工具的方式,主要是读离线gc文件

minor gc日志解读

full gc日志解读

下面是举实例解读

  1. 先准备好代码例子

  1. 运行结果

  1. 分析

  • 从打印结果可以看出,堆中新生代的内存空间为 18432K ( 约 18M ),eden 的内存空间为 16384K ( 约 16M),from / to survivor 的内存空间为 2048K ( 约 2M)。

  • 这里所配置的 Xmn 为 20M,也就是指定了新生代的内存空间为 20M,可是从打印的堆信息来看,新生代怎么就只有 18M 呢? 另外的 2M 哪里去了? 别急,是这样的。新生代 = eden + from + to = 16 + 2 + 2 = 20M,可见新生代的内存空间确实是按 Xmn 参数分配得到的。而且这里指定了 SurvivorRatio = 8,因此,eden = 8/10 的新生代空间 = 8/10 * 20 = 16M。from = to = 1/10 的新生代空间 = 1/10 * 20 = 2M。堆信息中新生代的 total 18432K 是这样来的: eden + 1 个 survivor = 16384K + 2048K = 18432K,即约为 18M。因为 jvm 每次只是用新生代中的 eden 和 一个 survivor,因此新生代实际的可用内存空间大小为所指定的 90%。因此可以知道,这里新生代的内存空间指的是新生代可用的总的内存空间,而不是指整个新生代的空间大小。另外,可以看出老年代的内存空间为 40960K ( 约 40M ),堆大小 = 新生代 + 老年代。因此在这里,老年代 = 堆大小 - 新生代 = 60 - 20 = 40M。 最后,这里还指定了 PermSize = 30m,PermGen 即永久代 ( 方法区 ),它还有一个名字,叫非堆,主要用来存储由 jvm 加载的类文件信息、常量、静态变量等。

  • 回到 doTest() 方法中,可以看到代码在第 14、18、19 这三行中分别申请了一块 1M 大小的内存空间,并在 16 和 20 这两行中分别显式的调用了 System.gc()。从控制台打印的信息来看,每次调 System.gc(),是先进行 Minor GC,然后再进行 Full GC。

  • 第 16 行触发的 Minor GC 收集分析: 从信息 PSYoungGen : 1351K -> 288K,可以知道,在第 14 行为 bytes 分配的内存空间已经被回收完成。引起 GC 回收这 1M 内存空间的因素是第 15 行的 bytes = null; bytes 为 null 表明之前申请的那 1M 大小的内存空间现在已经没有任何引用变量在使用它了,并且在内存中它处于一种不可到达状态 ( 即没有任何引用链与 GC Roots 相连 )。那么,当 Minor GC 发生的时候,GC 就会来回收掉这部分的内存空间。

  • 第 16行触发的 Full GC 收集分析: 在 Minor GC 的时候,信息显示 PSYoungGen : 1351K -> 288K,再看看 Full GC 中显示的 PSYoungGen : 288K -> 0K,可以看出,Full GC 后,新生代的内存使用变成0K 了,那么这 288K 到底哪去了 ? 难道都被 GC 当成垃圾回收掉了 ? 当然不是了。我还特意在 main 方法中 new 了一个 Test 类的实例,这里的 Test 类的实例属于小对象,它应该被分配到新生代内存当中,现在还在调用这个实例的 doTest 方法呢,GC 不可能在这个时候来回收它的。

  • 接着往下看 Full GC 的信息,会发现一个很有趣的现象,PSOldGen: 0K -> 160K,可以看到,Full GC 后,老年代的内存使用从 0K 变成了 160K,想必你已经猜到大概是怎么回事了。当 Full GC 进行的时候,默认的方式是尽量清空新生代 ( YoungGen ),因此在调 System.gc() 时,新生代 ( YoungGen ) 中存活的对象会提前进入老年代。

  • 第 20行触发的 Minor GC 收集分析: 从信息 PSYoungGen : 2703K -> 1056K,可以知道,在第 18行创建的,大小为 1M 的数组被 GC 回收了。在第 19 行创建的,大小也为 1M 的数组由于 bytes 引用变量还在引用它,因此,它暂时未被 GC 回收。

  • 第 20 行触发的 Full GC 收集分析: 在 Minor GC 的时候,信息显示 PSYoungGen : 2703K -> 1056K,Full GC 中显示的 PSYoungGen : 1056K -> 0K,以及 PSOldGen: 160K -> 1184K,可以知道,新生代 ( YoungGen ) 中存活的对象又提前进入老年代了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值