垃圾收集

在java的对象中,大部分的java对象只存活一小段时间,二存活下来的java对象会存活很长一段时间。基于这种情况,java虚拟机中有了分代回收的思想,简单来说就是把堆空间划分成两代,新生代和老年代。新生代用来存放新建的对象,如果对象存活的时间足够长,就把他移动到老年代。

java虚拟机针对不同代使用不同的回收算法那。对于新生代,一位大部分对象值存活一小段时间,所以采用频繁采用消耗时间较短的垃圾回收算法。

java虚拟机堆的划分

java虚拟机把堆划分成新生代和老年代。新生代有划分为Eden区,和两个大小相同的Survivor区域,其中一个survivor区域是空的,默认情况下,根据生成对象的速度和survivor区域使用情况动态调整Eden区域和survivor区域的比例。
使用new新建对象的时候,会在Eden区域划分出一块作为存储对象的内存,由于堆空间是线程共享的,所以划分空间的操作时需要进行同步的。当Eden区域的空间用完了,java虚拟机会触发一次Mirror GC,来收集新生代的垃圾,存活下来的对象,会被送到Survivor区域。新生代有连个Survivor区域,用from和to指针来指代,from指向的Survivor区域的存货对象复制到to指针指向的Survivor区域,然后交换from和to指针,保证to指针指向的区域是空的。

垃圾收集器的原理和基本概念

垃圾回收就是把已经分配出去的,不再使用的内存回收回来,方便下一次分配。在java虚拟机里面,垃圾指的是已经死亡的对象所占用的堆空间。

自动垃圾收集的前提是清楚哪些内存是可以被释放的。
主要考虑两方面:一、最重要的部分是对象实例,都是存储在堆上面的;另一个是方法区中的元数据等信息,例如类型不再使用,卸载这个java类就很合理了。

对于对象实例的收集,主要是两种算法,引用计数和可达性分析。

  • 引用计数算法,就是给对象添加一个引用计数,用来记录对象被引用的情况,如果计数为0,说明这个对象是可以被回收的。java没有选择引用计数算法,因为引用计数法没法处理几个对象循环引用的情况。
  • 另一种是可达性分析法,通常采用这种方法。简单来说就是,把对象和它的引用关系看成一个图,选定活动的对象作为GC Roots,然后跟踪引用链条,如果一个对象和GC Roots之间不可达,就是不存在引用关系,就认为这个对象可以被回收。什么是GC Roots? 比如虚拟机栈,本地方法栈当中正在引用的对象,静态属性引用的对象和常量,已经加载类的静态变量,已经启动还没有停止的java线程。
    • 优点:可达性分析可以解决引用计数法无法解决的循环引用问题。比如,对象a和对象b之间相互引用,但是只要从GC Roots触发无法到达a或者b,那么可达性分析就可以成功地判断出a和b都是可以回收的对象。
    • 缺点:在多线程的时候其他线程可能会更改已经访问过的对象中的引用,本来被引用的对象可能被设置成未被引用,正在被引用的对象内存会被垃圾回收器回收,这可能会造成java虚拟机崩溃。 解决这个问题,可以采用一种简单粗暴的方式,就是stop-the-world,停止不是垃圾回收的线程,直到完成垃圾回收。 java虚拟机中的stop-the-world是通过安全点safepoint机制实现的。当java虚拟机收到stoptheworld请求,会等所有的线程都到达安全点之后,才允许请求stoptheworld的线程进行独占的工作。

常见的垃圾收集算法,主要分为三类:

  • 复制(Copying)算法,把内存区域分为两部分,分别用from指针和to指针来维护,并且只用from指针指向的内存区域,当发生垃圾回收的时候,把活着的对象复制到to区域,拷贝的过程中把对象按顺序放,可以避免内存碎片化。 缺点是:要复制,就要提前预留内存空间,有一定的浪费。对于G1这样的GC,需要分拆成大量的region,意味着GC需要维护各个region之间对象的引用关系,这会造成内存上的开销。
  • 标记-清除算法(Mark-Sweep),首先进行标记工作,标记处所有需要清楚的对象,然后进行清除。具体做法:把死亡对象所占用的内存标记为空闲内存,并记录在一个空闲列表(free list)中,当需要新建对象的时候,内存管理模块就会从该空闲列表中寻找空闲内存,并划分给新建的对象。
    • 缺点:一个是这样会导致碎片化问题,不适合比较大的堆,由于java的对象必须是连续分布的,极端情况下,可能会出现内存的总的空间足够,但是无法分配,因为都是碎片化的空闲内存 另一个是分配的效率低,如果是一个连续的内存,我们可以通过指针做加法来分配内存。而对于空闲列表,java虚拟机需要逐个访问列表中的项,来查找能够放入新建对象的空闲内存。
  • 标记-整理(Mark-Compact)算法,未来避免碎片化问题,他会子啊清理的过程中把对象移动,确保移动后的对象占用连续的内存空间。

垃圾收集过程的理解

java应用不断地创建对象,通常是分配在Eden区域,当他的空间利用达到一定阈值的时候会触发minor GC,仍然被引用的对象存活下来,被复制到JVM的survior区域,没有被引用的对象被回收。

Mirror是只针对新生代,Eden区域和非空的Survivor区域的存活对象会被复制到空的Survivor区域,当Survivor区域的存活对象复制超过一定次数的时候,就会被晋升到老年代。

猜你喜欢

转载自blog.csdn.net/shida_hu/article/details/84280835