首先是大家都要提到的GC的基础算法:标记清除,标记整理,复制,分代。这些算法的第一步都是做的一件事: 标记(Mark)。

JVM的标记算法采用了根搜索算法(Root Tracing)。根有几种:

1. JVM栈的Frame里面的引用

2. 静态类,常量的引用

3. 本地栈中的引用

4. 本地方法的引用

一般我们能控制的就是JVM栈中的引用和静态类,常量的引用。标记也分为几个阶段,比如

1. 标记直接和根引用的对象

2. 标记间接和根引用的对象

3. 由于分代算法,被老年代对象所引用的新生代的对象

对于第三种,JVM采用了Card Marking(卡片标记)的方法,避免了在做Minor GC时需要对整个老年代扫描。具体的方法如下:

1. 将老年代的内存分片,1个片默认是512byte

2. 如果老年代的对象发生了修改,就把这个老年代对象所在的片标记为脏 dirty。或者老年代对象指向了新生代对象,那么它所在的片也会被标记为dirty

3. 没有标记为脏的老年代片它没有指向新的新生代对象,所以可以不需要去扫描

4. Minor GC扫描老年代空间时,只需要去扫描脏的卡片的对象,不需要扫描整个老年代空间

所以做Minor GC时标记的时间 = T(stack_scan) + T(card_scan) + T(old_root_scan).

T(stack_scan): 级联扫描在JVM栈里的根的时间

T(card_scan): 级联扫描卡表中脏卡片的时间

T(old_root_scan): 扫描在老年代中的直接的根的时间。注意是直接的根,不会去级联扫描老年代的对象。因为扫描都是从根开始的,一开始不知道根到底是在老年代还是新生代

和Card Marking相关的一个重要的JVM参数是-XX:UseCondCardMark 。使用这个参数的原因是在高并发的情况下,Card标记为脏的操作本身就存在着竞争,使用这个参数可以避免卡片被重复标记为脏,从而提高性能。

说完了标记,下面提一下几种基础的GC算法,没有什么新的点,直接引用网上的图

标记-清除算法

复制算法

标记--整理算法

分代算法是将对象分为新生代和老年代,然后使用不同的GC策略来进行回收,提高整体的效率。

由于新生代的大部分对象都会在一次Minor GC中死亡,存活的对象很少,所以新生代的GC收集器都采用了复制算法。新生代分为Eden + S0 + S1. S0和S1就是用来实现复制的,在任何一次Minor GC后,S0和S1总是只有一个区域有数据,另一个区域为空,以便于下一次复制使用

当新生代空间不能满足大对象分配时,老年代空间为它提供了分配担保,大对象可以直接进入老年代。有两个JVM参数可以控制新生代进入老年代的门槛:

PretenureSizeThreshold: 单位是B,设置了对象大小的阀值

MaxTenuringThreshold: 设置了进入老年代的年龄的阀值

老年代对象一般都是存活时间久,老年代的空间本来就大,所以没有更多空间来提供分配担保,所以老年代一般采用标记--清理或者标记--整理算法。

下面这张图很好地介绍了JDK6的各种GC收集器以及各自的特点:

1. 新生代都采用复制算法

2. CMS采用了标记--清除算法,由于标记清除算法会生成内存碎片,所以JVM提供了参数来使CMS可以在几次清除后作一次整理

-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

3. Serial Old(MSC)和Parallel Old都采用标记整理算法

4. UseSerialGC默认会在新生代使用Serial收集器,在老年代使用Serial Old收集器,这两个都是单线程的收集器

5. UseConcMarkSweepGC默认会再新生代使用ParNew收集器,这是个并发的收集器。在老年代会使用CMS + Serial Old收集器,当CMS失败的时候,会启用Serial Old做FULL GC

6. UseParallelOldGC默认会在新生的使用Parallel Scavenge收集器,在老年代使用Parallel Old收集器。这两个收集器都是吞吐量优先,所谓吞吐量优先就是它可以严格控制GC的时间,从而保证吞吐量。但是吞吐量提高了,新生代和老年代的空间就是动态调整的,而不是按照初始配置的大小。因为单位时间清除的垃圾量近乎一个常量,既然要保证时间,那么必须保证垃圾总量,而垃圾总量可以通过新生代和老年代的大小来控制的

