什么是垃圾?Garbage Collectors
没有引用指向的就是要回收的垃圾
一旦引用失效或不存在 就会memory leak内存泄漏 达到一个极端 就会 oom内存溢出
1.引用计数
判断是否是要回收的垃圾
环形垃圾就不能用引用计数判断
2.Root Searching 根可达算法
判断是否是垃圾
根据主线程一直达到端点
清除方法
1.Mark-Sweep 标记后清除
就会 内存碎片化
一旦整体的大对象来了就会把存活的对象的挤出去 导致 oom内存溢出
2.copying拷贝算法
把内存空间划分一半出来 只有上半部分可以存对象,把存活的对象拷贝到另一边删除上半部分的全部对象就不会 内存碎片化
3.Mark-Compact标记垃圾和不是垃圾的都
Mark-Sweep是用空间换时间Mark-Compact则是用时间换空间。Mark阶段用来发现并标记所有活的对象,然后compact阶段才移动对象来达到Compact的目的。如果Compact方式是Sliding Compaction,则在Mark之后就可以按顺序一个个对象“滑动”到空间的某一侧进行内存划分这样就不会 内存碎片化,同时压缩阶段是会暂停应用的,所以给我们应该尽量避免对象出现在老年代。
标出来进行压缩清理 效率低
Gc模型
前6种为分代模型,后三种是分区模型
Serial与Serial-old
stw停止业务线程由一个线程进行回收 Serial-old也是一样的只是工作在老年代,一般是几十mb
Parallel Scavenge与Parallel Scavengel-old
stw停止业务线程由多个线程进行回收 Parallel Scavengel-old也是一样的只是工作在老年代
cms
stw stop-the-world 停止业务线程
concurrent mark sweep并发 low-pause短的stw 的 collector回收器
内存空间越来越大,上面的策略不能处理,就是当stw的时间越来越长非常长;
cms就是缩短stw时间,业务线程停止的时间减短也就是垃圾回收线程缩短,响应也就及时
特点
-
初始标记(initial mark) 有 STW
-
并发标记(concurrent mark) 没有 STW
-
重新标记(remark) 有 STW
-
并发清除(concurrent sweep) 没有 STW
黄色是垃圾回收
初始标记
在垃圾回收线程并发标记工作时并不停止业务线程,
并发标记的两种情况
浮动垃圾:在并发标记阶段产生了新垃圾不会被及时回收,而是只能等到下一次GC
floating garbage -留到下次清除
重新标记(重点)
就是为了修正错标的问题
重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。
重新标记,重新从GC Root开始查找新关联的对象,并进行标记;而初始标记、并行标记两个步骤标记的对象,即使并行标记过程中已经没有相关引用了,也不会再去清除这些对象的标记(直到等到下一次GC发生的时候再去清除)
并发清理
通过以上阶段的标记,老年代所有存活的对象已经被标记并且现在要通过Garbage Collector采用清扫的方式回收那些不能用的对象了。
这个阶段主要是清除那些没有标记的对象并且回收空间;
由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。
分代模型和分区模型
不管内存大小 都是先分代模型判断够不够,不够就选择分区模型
分代一般2个gc,分区1个gc
分区模型是分成很多小块,分时间段管理其中几块,然后下个时间段再管其中几块
堆内存逻辑分区 分代模式
如何从新生代到老年代?
先是eden没被回收的就升一次级升级到特定等级就存入老年代。
第一个survivor就是Fromspace 第二个survivor就是Tospace
第一次新生代的eden创建 new了10个对象 ,如果只有1个保存就用copying算法把活的对象存入fromspace,清掉eden;第二次eden又创建 new了10个对象,如果只有1个保存就用copying算法把活的对象存入Tospace,并且把fromspace的那一个也存入Tospace,清掉eden和fromspace,第三次eden又创建 new了10个对象,如果只有1个保存就用copying算法把活的对象存入fromspace,又把tospace的两个存入fromspace; fromspace和tospace来去往返的切换copying算法存储
eden满了survivor满了就存入老年代
栈逃逸
先在栈上分配,内存够用就存入栈, 栈中的方法引用结束,就pop弹出栈引用,栈针就下移一位,直接结束,不用垃圾回收器介入,占的内存很少。
栈的a对象在方法内部分配里面有局部对象;但是另一个b方法对象用了a的局部对象,就不能在栈中分配,会发生栈逃逸,就要去堆
FGC FullGC全量回收
TLAB thread local allocation buffer 线程本地分配缓存区, 会发生线程之间的竞争,为了去除线程之间的竞争,就设计了线程私有本地空间
jvm调优
减少老年代的fullgc次数(减少stop the world次数)增大老年代内存
吞吐量 程序运行5个小时 只有2个小时在运行 其余时间都是老年代full时的stop the world,gc调优就在于减少stop the world次数