一、内存判定对象可回收的机制
1、引用计数算法:
该算法通过给对象添加引用计数器来判定其可回收性。当有地方引用对象时,计数器值加1;引用失效时,计数器值减1;计数器为0时,对象不再被使用。
主流Java虚拟机未选用此算法,因为难以解决对象之间相互循环引用的问题。
2、可达性分析法:
通过一系列称为『GCRoots』的对象作为起始点,向下搜索形成引用链。当对象到GC Roots没有任何引用链相连时,则证明该对象不可达,可以被回收。
GC Roots包括虚拟机栈中引用的对象、本地方法栈中Native方法引用的对象、方法区中类静态属性引用的对象以及方法区中常量引用的对象。
二、内存回收算法
1.标记清除算法
当成功区分出内存中存活对象和死亡对象后,GC接下来的任务就是执行垃圾回收,释放掉无用对象所占用的内存空间,以便有足够的可用内存空间为新对象分配内存。
1.1执行过程(先标记后清除)
当堆中的有效内存空间(available memory)被耗尽的时候,就会停止整个程序(也被称为stop the world),然后进行两项工作,第一项则是标记,第二项则是清除。
标记:收集器(Collector )从引用根节点开始遍历,标记所有被引用的对象。
一般是在对象的 Header 中记录为可达对象。标记的是引用的对象,不是垃圾!!
清除:收集器(Collector )对堆内存从头到尾进行线性的遍历,如果发现某个对象在其 Header中 没有标记为可达对象,则将其回收。
注意:这里所谓的清除并不是真的置空,而是把需要清除的对象地址保存在空闲的地址列表里。下次有新对象需要加载时,判断垃圾的位置空间是否够,如果够,就存放覆盖原有的位置。
1.2缺点
标记清除算法的效率不高。
在进行GC的时候,需要停止整个应用程序,用户体验较差。
这种方式清理出来的空闲内存是不连续的,产生内碎片,需要维护一个空闲列表,就会导致上面的问题出现(明明空间够用,但是却填不进去)
2.标记整理算法
2.1定义
和标记清除一样,标记整理的第一个阶段也是对垃圾对象进行标记,区别主要在第二个步骤,即整理。所谓的整理就是避免之前标记清除时的内存碎片的问题,他就会在清除的过程中,会把可用的对象向前给他移动,这样的话让内存更为紧凑,这就是整理的过程。整理之后,就能发现内存变的更紧凑了,即连续的空间就更多了,这样就不会造成内存碎片。
2.2优缺点
优点:
整理之后,就能发现内存变的更紧凑了,即连续的空间就更多了,这样就不会造成内存碎片。
解决了“标记-复制算法”需要分配担保的问题。
缺点:
1、“标记-清除算法”在分配对象阶段更为复杂,“标记-整理算法”是移动式回收算法,在老年代中大部分对象都是存活的,因此在回收对象时,会伴随大量的对象移动,从而会导致对象回收阶段会占用相对多的时间
2.如果那些局部变量或对象引用了这个,那么还得改变那些变量或对象的引用地址,因为内存地址变了,所以涉及的工作就比较多一些,牵扯到内存区块的拷贝移动,牵扯到所有引用的地址加以改变。所以速度较慢
3.复制算法
3.1定义
复制算法比较特殊,他把内存区域划分成了大小相等的两块儿区域,左边区域称之为FROM,右边区域称之为TO,其中TO这个区域始终空闲着,即里面一个对象都没有。
3.2执行过程
首先做一次标记(在FROM区域),就找到那些不被根对象引用的对象标记为垃圾,然后在FROM区域这些存活的对象内存空间复制到TO区域中,复制的过程中就会完成碎片的整理,因此也不会产生碎片,等复制完成后,可以看到FROM区域全是垃圾,所以一下子给予清空,并且之后交换FROM和TO他两的位置,即原来的TO变成了FROM,原来的FROM变成了TO,所以TO总是空闲的一块儿。
1.先扫谁是垃圾 2.把好的不是垃圾的放进空的to里面 3.交换to-》form的位置 4.删除垃圾
3.3优缺点
优点:没有内存碎片
缺点:需要双倍的内存空间(网友戏称双倍快乐)
4.分代垃圾回收
4.1为什么要分代?
一般来说,堆分为新生代和老年代,这样就可以根据各个年代的特点选择合适的垃圾收集算法。
在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成垃圾收集的过程。
而老年代的对象存活率是比较高的,且没有额外的空间对它进行分配,所以我们必须选择 “标记-清除” 或 “标记-整理” 算法进行垃圾收集。
4.2垃圾回收分为哪些类型?
部分收集 (Partial GC):
新生代收集(Minor GC / Young GC):只对新生代进行垃圾收集;
老年代收集(Major GC / Old GC):只对老年代进行垃圾收集。需要注意的是 Major GC 在有的语境中也用于指代整堆收集;
混合收集(Mixed GC):对整个新生代和部分老年代进行垃圾收集。
整堆收集 (Full GC): 收集整个 Java 堆和方法区。
4.3分代垃圾回收的过程
1.对象首先会分配在伊甸园区域
2.新生代空间不足时,触发 minor gc,伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的对象年龄1并目交换 firom to
3.minor gc 会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
4.当对象寿命超过闻值时,会晋升至老年代,最大寿命是15 (4bit)
5.当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,STw的时间更长
4.4相关的VM参数
|参数 | 含义 |
|-Xms–|-用于设置JVM初始分配的堆内存大小,即Java虚拟机启动时所占用的内存大小-|
| -Xmx或–XX:MaxHeapSize=size | 设置最大 Java 堆大小,最大不应该超过总内存的70%|
|-Xmn或(–XX:NewSize=size+ -XX:MaxNewSize=size)| 设置新生代大小。整个堆大小=新生代大小 + 年老代大小 + 常量池Sun官方推荐配置为整个堆的3/8 |
|-–XX:InitialSurvivorRatio=ratio和–XX:+UseAdaptiveSizePolicy-|-根据应用使用情况,自动调整幸存区的比例。可以根据年轻代(Y)大小和初始幸存区比例®来计算幸存区初始空间(S)大小,公式:S=Y/(R+2)。公式中的 2 代表有2个幸存区,幸存区初始比例®越大,则幸存区初始空间越小。默认值 8。如果年轻代空间是 2MB,幸存区空间则为 0.2 MB。
| -XX:SurvivorRatio=ratio |设置 eden 区和幸存区 survivor 区比率。默认值 8。 |
|–XX:MaxTenuringThreshold=threshold-|-即年轻代晋升老年代的最大年龄阈值。默认值是15-|
|-XX:+PrintTenuringDistribution | 打印晋升年龄信息。默认禁用此选项。 |
|-XX:+PrintGCDetails -verbose:gc| 用于打印输出详细的GC收集日志的信息 |
|–XX:+ScavengeBeforeFullGC-|-新生代GC优先于Full GC执行-|