没有引用 指向的 对象 都是垃圾
Java | C++ |
---|---|
GC处理垃圾 | 手工处理垃圾 |
开发效率高,执行效率高 | 开发效率低,执行效率高 |
缺陷1 :忘记回收会造成内存泄漏 | |
缺陷2 :回收多次可能造成非法访问 |
一个对象被几个指针指向。就记录其指针的数量
当 引用==null 时,count - -
当 count == 0 时,这就是垃圾了
弊端:不能找到循环引用组成的垃圾
GC roots : 线程栈变量、 静态变量、常量池、JNI指针( java本地接口 )
以上 根 指向的地方、可以根据引用抵达的地方 不是垃圾,其余都是
存活对象多时效率高,如老年代
标记还 有用的对象,清除无用的
存活对象比较多的情况下,效率较高
所以不适合伊甸园区
算法缺陷
两遍扫描,效率偏低(第一遍标记、第二遍清除)
容易产生碎片
存活对象少时效率高,适用于伊甸园区内存一分为2
找到有用的,把有用的对象拷贝到另一边空地,全找完后本区域全部清除
优点 | 弊端 |
---|---|
适用于存活对象较少的情况(如伊甸园区),只扫描一次,效率高 | 移动复制对象,需要调整对象的引用(所以 java 即使一直指向同一个对象,它的引用的值也会变。) |
没有碎片 | 空间浪费 |
第一遍先标记出还有用的对象
第二遍把有用的对象挪到一起,清除剩下的垃圾。所以剩下的空间就很整齐了。
优点 | 弊端 |
---|---|
不会产生碎片,方便对象分配 | 扫描两次 |
不会产生内存减半 | 需要移动对象,效率偏低 |
分代算法
部分垃圾回收期使用的模型
除 Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型
G1 是逻辑 分带,物理不分代
除此之外 都 逻辑分代 + 物理分代
- 堆内存占比 1/3
- 存活对象少
- Copying算法
- MinorGC/YGC :年轻代空间耗尽时触发(-Xmn)
新生代分为 伊甸园区 eden 和 幸存者1区 servivor 1 和 幸存者2区 servivior 2
比例 8 :1 :1
而新生代和老年代比例 1 :2
- 堆内存占比 2/3
- 存活对象多
- Mark Compact算法或MarkSweep算法;
- MajorGC/FullGC:在老年代无法继续分配空间时触发,新生代和老年代同时进行回收(-Xms -Xmn)
新生代和老年代在堆,永久代在方法区
存class信息、代码的编译信息等
方法区的实现
JDK1.8之前 | JDK1.8之后 |
---|---|
永久代 | 元数据区 |
必须指定大小 | 元数据区可以设置大小,也可以不设置,无上限(受限于物理内存) |
字符串常量在永久代 | 字符串常量在堆 |
- 线程私有小对象
- 无逃逸 (这个对象就某一块代码里集中使用,其余地方不用)
- 支持标量替换 (用几个普通的类型就能代替这个对象)
- 无需调整
-XX:-DoEscapeAnalysis 去掉逃逸分析
-XX:-DoEscapeAnalysis 去掉标量替换
-XX:-UseTLAB 去掉使用线程本地分配
线程本地分配 TLAB ( Thread Local Allocation Buffer )
占用eden,默认1%
每个线程在伊甸园区取1%作为自己的,每次分配内存时,首先往自己的线程本地分配,
多线程时候不用竞争eden就可以申请空间,提高效率
- 小对象
- 无需调整
- 大对象 直接进入老年代
- 幸存者区达到最大年龄的对象 ( 默认15.因为在markword中年龄占4个bit,即最大为1111 )
超过 XX:MaxTenuringThreshold指定次数 YGC
Parallel Scavenge 15
CMS 6
G1 15- 动态年龄
幸存者1区 或 幸存者2区 的对象超过当前区总内存的50%时
把年龄最大的放入老年代(也就是说不一定到15岁)- 分配担保
YGC(新生代垃圾回收)期间,survivor区空间不够了,通过空间担保直接进入老年代
老年代内存不够用了,触发FGC,清理老年代,可以用以下垃圾回收器回收;
除了圈出来的是 物理不分代 + 逻辑分代
其余都是 物理分代 + 逻辑分代
JDK诞生,Serial追随,为了提高效率,诞生了Parallel Scavenge,
为了配合CMS,诞生了PaarNew.
CMS是JDK 1.4 之后引入的
CMS是里程碑式的GC, 开启了并发回收
但是CMS毛病多,因此目前没有任何一个JDK版本默认是CMS,都是默认PS
CMS 收集器是以 获取 最短停顿时间 为 目标的收集器
并行收集
STW 垃圾清理时工作暂停,单线程清理垃圾
目前还没有不会产生STW 的垃圾回收器
STW Stop The World
当前JVM默认是这种 ( 并行的 )
PS + PO 和STW差不多,垃圾清理时工作暂停,不过是多线程清理垃圾。吞吐量优先
默认线程数为CPU核数,响应时间优先
是PS的变种,和CMS配合使用
JDK1.8出现,1.9完善
debug用的
Concurrent Mark Sweep 并发标记清除
[多个线程同时回收是并行,这里是并发,可以垃圾回收和工作线程并发执行]
并发垃圾回收是因为无法忍受STW
以前内存不大,停下来清理也快。
现在内存很大,停下来清理要很久很久,甚至要停几天
CMS也是老年代垃圾回收器,老年代满了就触发 FGC
- 初始标记阶段 initial mark
--------- STW 标记存活的对象,只标记一些根对象 (根可达法)
--------- 所以速度 很快
------------- 本阶段仅标记 GC roots 直接连接的对象
- 并发标记阶段 concurrent mark
--------- 边 标记 边 运行 工作线程- 重新标记阶段 remark (大多数的垃圾在并发标记时期已经标记完了)
--------- STW 过程,工作线程停止,标记新产生的垃圾
--------- 因为新产生的垃圾并不多,所以也很快- 并发清理阶段 concurrent sweep
--------- 边清理标记的垃圾,边运行工作线程,此时产生的浮动垃圾由CMS下次清理
- 浮动垃圾
解决方案:降低触发CMS的阈值,保持老年代有足够的空间
Concurrent Model Failure
PromotionFaoled
如果是有很多碎片了,实在放不下对象了
CMS就会把老奶奶 Old Sorial 请出来清理
会SWT,停止工作线程,Old Sorial要清理很久很久很久
降低CMS触发的阈值
- 内存碎片化
CMS
Concurrent Mark Sweep
并发标记清除法
标记清除,会产生大量碎片
-XX:CNSInitiationOccupancyFraction 92%
降低触发CMS的阈值,保持老年代有足够的空间
CMS ( 三色标记法 + incremental Update )
G1 ( 三色标记法 + SATB )
ZGC ( Coloredpointers 颜色指针 + 写屏障 )
Shenandoah ( ColoredPointers + 读屏障 )
在并发标记时,引用 可能 产生 变化,白色对象有可能被错误回收
- 白:没有遍历到的节点
- 灰:自己标记完成,还没来得及标记fields
- 黑:自己已经标记,fields都标记完成
解决方案
- SATB
shapshot at the begining 在起始的时候做一个快照
当B->D消失时,要把和这个引用 推到 GC的堆栈,保证D还能被GC扫描到- incurmental Update
( 自己的白的没扫完,变成黑的重新扫 )
当一个白色对象被一个黑色对象引用,将黑色对象重新标记为灰色,让collector重新扫描