笔记:深入理解JVM 第3章 垃圾回收器与内存分配策略
1、对象是否已死
(1). 引用计数法:无法回收相互引用的对象,故JVM没有采用
例子:
public class ReferenceObj { public ReferenceObj refObj;
} public static void main(String[] args){ReferenceObj obj1 = new ReferenceObj(); ReferenceObj obj2 = new ReferenceObj(); obj1.refObj = obj2; obj2.refObj = obj1; obj1 = null; obj2 = null;
}
以上例子使用引用计数法无法回收,但是JVM使用的不是,JVM可回收。
(2). 可达性分析算法:
通过一系列“GC root” 作为起始点,从这些节点开始往下搜索,搜索经过的路径成为引用链。若对象不与引用链相连接,则该对象不可用,可被回收。
GC root的对象有:
a. 虚拟机栈中引用的对象
b. 方法区中类静态属性引用的对象
c. 方法区中常量引用的对象
d. 本地方法栈中JNI引用的对象
(3).引用的类型
强引用:引用存在则不会回收
软引用:内存不足,则回收
弱引用:不管内存是否足够,必定回收
虚引用:无法通过虚引用获得对象,只为在回收之前获得系统通知
(4).finalize 方法可使得对象在回收之前复活,但是不建议使用
(5). 方法区(持久代)回收
方法区中无用的常量、无用的类会被回收。判断无用的类的准则,,同时满足:
a.该类的所有实例已经回收
b.该类的ClassLoader已经回收
c.该类的Class 对象不再被引用。
JVM 通过设置 -Xnoclaagc 控制。
在大量使用反射、动态代理、CGLib等框架,动态生成JSP、OSGI等频繁定义ClassLoader场景下都需要JVM具备卸载类的功能,保证方法区不溢出。
2、垃圾回收算法
(1).标记-清除算法
最简单的算法。
不足:效率低;内存产生大量碎片。
(2). 复制算法 ( 新生代)
两块内存,每次使用一块,回收时候将对象从块A移到块B,再清理块A。
优点:简单、高效,无碎片。
缺点:内存使用率低。
新生代一般用的是复制算法。因为新生代中对象98%是“朝生夕死”,所以两块内存不需要1:1,而是使用1块比较大的Eden和2块比较小的Survivor。回收时,将Eden和Survivor A中还存活的数据移动到Survivor B,再清理Eden 和 Survivor A;再次回收时候,将Eden和Survivor B中还存活的数据移动到Survivor A,再清理Eden 和 Survivor B。JVM 默认Eden 和 Survivor的比例是8:1, 即Eden: Survivor A: Survivor B = 8 :0.5:0.5 。当回收时候 Survivor 不够用时候, 对象将被移动到老生代。
(3). 标记-整理算法 (老生代)
先标记,再让所有存活的对象向一端移动,然后直接清除端边界一外的内存。
(4). 分代收集算法
也就是新生代用 “复制" 算法,老生代用 “标记-整理” 或者 "标记-清除" 算法。
3、HotSpot 的JVM 算法的实现
GC 导致必须停顿所有Java执行进程的原因:枚举根节点,作可达性分析。
4、垃圾收集器
(1). Serial 收集器 (新生代)
基于复制算法。
只使用单线程,在垃圾收集时候,必须暂停其他所有线程,直到其收集结束 (stop the world)。Serial Collector 是Client模式下,新生代的默认收集器。
(2). ParNew 收集器 (Parallel New 收集器) (新生代)
基于复制算法。
是Serial Collector 的多线程版本,也必须暂停其他所有线程。 ParNew Collector 是Server模式下,新生代的默认收集器。使用 -XX: UseParNewGC 打开。
当老生代使用 -XX:+UseConcMarkSweepGC 时候,新生代必须使用 Serial Collector 或 ParNew Collector ,默认是 ParNew Collector 。
默认开启的线城数目与CPU核数目一致。
(3). Parallel Scavenge 收集器 (新生代)
基于复制算法。
与ParNew Collector 相比,可以控制最大垃圾收集停顿时间-XX:MaxGCPauseMillis 和 吞吐量-XX: GCTimeRadio,-XX:
最大垃圾收集停顿时间-XX:MaxGCPauseMillis=500:停顿时间越短,则垃圾回收越频繁。
吞吐量-XX:GCTimeRadio=99:吞吐量 = (用户线程使用CPU时间) / (用户线程使用CPU时间 + GC线程使用CPU时间)
自适应调节策略 -XX:+UseAdaptiveSizePolicy: 开启后系统会使用当前系统的运行情况收集性能监控信息,动态调整停顿时间和吞吐量。
(4). Serial Old 收集器 (老生代)
基于标记-整理算法。
Serial 收集器的老生代版本。三个方面用途:
a. Client 模式下用
b. Server 模式下老版本的JVM 与Parallel Scavenge 搭配使用
c. 作为老生代CMS的后备方案,失败的时候使用。
(5). Parallel Old 收集器
基于“标记-整理”算法。
Parallel Scavenge 的老生代版本。一般用于 与新生代的 Parallel Scavenge 相搭配,整个系统吞吐量优先。
(6). CMS 收集器
基于“标记-清除”
Concurrent Mark Sweep ,以获取最短时间停顿为目标的收集器。互联网站和B/S系统的服务端,一般都用CMS,以保证服务的响应速度,停顿时间短,给用户带来较好地体验。
包括4个步骤:初始标记、并发标记、初始标记、并发清除。
优点:并发收集、低停顿。
缺点:
a. 对CPU资源敏感,即占用大量CPU资源,不会导致用户线程停顿但是会变慢;
b. 无法处理浮动垃圾,即在清理阶段产生的垃圾,这些垃圾必须在下一次GC时候回收,所以还必须预留部分空间,设置-XX:CMSInitiatingOccupancyFraction 来提前触发 (默认 92% 触发);
c. 收集结束会产生空间碎片,设置+UseCMSCompactAtFullCollection 整理空间碎片。
(7) G1 收集器 (新生代、老生代)
Garbage First ,未来可能替换CMS。特点:
a. 并发与并行
b. 分代收集
c.空间整合,使用“标记-整理”算法,不会产生空间碎片
d.可预测的停顿
现在还没有广泛运用。
5、配置参数总结
-XX:+UseSerialGC 使用 Serial + Serial Old 收集器
-XX:+UseParNewGC 使用ParNew + Serial Old 收集器
-XX:+UseConcMarkSweepGC 使用ParNew + CMS + Serial Old (失败后的预备方法)
-XX:+UseParallGC 使用 Parallel Scavenge + Serial Old
-XX:+UseParallelOldGC 使用 Parallel Scavenge + Parallel Old
-XX:SurvivorRatio=8 新生代中Eden 与 Survivor 的比例,默认8
-XX:PretenureSizeThreshold=1024 大于此参数的对象直接分配在老生代
-XX:MaxTenuringThreshold=5 新生代中对象年龄超过此值,移动到老生代
-XX:+UseAdaptiveSizePolicy 动态调整Java 堆中各区域大小 以及进入老生代的年龄
-XX:+HandlePromotionFailure 是否允许分配担保失败
-XX:ParallelGCThreads=10 GC并行线程个数
-XX:GCTimeRatio=98 GC吞吐量,仅在Parallel Scavenge 有效
-XX:MaxGCPauseMillis=500 GC最大停顿时间,仅在Parallel Scavenge 有效
-XX:CMSInitiatingOccupancyFraction=70 CMS在使用70%触发, 默认68%
-XX:+UseCMSCompactAtFullCollection CMS后进行内存整理
-XX:CMSFullGcsBeforeCompaction=5 CMS 5次后进行碎片整理
6. 理解GC日志
[GC :GC 没有发生Stop-The-World停顿
[Full GC :GC 发生了Stop-The_World
[Full GC(System) : GC 由System.gc() 调用
[DefNew : 新生代使用了默认的Serial 收集器 Default New Generation
[Tenured: 老生代 Tenured Generation
[Perm: 持久带 Perm Generation
[ParNew: 新生代使用ParNew收集器 Parallel New Generation
[PSYoungGen: 新生代使用Parallel Scavenge 收集器
3324K ->152K (3712K) : GC前为3324K -> GC后为152k (总容量为3712K)
[Times: user=0.01, sys=0.00 , real=0.02secs] : 用户态消耗的CPU时间、内核态消耗的CPU时间、墙钟时间。墙钟时间包括CPU时间与非运算的等待消耗,如等待磁盘IO、等待线程阻塞。
7、内存分配与回收策略
(1)、对象优先在Eden分配
大多数情况下,对象在新生代的Eden区分配,当Eden区没有足够的空间进行分配适合,虚拟机会进行一次 Minor GC。
配置:
-Xmx20M -Xms20M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=8
代码:
public class MinorGCTest {private static final int _1MB = 1024 * 1024;public static void main(String[] args) {byte[] allocate1, allocate2, allocate3, allocate4;allocate1 = new byte[2*_1MB];allocate2 = new byte[3*_1MB];allocate3 = new byte[3*_1MB];allocate4 = new byte[4*_1MB];}
}
输出:
[GC [PSYoungGen: 5792K->600K(9216K)] 5792K->5720K(19456K), 0.0022215 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC [PSYoungGen: 600K->0K(9216K)] [ParOldGen: 5120K->5589K(10240K)] 5720K->5589K(19456K) [PSPermGen: 2508K->2507K(21504K)], 0.0091615 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
Heap
PSYoungGen total 9216K, used 6727K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 82% used [0x00000000ff600000,0x00000000ffc91c28,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
ParOldGen total 10240K, used 5589K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 54% used [0x00000000fec00000,0x00000000ff1754e0,0x00000000ff600000)
PSPermGen total 21504K, used 2517K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c75520,0x00000000faf00000)
(2)、大对象直接进入老生代
可通过 -XX:PretenureSizeThreshold 设置。写程序应该避免大对象。
配置:
-Xmx20M -Xms20M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:PretenureSizeThreshold=3145728
代码:
public class PretenureSizeThresholdTest {private static final int _1MB = 1024 * 1024;public static void main(String[] args) {byte[] allocate1;allocate1 = new byte[8*_1MB];}
}
输出:
Heap
PSYoungGen total 9216K, used 835K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 10% used [0x00000000ff600000,0x00000000ff6d0fc0,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 10240K, used 8192K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 80% used [0x00000000fec00000,0x00000000ff400010,0x00000000ff600000)
PSPermGen total 21504K, used 2515K [0x00000000f9a00000, 0x00000000faf00000, 0x00000000fec00000)
object space 21504K, 11% used [0x00000000f9a00000,0x00000000f9c74cd0,0x00000000faf00000)
(3)、长期存活的对象移动到老生代
通过-XX:MaxTenuringThreshold 设定。
配置
-Xmx20M -Xms20M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
代码:
private static final int _1MB = 1024 * 1024;public static void main(String[] args) {byte[] allocate1, allocate2, allocate3, allocate4;allocate1 = new byte[ _1MB/4];allocate2 = new byte[4*_1MB];allocate3 = null;allocate3 = new byte[4*_1MB]; }
输出:
[GC[ParNew: 5024K->758K(9216K), 0.0021874 secs] 5024K->4856K(19456K), 0.0022406 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 9216K, used 5346K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
eden space 8192K, 56% used [0x00000000f9a00000, 0x00000000f9e7af60, 0x00000000fa200000)
from space 1024K, 74% used [0x00000000fa300000, 0x00000000fa3bdaa0, 0x00000000fa400000)
to space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
concurrent mark-sweep generation total 10240K, used 4098K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
concurrent-mark-sweep perm gen total 21248K, used 2519K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
(4)、动态对象年龄判定
为了能更好地适应不同程序的内存情况,虚拟机并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才进入老生代。如果在Survivor 空间中相同年龄所有对象大小的总和大于Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老生代,无需等到MaxTenuringThreshold 。
(5)、空间分配担保
在发生Minor GC 之前, 虚拟机会先检查老生代最大可用的连续空间是否大于新生代所有对象总空间,如果成立,则Minor GC可用确保是安全的。在JDK 1.6_24 之后,只要大于,则进行Minor GC, 否则则进行Full GC。
8. 新生代GC 和 老生代GC 各自特点
新生代GC:Minor GC,因为新生代的对象大多数朝生夕灭,所以非常频繁,回收速度也很快。
老生代GC:Major GC/Full GC, 其速度比 Minor GC 慢10倍以上。
转载于:https://www.cnblogs.com/leeeee/p/7276309.html
笔记:深入理解JVM 第3章 垃圾回收器与内存分配策略相关推荐
- 深入理解Java虚拟机-垃圾回收器与内存分配策略
本博客主要参考周志明老师的<深入理解Java虚拟机>第二版 读书是一种跟大神的交流.阅读<深入理解Java虚拟机>受益匪浅,对Java虚拟机有初步的认识.这里写博客主要出于以下 ...
- Java虚拟机的垃圾回收器以及内存分配策略详解
概述 垃圾回收器(GC)是什么以及为什么我们需要垃圾回收器?? 垃圾回收是Java语言区别于其他语言的一种最为重要的特性之一, 通过垃圾回收器(Garbage Collection)来实现对我们Jav ...
- 垃圾回收器和内存分配策略
本文作者:李敏,叩丁狼高级讲师.原创文章,转载请注明出处. 4. 垃圾回收器和内存分配策略 **GC(Garbage Collection)**的历史比java久远.1960年诞生于MIT的Lisp是 ...
- 垃圾回收器与内存分配策略
垃圾回收器与内存分配策略 1. 前言 计数器:计数器难以解决循环引用,需要大量的额外处理才能正确工作. 可达性分析算法 在java技术栈里GC Roots包括以下几种 虚拟机栈中引用的对象 方法区中类 ...
- 【JVM和性能优化】2.垃圾回收器和内存分配策略
内存回收 为什么要了解GC(Garbage Collection)和内存分配策略 1.面试需要 2.GC对应用的性能是有影响的 3.写代码有好处 那些需要GC: 共享区的都要被回收比如堆区以及方法区. ...
- Java 垃圾回收器与内存分配策略 JVM
文章目录 什么是垃圾回收? 哪些位置内存需要回收? 四种引用关系 如何判断一个对象是不是需要回收? GC Roots 对象有哪些? 如何回收? 垃圾回收算法的指导思想 常用的垃圾回收算法 看图即可明白 ...
- 20200405——java之jvm 垃圾回收器和内存分配策略 二
什么叫GC Garbage Collection 什么内存区域需要GC 共享区的都要被回收比如堆区以及方法区. 在进行内存回收之前要做的事情就是判断那些对象是'死'的,哪些是'活'的.常用方法有两种 ...
- 【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略
最近想好好复习一下java虚拟机,我想通过深读 [理解Java虚拟机 jvm 高级特性与最佳实践] (作者 周志明) 并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强 ...
- java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略
java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略 GC需要完成的三件事情:哪些内存需要回收.什么时候回收.如何回收 垃圾回收器在对堆进行回收前,首先要确定那些对象存活,哪些对象已经死去,判断的 ...
最新文章
- C#利用Graphics类绘制进阶--绘制条形码Code128
- 51nod 1275 连续子段的差异
- 妈蛋:kinMaxShow轮播图异常,WebUploader图片上传坑爹,图片被压缩了
- 学好机器学习,这里有想要的一切
- APP下载页面(支持微信扫一扫)
- zabbix agent类型的所有key 值
- 如何.gitignore文件夹中的所有文件/文件夹,但不是文件夹本身? [重复]
- 最全的HTTP1.1状态码
- 无法编辑PDF文档?看完这篇文章即刻解决~
- 【校招VIP】产品行测之逻辑计算题
- 平方矩阵 II--C++ 经典解析版-----编程/算法/数据结构
- 苹果计算机怎么隐藏应用,苹果电脑怎么隐藏界面图标
- 2018 东北四省赛
- C语言源代码转变为可执行程序的过程
- 感觉自己成长慢,单点突破可以让你成长快10倍
- LBS找外贸客户 外贸怎么找客户
- 有道再出发:真正的教育事业没有终点
- 聚焦大规模分布式机器学习,全面剖析Google TensorFlow,来看阿里、京东等在人工智能技术的进展...
- 黑马程序员高薪就业学习方法揭秘
- DES Wrong Key Size错误