垃圾回收发生在哪些区域?
新生代、老年代、永久代(1.7)
怎么判断对象已死?
方法一:引用计数法
给对象添加一个引用计数器,当这个对象被引用时,计数器加一,取消引用计数器减一,当计数器为0时,该对象没有被引用,可以被垃圾回收。
缺点:不能解决循环引用的问题。
方法二:根搜索算法
从GC Roots出发向下搜索对象,经过的路径叫做引用链,当搜索结束后,没有在引用链上的对象可以被垃圾回收。
可以被当做GC Roots的对象是:
JVM栈中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
native方法引用的对象
垃圾回收的算法
- 标记-清除算法
从GC Roots出发遍历对象图,标记遇到的每个对象,标记完成后,回收所有未被标记的对象。
缺点:会造成内存碎片,影响大对象分配内存从而提前触发fullGC。
- 标记-整理算法
从GC Roots出发遍历对象图,标记遇到的每个对象,标记完成后先将存活的对象移动到内存的一侧,然后将另一侧的内存空间全部清除掉。
- 复制算法
两块大小一样的内存空间,每次只用其中一块内存,进行垃圾回收时先将存活的对象复制到另一块内存中,然后将用过的那块内存空间全部清除。
优点:实现简单、高效,不会造成内存碎片。
缺点:会造成内存的空间浪费。
- 分代收集算法
根据对象的存活周期不同,讲内存空间划分为新生代和老年代。
内存的分配策略
-
对象优先在Eden区分配
-
大对象直接进入老年代
-
长期存活(age>15)的对象晋升到老年代
-
对象动态年龄判定,同一年龄的对象所占内存超过S区的一半,那么这些对象进入老年代
常见的垃圾收集器有哪些?
- CMS垃圾收集器
并发收集的方式清除垃圾对象,GC停顿时间短,适合对时延较高的服务。
缺点:采用标记-清除算法,容易造成垃圾碎片。
CMS执行过程:
初始标记(STW);标记GC Roots直接关联的对象
并发标记;GC线程和应用线程并发执行,根据初始标记时的存活对象,递归遍历标记可达对象
并发预清理;标记在上一个阶段发生变化的对象引用
重新标记(STW);完成标记整个老年代的所有存活对象
并发清理;清除那些没有标记的对象并回收空间
- G1垃圾收集器
将堆划分为多个大小固定的独立内存区域,并且跟踪这些区域里的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间优先回收垃圾最多的区域。
每次不会GC的特别干净,类似于打扫房间时只打扫一部分区域。当然,G1也不会等到内存不足时再打扫,而是找出高收益的区域进行打扫。
G1执行过程:
初始标记(STW);标记了从GC Roots开始直接可达的对象
并发标记;从GC Roots开始,标记线程和应用程序线程并行执行,并且收集各个Region的存活对象信息
最终标记(STW);标记那些在并发标记阶段发生变化的对象
清理;清除空Region(没有存活的对象)并加入可用Region队列
如果觉得有帮助,就请点个赞鼓励一下吧。