网站首页 > 技术文章 正文
首先是大家都要提到的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扫描老年代空间时,只需要去扫描脏的卡片的对象,不需要扫描整个老年代空间
几种基础的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会和用户线程一起运行。
查看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
猜你喜欢
- 2024-10-19 Java高频面试题之Linux(java面试 linux)
- 2024-10-19 性能测试能力提升-JVM GC监控和优化
- 2024-10-19 排查GC问题常用的工具(排查问题的方式有哪些)
- 2024-10-19 开发好物推荐8之自动化部署插件,再也不用登录SSH
- 2024-10-19 20道阿里巴巴中高级java面试题详解,把这些弄懂offer拿到你手软
- 2024-10-19 Java Web实战篇:发布和运维必备的12条Linux命令
- 2024-10-19 Java工程师常用Linux命令大全(linux javac)
- 2024-10-19 玩转linux下启动jar和关闭jar方式(java程序员必看)
- 2024-10-19 超详细的EFK安装部署教程--环境准备篇
- 2024-10-19 为什么阿里架构师是这样定位Java性能问题的?这几点总结的很详细
- 最近发表
- 标签列表
-
- cmd/c (64)
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- sqlset (64)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)