本篇是《深入理解java虚拟机第三章》的笔记记录。
一 为什么要关注GC和内存分配?
需要排查各种内存溢出、内存泄漏问题时,或者当GC成为系统达到更高并发量的瓶颈时,需要对jvm的默认参数进行调节;
二 哪些内存需要回收?
- 方法区回收:废弃常量和无用类;
无用类:
该类的所有实例都被回收;
加载该类的ClassLoader已经被回收;
该类对应的java.lang.Class对象没有在任何地方被引用,无法再任何地方通过反射访问该类的方法;
- 引用计数算法:给对象添加一个引用计数器,每当有一个地方引用它时+1,引用失效时-1,任何时刻计数器=0则对象不会再用到。但这个算法无法解决对象之间相互循环引用的问题;
- 可达性分析算法:GC Roots无法引用到的对象。即从GC Roots节点向下搜索,当一个对象到GC Roots没有任何引用链存在即此对象不可用;
GC Roots包括:
-
-
- JVM栈中本地变量表引用的对象;
- 方法区中静态属性引用的对象;
- 方法区中常量引用的对象;
- 本地方法栈中JNI引用的对象;
-
三 什么时候回收?
- 程序计数器、jvm栈、本地方法栈 随着线程生死;
- 栈帧分配的内存随着栈帧的调用结束而被回收;
- 堆内存:
一般情况:
Minor GC: 当新生代中的Eden区没有足够空间时触发一次Minor GC;
Full GC(Major GC):当老年代没有足够空间时;
实际上:
四 如何回收
1 算法
(1)标记清除算法;
缺点: A 效率不足,标记和清除两个过程都效率不高;
B 清楚后会产生大量不连续的内存碎片,空间碎片太多导致当需要分配较大对象时,无法找到足够连续空间触发GC;
(2)复制算法;
缺点:将内存缩小为原来的一半;
优点:不会发生空间碎片;
应用:新生代使用的收集算法,将新生代划分为一块Eden,两块较小的Survivor空间,每次使用Eden与一块Survivor,回
收时将Eden和Survivor存货对象复制到另一块Survivor,清理原始Eden Survivor空间;
HotSpot虚拟机默认Eden和Survivor大小比例为8:1,可使用-XX:SurvivorRatio=8来调整;
当Survivor空间不够时,使用老年代进行分配担保;
(3)标记整理算法:将所有存活对象都向一端移动,然后清理掉端边界以外的内存;
优点:对象存活率较高时不进行复制操作,提高效率;
应用: 老年代;
(4)分代收集算法;
2 垃圾收集器
(1)Serial 收集器:
复制算法;
单线程执行;
执行过程中,用户的工作线程全部停掉;
应用:单CPU环境;
(2) ParNew 收集器:
复制算法;
多线程执行(并行,用户线程处于等待状态);
使用-XX:+UseParNewGC强制指定或者-XX:+UseConcMarkSweepGC的默认新生代收集器;
默认开启的收集线程数与CPU的数量相同,当CPU非常多,可使用-XX:ParallelGCThreads限制收集线程数;
应用:许多运行在server模式下的虚拟机新生代首选收集器,因为除了Serial收集器外,目前只有它能与CMS收集器
配合工作;
(3) Parallel Scavenge收集器:
新生代;
复制算法;
并行多线程收集器;
特点:目标是达到一个可控制的吞吐量 吞吐量=运行用户代码时间/(运行用户代码时间 + 垃圾收集时间),适用于
后台运算而不需要太多交互的任务
使用: -XX:MaxGCPauseMillis 控制最大垃圾收集停顿时间;(>0的毫秒数)
-XX:GCTimeRatio 设置吞吐量大小;(0-100)
-XX:+UseAdaptiveSizePolicy 开关参数,打开后不需要手动指定-Xmn,-XX:SurvivorRatio,
-XX:PretenureSizeThreshold等细节参数,自适应的调节策略;
(4) Serial Old 收集器
老年代;
单线程;
标记-整理算法
用途:CMS收集器后备;
(5) Parallel Old收集器
老年代;
多线程;
标记-整理算法;
对应Parallel Scavenge的收集器
用途:注重吞吐量以及cpu资源敏感的场所;
(6) CMS收集器(Concurrent Mark Sweep):
老年代;
并发收集器;
标记-清除算法;
目标:获取最短回收停顿时间,提升用户体验
用法:使用-XX:CMSInitiatingOccupancyFraction设定cms启动阈值(老年代使用空间的百分比)
-XX:+UseCMSCompactAtFullCollection(开关参数,默认开启) 设置CMS收集器在Full GC前开启内存碎片
合并整理过程;
缺点:
cpu资源敏感;
无法处理浮动垃圾,可能出现Concurrent Mode Failure失败而导致一次Full GC的出现;
产生大量碎片,可以使用-XX:+UseCMSCompactAtFullCollection开启内存碎片合并整理;
-XX:CMSFullGCsBeforeCompaction设置执行多少次不压缩的Full GC后来一次压缩的
默认值为0,即每次Full GC前都压缩;
(7)G1收集器
并行与并发;
将整个堆划分为多个大小相等的Region,根据各个Region的垃圾堆积价值大小优先回收价值最大的Region
独自可以管理整个GC,而且可以采用不同方式处理新创建的对象和已经存活了一段时间的对象获取更好的收集效果
不会有空间碎片;
可以预测的停顿(G1相对于CMS的优势,除低停顿外,还可以建立可以预测的停顿时间模型)
3 内存分配
(1) 对象优先在Eden分配
(2) 大对象直接进入老年代 -XX:PretenureSizeThreshold=3145728 大于3MB的对象直接进入老年代
(3) 长期存活的对象进入老年代 : Eden对象经过一次Minor GC后仍存活并且能够被Survivor所容纳将被移动到Survivor
空间中,对象年龄设置为1,对象在Survivor空间中没经过一次Minor GC年龄加1,当年龄增加到
-XX:MaxTenuringThreshold=1(默认15)时,对象会晋升到老年代
(4) 如果在Survivor空间中相同年龄所有对象大小总和大于Survivor空间的一半,年龄大于或等于该年龄的对象直接进入老年代;