7. 对于和用户有交互的应用,比如Web应用,一个重要的考量是系统的响应时间,要保证系统的响应时间就要保证由GC导致的stop the world次数少,或者让用户线程和GC线程一起运行。所以Web应用是使用CMS收集器的一个重要场景。CMS减少了stop the world的次数,不可避免地让整体GC的时间拉长了

8. 对于计算密集型的应用可能会考虑计算的吞吐量,这时候可以使用Parallel Scavenge收集器来保证吞吐量

9. Serial, ParNew, Parallel Scanvange, Parallel Old, Serial Old全程都会Stop the world,JVM这时候只运行GC线程,不运行用户线程

10. CMS主要分为 initial Mark, Concurrent Mark, ReMark, Concurrent Sweep等阶段,initial Mark和Remark占整体的时间比较较小,它们会Stop the world. Concurrent Mark和Concurrent Sweep会和用户线程一起运行。

下面这张图对GC的日志信息做了说明:

关于JVM调优的各种参数设置,网上一抓一大把,这里不多说了。有一个调优的整体的原则:

1. 先做一个JVM的性能测试,了解当前的状态

2. 明确调优的目标,比如减少FULL GC的次数,减少GC的总时间,提高吞吐量等

3. 调整参数后再进行多次的测试,分析,最终达到一个较为理想的状态。各种参数要根据系统的自身情况来确定,没有统一的解决方案

将各种工具的文章页很多,这里从解决问题的角度出发列出几个。

查看JVM启动参数

1. jps -v

2. jinfo -flags pid

3. jinfo pid -- 列出JVM启动参数和system.properties

4. ps -ef | grep java

查看当前堆的配置

1. jstat -gc pid 1000 3  -- 列出堆的各个区域的大小

2. jstat -gcutil pid 1000 3 -- 列出堆的各个区域使用的比例

3. jmap -heap pid  -- 列出当前使用的GC算法,堆的各个区域大小

查看线程的堆栈信息

1. jstack -l pid

dump堆内的对象

1. jmap -dump:live,format=b,file=xxx pid

2. -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=xxx  -- 设置JVM参数,当JVM OOM时输出堆的dump

3. ulimit  -c unlimited  -- 设置Linux ulimit参数,可以产生coredump且不受大小限制。之前在线上遇到过一个极其诡异的问题,JVM整个进程突然挂了,这时候依靠JVM本身生成dump文件已经不行了,只有依赖Linux,让系统来生成进程挂掉的core dump文件

使用jstack 可以来获得这个coredump的线程堆栈信息:  jstack "$JAVA_HOME/bin/java" core.xxx > core.log

获得当前系统占用CPU最高的10个进程,线程

ps Hh -eo pid,tid,pcpu,pmem | sort -nk3 |tail > temp.txt

图形化界面
1. jvisualvm 里面有很多插件,比如Visual GC,可以可视化地看到各个堆区域时候的状态,从而可以对整体GC的性能有整体的认识

就说到这吧,有遗漏的后面再补充

参考资料:  Understanding GC pauses in JVM, HotSpot's minor GC.

《深入理解JVM》

《Think in GC》

