1.什么是内存溢出,什么是内存泄漏,有什么区别
内存的溢出指无法申请到足够的内存空间,导致内存溢出;
内存泄漏是指中动态分配内存给一些临时对象,但对象不会被GC回收,它始终占用内存,被分配的对象可达但已无用,内存被耗尽。
JAVA的垃圾回收机制
在进行垃圾回收使考虑的3个问题
(1)哪些内存需要回收?
(2)什么时候回收?
(3)如何回收?
在堆内存中存储着JAVA中的实例对象,垃圾收集器在进行回收前,首先需要判断,哪些对象在被使用,哪些对象已经无用
判断对象是否存活的算法主要有两个(a)引用计数算法;(b)可达性分析法;
(a)引用计数法:给对象分配引用计数器,每当有一个对象引用它时,计数器的值就+1;当引用失效是,计数器的值-1;任何计数器的值为0的对象就是不能再被引用的。
(b)可达性分析算法:通过一系列称为“GC ROOTS”的对象作为起始点,从这些节点开始向下搜索,当一个对象到GC roots没有任何引用链的时候,证明此对象无用。
垃圾收集的算法
(a)标记-清除算法
原理:标记出所有需要标记的对象,标记完成后统一回收所有被标记的对象;
不足:效率不高;空间问题:标记清楚后产生大量不连续的内存碎片;
(b)复制算法
原理:将可用内存等分两份,当一块用完时,将存活的对象复制到另外一块上,把使用过的内存空间一次性清理掉。每次在半个区域内进行内存回收
优点:实现简单,运行高效,适用于回收新生代。
不足:因为进行复制操作导致效率降低。
(c)标记-整理算法
原理:类似于标记-清除算法,只是将存活对象都向一端移动,直接清理掉端边界以外的内存。
大多数适用于:老年代
(d)分代整理算法
原理:将Java堆分为新生代和老年代,根据不同的年代选取不同的回收算法。
新生代中每次收集都有大量对象死去,少量存活,选用复制算法;
老年代对象中存活率高,没有额外空间对它进行担保,采用“标记清理”或“标记整理”算法进行回收。
目前大部分回收机制采用的是可达性分析算法,选取Gc roots根节点的方法(枚举根节点--安全点选取-安全区域选取)
垃圾收集器
Serial收集器:
新生代收集器,单线程(垃圾收集时,必须暂停其他所有的工作线程,知道收集结束),采用复制算法,可以与CMS共同工作;
适用于:Client模式下的虚拟机。
ParNew收集器
Serial收集器的多线程版本,Server模式下的新生代收集器,可以与CMS配合使用,适合多CPU。
Parallel Scavenge收集器
新生代收集器,使用复制算法,并行的多线程收集器,注重优化吞吐量,即CPU用于运行代码的时间与CPU总消耗时间的比值。
Serial Old收集器
是Serial收集器的老年代版本,单线程收集器,采用标记-整理算法,主要用于Client模式下的虚拟机使用,。
Parallel Old 收集器
是Paraller Scavenge收集器的老年代版本,使用多线程,“标记-整理”算法
在注重吞吐量的场合可以优先考虑Parallel Old+Parallel Scavenge。
CMS收集器
原理:以获取左端回收停顿时间为 目标的收集器,B/S模式,采用“标记-清除”算法
运行过程:
(1)初始标记;(2)并发标记;(3)重新标记;(4)并发清除;
G1收集器
特点
(1)并发与并行:使用多个CPU缩短“Stop the world”的时间,其他收集器原本需要停顿线程执行的GC动作,G1可通过并发的方式让JAVA程序继续运行。
(2)分代收集:不需要其他堆配合就可以独立管理整个GC堆
(3)空间整合:整体采用“标记-整理”算法,局部采用“复制算法”,不会产生内存空间碎片。
(4)可预测的停顿:建立可预测的停顿时间 模型。
并发与并行的区别:
并发(Parallel):指多条垃圾收集线程并行工作,此时用户线程出于等待状态;
并发(Concurrent):指用户线程与垃圾收集线程同时执行,用户程序在继续执行,垃圾收集程序在另一个CPU上运行。