什么是垃圾?
我们创建出来的对象没有引用指向的的时候,这个对象就再也无法被用到,就是说,这个对象已经成为了垃圾对象,一直存放在我们的内存当中
我们怎么判断一个对象是不是垃圾
两种方式
- 引用计数法
比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题
- 可达性分析法
GCRoot根搜索法
这种方式从一个GCRoot根出发,所有依赖到的对象都被标记为不是垃圾,其他的都是垃圾
GCRoot根:
1. 堆中类静态属性引用的对象
2. 方法区中变量引用的对象
3. 方法区中常量引用的对象
4. 虚拟机栈中本地变量表引用的对象
5. 本地方法栈引用的对象(就是native方法里面引用的对象)
怎么回收垃圾
- 复制算法
空间一分为二,一边放对象,另一边什么都不放,空着
开始清理垃圾,将存活对象复制另一部分,直接把原来的空间清空,然后右边空间再作为对象存放的地方,左边则成为什么也不放的区域,空着
清除效率高,适合一个区域垃圾很多,存活的很少,这样我们只需把存活的几个移动一下就行了,但如果垃圾很少,存活的很多,那我们就需要复制一大堆存活对象到另一个存储空间,效率低,而且这种方式很费空间,毕竟只用一半
- 标记清除
很简单就是把所有的垃圾标记一遍,直接清除所有的垃圾;缺点是会产生很多内存碎片,其他大对象来的时候存不进去,效率适中
- 标记整理
在上面的基础上,清除以后整理一遍,使得对象排列规整,这种方式没有碎片但是,又要标记又要清除又要移动到指定区域,等于重新复制了一遍,所以效率不高
hostSpot虚拟机堆中的垃圾回收
我们虚拟机采用了封装的技术,封装了几个垃圾收集器,用来回收垃圾,接下来分析每个垃圾收集器的讲解
1.分代垃圾回收器
年青代
在年轻代因为对象大多都是朝生夕死的,所以很多都是垃圾,存活的只有很少,所以Parallel Scavenge用复制算法,上图中分为Eden区和两个survive区,他们为什么不是内存劈一半呢?而是8:1:1
他是这样工作的:
- Eden区放对象,此时两个幸存区都没有对象,发生垃圾收集时,Eden区的存活对象全部复制到survive1区,同时将Eden区和Survive2区清空,然后再向Eden区放对象,再次垃圾回收,将Eden区和survive1区的存活的对象复制到survive2中,将survive1和Eden区清空,循环往复。。。Parallel Scavenge他是这样复制的,然后每一次的垃圾回收都会给对象头上的分代年龄(4位)加一,当到15时,将对象复制到老年代,
老年代
它采用的是标记-整理算法,这里和我们讲的标记整理算法一样,不详细讲了
发生在年轻代的gc是youngGC 发生在老年代的是FullGC,FullGC只有当内存不足的时候会发生,这时一次会回收老年代的很多对象,释放内存,youngGC很频繁,还有这里涉及stw(stopTheWord),指在垃圾回收时其他的所有线程不能工作,也就是此时服务器会处理不了任何请求,所以尽量减少fullGC和stw的时间是GC调优的关键,
新生代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:CMS、Serial Old、Parallel Old
2.不分代垃圾收集器
整堆收集器: G1
并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-The-World停顿时间。部分收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让Java程序继续运行。
分代收集:G1能够独自管理整个Java堆,并且采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
空间整合:G1运作期间不会产生空间碎片,收集后能提供规整的可用内存。
可预测的停顿:G1除了追求低停顿外,还能建立可预测的停顿时间模型。能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。
强引用、弱引用、软引用和虚引用
(一) 强引用(StrongReference)
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它,当内存空间不足时,Java
虚拟机宁愿抛出OutOfMemoryError
错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
(二) 软引用(SoftReference)
如果一个对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
(三) 弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
(四) 虚引用(PhantomReference)
虚引用就是一个标记,主要用来跟踪对象被垃圾回收器回收的活动。
虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
参考 https://blog.youkuaiyun.com/lioncatch/article/details/106070460