聊聊JVM(一)相对全面的GC总结相关推荐

  1. 聊聊JVM(八)说说GC标记阶段的一些事

    这篇说说GC标记阶段的一些事情,尝试把一些概念说清楚.本人不是研究JVM实现的,如果表述有问题请查看参考资料进一步学习,推荐高级语言虚拟机圈子 ,里面有很多好的文章值得一看. GC最简单的理解就是先把 ...

  2. 全面的GC原理及调优

    本文介绍 GC 基础原理和理论,GC 调优方法思路和方法,基于 Hotspot jdk1.8,学习之后你将了解如何对生产系统出现的 GC 问题进行排查解决. 图片来自 Pexels 内容主要如下: G ...

  3. 聊聊JVM(二)说说GC的一些常见概念

    1. 什么时候会触发Minor GC? Eden区域满了,或者新创建的对象大小 > Eden所剩空间 CMS设置了CMSScavengeBeforeRemark参数,这样在CMS的Remark之 ...

  4. 可能是最全面的 Java G1学习笔记

    转载自 可能是最全面的 Java G1学习笔记 引子 最近遇到很多朋友过来咨询G1调优的问题,我自己去年有专门学过一次G1,但是当时只是看了个皮毛,因此自己也有不少问题.总体来讲,对于G1我有几个疑惑 ...

  5. 【JVM】最全笔记(黑马+尚硅谷+张龙整合笔记)

    本身整合了如下视频的笔记,并进行了整理:尚硅谷周阳.张龙.黑马程序员 黑马ppt非常好:https://download.csdn.net/download/hancoder/12834607 本文及 ...

  6. (八)JVM成神路之GC分区篇:G1、ZGC、ShenandoahGC高性能收集器深入剖析

    引言 在<GC分代篇>中,我们曾对JVM中的分代GC收集器进行了全面阐述,而在本章中重点则是对JDK后续新版本中研发推出的高性能收集器进行深入剖析,但在开始前,先来看看JDK的发布记录中关 ...

  7. 最全面的Java面试题-----是你更好的掌握java知识

    最全面的Java面试题-----是你更好的掌握java知识 目录 l     概念题--- 1 一.        JAVA基础--- 1 1)      面向对象的特征--- 1 2)      什 ...

  8. JVM的三种常见GC:Minor GC、Major GC与Full GC

    文章目录 JVM的GC JVM GC的种类 GC的触发机制 年轻代GC(Minor GC)触发机制 老年代GC(Major GC/Full GC)触发机制 Full GC触发机制 为什么需要把Java ...

  9. JVM知识点(全,一篇搞定)

    1.什么是JVM JVM是Java程序运行的平台,它就像一台虚拟出来的计算机一样,负责执行Java编译好的字节码文件.JVM具有非常严格的实现规范,大多数操作系统都可以安装JVM,为Java语言的跨平 ...

最新文章

  1. React 项目---class 创建组件 (11)
  2. 记住这53个要点提高PHP编程效率
  3. 详解PHP spl_autoload_register()函数
  4. 编程中的蛇形填空问题_在线编程问题当中的蛇形矩阵问题
  5. 物理化学 化学 动力学(下)
  6. js 获取td高度_JS或jQuery获取宽高度
  7. Redis分布式基础主从同步
  8. python之路-操作系统
  9. sublime Text 2使用小技巧
  10. 《树莓派Python编程入门与实战》——1.2 获取树莓派
  11. 关于序列化的几个注意点
  12. 为什么你的温湿度传感器测不准?
  13. 【渝粤教育】电大中专学前教育学_1作业 题库
  14. 数字游戏(数位dp)
  15. margin的百分比参照物
  16. 记录小米设备事件获取
  17. Supervisor守护Java进程_使用Supervisor来守护我们的服务
  18. 12、TCP、IP体系---ICMP与DHCP协议
  19. 【力扣】数据结构入门【7天32题数据结构入门】
  20. 鼎普计算机保密检查系统,敏感电子信息集中管控平台系统

热门文章

  1. mc显示服务器生命值,[1.7-1.8]CombatIndicator — 全息显示攻击伤害的数值 让我的世界服务器更有游戏感...
  2. python 输入参数获取_python获取输入参数
  3. JavaWeb中的乱码解决方法总结
  4. Linux网络深入DHCP、FTP原理和配置方法(详细图解)
  5. 三星a7108android 7.0,三星A7108系统运行速度变慢变卡顿了_怎么进行具体的刷机教程...
  6. mysql dataset_数据库 dataset database
  7. dede问答模块 那个php文件相对重要,DEDE5.7 问答模板的修改技巧
  8. mysql拒绝访问root用户_设置mysql的root用户允许远程登录
  9. python eel 多线程_利用Eel使JavaScript调用Python程序
  10. 怎么复制远程服务器上的文件夹,Linux系统复制文件/文件夹到远程服务器