admin管理员组文章数量:1578033
JVM+GC
- JDK8
- JVM内存结构
- GC作用域
- 常见垃圾回收算法
- GC Roots ?
- JVM 参数
- -XX:+ PrintGCDetails 打印解释
- -XX:SurvivorRatio=8
- -XX:NewRatio=2 设置老年代占比
- -XX:MaxTenuringThreshold 设置垃圾最大年龄
- 强引用 Reference
- 软引用 SoftReference
- 弱引用 WeakReference
- 虚引用 PhantomReference
- GC垃圾回收
- 四大垃圾回收方式
- 查看默认垃圾收集器
- 7大垃圾收集器
- 年轻代和老年代垃圾回收器对应关系图
- GC之Serial收集器
- GC之ParNew收集器
- GC之Parallel收集器
- GC之ParallelOld收集器
- GC之CMS收集器
- GC之SerialOld收集器
- GC之G1收集器
- 如何选择GC收集器
JDK8
JVM内存结构
GC作用域
常见垃圾回收算法
引用计数
复制拷贝
标记清除
标记整理
GC Roots ?
JVM 确定垃圾,根节点判断
JVM 参数
基本参数
JVM 参数
1.标配参数 -version -help
2.X参数
3.xx参数
booleen参数 -XX + 是 - 否
-XX:+ PrintGCDetails 打印GC日志
key 参数
-XX:MetaspaceSize=128m
Jinfo 查看配置项
jinfo -flag 配置项 进程编号
查看参数
原始的 -XX:+PrintFlagsInitial
修改的 -XX:+PrintFlagsFinal
打印命令行参数
java -XX:+PrintCommandLineFlags -version
-Xms 初始大小内存
-Xmx 最大分配内存
-Xss 1024k 栈内存
-Xmn 设置年轻代大小
-XX:MetaspaceSize 设置元空间大小,不在虚拟机,使用本地内存
一些值打印
System.out.println("电脑核心线程数" + Runtime.getRuntime().availableProcessors());
long totalMemory=Runtime.getRuntime().totalMemory();// 返回java虚拟机内存总量
long maxMemory=Runtime.getRuntime().maxMemory();//返回java虚拟机内存最大内存量
System.out.println("TOTAL MEMORY(-Xms) = " + totalMemory + " (字节) . " + (totalMemory / (double) 1024 / 1024) +"MB");
System.out.println("MAx MEMORY(-Xmx) = " + maxMemory + " (字节) . " + (maxMemory / (double) 1024 /1024) +"MB");
-XX:+ PrintGCDetails 打印解释
GC
Full GC
-XX:SurvivorRatio=8
设置新生代eden 和S0 / S1空间的比例
默认 -XX:SurvivorRatio=8 ,Eden:S0 :S1 8:1:1
-XX:SurvivorRatio=4 ,Eden:S0 :S1 4:1:1
-XX:NewRatio=2 设置老年代占比
设置年轻代和老年代在堆结构中的比例
默认 -XX:NewRatio=2 ,新生代占1,老年代2 ,年轻代占堆三分之一
-XX:NewRatio=4 ,新生代占1,老年代4 ,年轻代占堆五分之一
-XX:MaxTenuringThreshold 设置垃圾最大年龄
强引用 Reference
OOM了都不会回收
例子:
软引用 SoftReference
需要用java.lang.ref.SoftReference 类实现,让对象豁免一些垃圾回收。
系统内存充足 不会 被回收
系统内存不足 会 被回收
内存够用就保留,不够用就回收
应用读取大量本地图片
HashMap 保存路径和相应图片对象关联软引用映射关系,内存不足,会自动回收避免OOM
弱引用 WeakReference
只要执行垃圾回收,不管JVM空间是否足够,都会回收
虚引用 PhantomReference
顾名思义是形同虚设 ,需要和引用队列一起使用ReferenceQueue联合使用
GC垃圾回收
四大垃圾回收方式
-
串行垃圾回收期(Serial)它为单线程环境设计并只使用一个线程进行垃圾回收,会暂停所有用户线程
-
并行垃圾回收器(Paralle)多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于科学计算、大数据处理
-
并发垃圾回收期(CMS)用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程。互联网公司多用它,对响应时间有要求的场景
-
G1
查看默认垃圾收集器
7大垃圾收集器
年轻代和老年代垃圾回收器对应关系图
GC之Serial收集器
【串行回收器:Serial收集器】
一句话:一个单线程的收集器,在进行垃圾收集时候,必须停止其他所有工作线程直到它收集结束
串行收集器是最古老,最稳定及效率最高的收集器,只是用一个线程回收但其在垃圾收集进程中可能产生较长的停顿(Stop-The-Word 状态)。虽然收集垃圾过程中需要暂停所有其他工作线程,但是简单高效,对于限定单个CPU环境来说,没有线程交互的开销可以获得最高的单线程垃圾收集效率,因此Serial垃圾收集器依然是Client模式下默认的新生代垃圾收集器。
对应JVM参数 -XX:+UseSerialGC
开启后会使用:Serial(Young区用) + Serial Old (Old区用)的收集器组合
表示:新生代、老年代都会使用串行回收收集器,新生代使用复制算法,老年代使用 标记-整理算法
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseSerialGC (DefNew + Tenured)
GC之ParNew收集器
【ParNew 并行收集器】
一句话:使用多线程进行垃圾回收,垃圾收集时,会 Stop-the-world暂停其他所有工作线程直到它结束
PerNew收集器其实就是Serial收集器新生代的并行多线程版本,最常用应用场景配合老年代的CMS GC工作,其余行为和Serial收集器一样,PerNew垃圾收集器在垃圾收集过程中也要暂停其他所有工作线程。它是很多Java虚拟机运行在Server模式下新生代默认垃圾收集器
常用JVM参数:-XX:+UseParNewGC 启用ParNew收集器,只影响新生代的收集,不影响老年代
开启上述参数后,会使用 ParNew(young区用) + Serial Old的收集器组合,新生代使用复制算法,老年代使用标记-整理算法
但是,PartNew+ Tenured这样的搭配,java8不在推荐
Java HotSpot™ 64-Bit Server VM warning:
Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
备注
-XX:ParallelGCThreads 限制线程数量,默认开启和CPU数目相同的线程数
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParNewGC
GC之Parallel收集器
Parallel Scavenge收集器类似ParNew 也是一个新生代垃圾收集器,使用复制算法,也是一个并行多线程的垃圾收集器,俗称吞吐量优先收集器。一句话:串行收集器在新生代和老年代的并行化
它重点关注的是:
可控制吞吐量(Thoughput=运行用户代码时间 / (运行用户代码时间+垃圾收集时间),也即比如程序运行100分钟,垃圾收集时间1分钟,吞吐量99%)。高吞吐量意味着高效利用CPU的时间,多用于后台运算不需要太多交互的任务。
自适应调节策略也是ParallelScavenge收集器与ParNew收集器的一个重要区别。(自适应调节策略:虚拟机会根据当前系统运行情况收集性能监控信息,动态调整这些参数以提供最合适停顿时间(-XX:MaxGCPauseMills)或最大吞吐量)
常用JVM参数:-XX:+useParallelGC或-XX:+UseParallelOldGC(可互相激活)使用Parallel Scabvenge 收集器
开启该参数后:新生代使用复制算法,老年代使用标记-整理算法
多说一句:-XX:ParallelGCThreads=数字N 表示启动多少个GC线程
cpu>8 N=5/8
cpu<8 N=实际个数
GC之ParallelOld收集器
Parallel Old 收集器是Parallel Scavenge 的老年代版本,使用多线程的标记-整理算法,Parallel Old收集器在JDK1.6开始提供
在JDK1.6之前,新生代使用Parallel Scavenge 收集器只能搭配老年代Serial Old收集器,只能保证新生代吞吐量优先,无法保证整体吞吐量。在JDK1.6之前(Parallel Scavenge + Serial Old)
Parallel Old 正是为了在年老代同样提供吞吐量优先的垃圾回收器,如果系统对吞吐量要求比较高,JDK1.8可以考虑新生代Parallel Scavenge 和老年代Parallel Old收集器的搭配策略。在JDK1.8及后(Parallel Scavenge + Parallel Old)
JVM常用参数
-XX:+UseParallelOldGC 使用Parallel Old收集器,设置该参数后,新生代Parallel +老年代Parallel Old
GC之CMS收集器
CMS收集器(Concurrent Mark Sweep: 并发标记清除)是一种以获取最短回收停顿时间为目标的收集器。
适合应用在互联网站或者B/S系统的服务器上,这类应用尤其重视服务器响应速度,子网系统停顿时间最短。
CMS非常适合堆内存大、CPU核数多的服务器端应用,也是G1之前大型应用首选收集器
标记清除分为四布:
- 初始标记(CMS inital mark)
- 并发标记(CMS concurrent mark)和用户线程一起
- 重新标记(CMS remark)
- 并发清除(CMS concurrent sweep)和用户线程一起
开启该收集器的JVM参数:-XX:+UseConcMarkSweepGC 开启该参数会自动将-XX:+UseParNewGC打开
开启该参数后,使用ParNew(young区用) + CMS(Old区用) + Serial Old 的收集器组合,Serial Old将作为CMS出错的后备收集器
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC
优缺点
优:1.并发收集低停顿
缺:1. 并发执行,对CPU压力大 2. 采用标记清除算法导致大量碎片
GC之SerialOld收集器
Serial Old是Serial 垃圾收集器老年代版本,同样是单线程的收集器,使用标记-整理算法,收集器主要运行Client 的Java虚拟机默认老年代垃圾收集器
在Server 模式下,主要两个用途(了解,版本已经到8 及以后)
- 在Jdk1.5之前与新生代Parallel Scavenge 收集器搭配使用。(Parallel Scavenge + Serial Old)
- 作为老年代版本使用CMS收集器的后备垃圾收集方案。
GC之G1收集器
最大好处
最大好处化整为零,避免全内存扫描,只需要按照区域来进行扫描即可。
以前收集器特点
- 年轻代和老年代是各自独立且连续的内存块
- 年轻代收集使用单 eden + S0 + S1 进行复制算法
- 老年代收集必须扫描整个老年代区域
- 都是尽可能少而快速执行GC为原则
官网描述中,G1是一种服务器端垃圾收集器,应用在多服务器和大容量内存环境下,在实现高吞吐量的同时,尽可能满足垃圾收集暂停时间的要求,另外,具有以下特性:
- 像CMS收集器一样,能与应用程序线程并发运行。
- 整理空闲时间更快。
- 需要更多时间预测GC停顿时间。
- 不希望牺牲大量吞吐性能。
- 不需要更大Java Heap.
G1收集器的设计目标是取代CMS收集器,同CMS相比,在以下表现更出色:
- G1是一个整理内存过程的垃圾收集器,不会产生很多内存碎片。
- G1的Stop The World (STW) 更可控,G1停顿时间添加预测机制,用户可以指定期望停顿时间。
CMS收集器虽然减少了暂停应用程序的运行时间,但是它还是存在着内存碎片问题。于是,为了去除内存碎片问题,同时又保留CMS收集器低暂停时间的优点,JAVA7发布了一个新的垃圾收集器-G1垃圾收集器
G1在2012年才在jdk1.7u4中使用。oracle 官方计划在jdk9中将G1变成默认垃圾收集器以替代CMS。它是一款面向服务端应用的收集器,主要应用在多CPU和大内存服务器环境下,极大减少垃圾收集的停顿时间,全面提升服务器的性能,逐步替换java8以前的CMS收集器。
主要改变的是Eden,Survivor和Tenured 等内存区域不再是连续的,而是一个个大小一样的region。
每个region从1M到30M不等。一个region有可能属于Eden,Survivo或Tenured内存区域。
特点
1.G1充分利用多CPU、多核环境硬件优势,尽量缩短STW。
- G1整体上采用标记-整理算法,局部是通过复制算法,不会产生碎片。
- 宏观看G1不在区分年轻代和老年代。把内存划分多个独立的子区域(region),可以近似理解一个围棋的棋盘。
- G1收集器里面整个内存混合在一起,但其本身在小范围内进行年轻代和老年代的区分,保留新生代和老年代。单它们不再是物理隔离,而是一部分Region的集合不需要Region 是连续的,也就是说依然采用不同GC方式处理不同区域。
- G1虽然分带收集器,但整个内存分区不存在物理上的年轻代和老年代区别,也不需要完全独立survivor ( to space) 堆做复制准备。G1只有逻辑上分代概念,或者说每个分区都可能随G1运行在不同代之间前后切换。
Region 区域化垃圾收集器
区域化内存划片Region,整体为了一些列不连续的内存区域,避免全内存的GC操作。
核心思想将整个堆内存区域分为大小相同的子区域(Region) 在JVM启动时自动设置这些子区域的大小。
在堆得使用上,G1并不要求对象存储一定是物理上连续的只要逻辑上连续即可,每个分区不会固定为某个代服务,可以按需在年轻代和老年代之间切换。启动时可以通过参数-XX:G1HeapRegionSize=n 可指定分区大小(1MB-32MB,且必须是2的幂),默认整堆划分2048个分区。
大小范围在1MB-32MB,最多设置2048个区域,也即是支持最大内存:32MB * 2048 = 65536 = 64G内存
G1收集器Young GC
针对Eden区进行收集,Eden 区耗尽后被触发,主要小区域收集 + 形成连续内存块,避免内存碎片
- Eden区数据移动到Survivor区,假如出现Survivor区空间不够,Eden区数据会晋升到old区
- survivor 区的数据移动到新的Survivor区,部会数据晋升到old区
- 最后Eden区收拾干净了,GC结束,用户应用程序继续执行
4步过程
常用配置参数
- -XX:+UseG1GC
- -XX:G1HeapRegionSize=n 设置G1区域大小。值是2的幂,范围1MB-32MB。目标根据最小的java堆大小划分出的约2048个
- -XX:MaxGCPauseMills=n 最大GC停顿时间,这个是软目标,JVM尽可能(不保证)停顿小于这个时间
- -XX:initiatingHeapOccupancyPercent=n 堆占用多少的时候就触发GC,默认为45
- -XX:ConcGCThreads=n 并发GC使用的线程数
- -XX:G1ReservePercent=n 设置作为空闲空间的预留内存百分比,比降低目标空间溢出的风险,默认值10%
开发人员仅仅需要声明以下参数即可:
三步收纳:开始G1 + 设置最大内存 + 设置最大停顿时间
+XX:+UseG1GC -Xms32g -XX:MaxGCPauseMills=100
-XX:MaxGCPauseMills=n 最大GC停顿时间单位毫秒,这是个软目标,JVM尽可能(但不保证)停顿小于这个时间
和CMS相比优势
- G1不会产生内存碎片
- 是可以精确控制停顿。该收集器是整个堆(新生代、老年代)划分多个固定大小的区域,每次根据允许停顿时间去收集垃圾最多的区域。
如何选择GC收集器
版权声明:本文标题:JVM + GC 学习笔记 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://www.elefans.com/dianzi/1725925170a1049284.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论