垃圾收集器
Serial 收集器
算法:堆内存年轻代采用复制算法,老年代采用标记-整理算法
单线程收集器,只会使用一个CPU或者单个收集线程去完成垃圾收集工作
垃圾收集时必须暂停其他工作线程,直到垃圾收集结束,也就是Stop The World
优点是简单而高效(与其他收集器的单线程相比),对于限定单个CPU的环境来说,<br>Serial收集器由于没有线程交互的开销,专心做垃圾收集自然就可以获取最高的单线程收集效率
ParNew 收集器
算法:堆内存年轻代采用复制算法
ParNew 收集器是Serial 收集器的多线程版本
只能用于年轻代
多线程并行收集
在多CPU的环境下,随着CPU数量的增加,它对于GC时系统资源的有效利用是有益的。<br>默认开启收集线程数与CPU数量相等
ParNew收集器在单线程环境下,效率绝对不会有Serial收集器的效率高。<br>甚至在超线程计数实现的两个CPU的环境下也不一定比得过Serial收集器<br>,多线程间交互的开销还是比较大的
Parallel Scavenge 收集器
算法:堆内存年轻代采用复制算法,配合收集器Parallel Old GC,堆内存老年代使用标记-整理算法
多线程收集器
只适用于新生代
自适应调节策略
目标是达到一个可控制对吞吐量
Parallel Scavenge 收集器无法与CMS收集器配合使用
CMS 收集器
运作过程
初始标记
仅仅是标记一下 GC Roots能直接关联到的对象,速度很快,Stop The World的时间很短
并发标记
根据GC Roots追溯所有对象的过程,这个过程耗时最长
重新标记
为了修正并发标记过程期间因用户程序继续运行导致标记产生变动的那一部分标记记录,<br>这个阶段比初始标记时间长一些,但远比并发标记时间短,这个时候也会Stop The World
并发清除
特点
Concurrent Mark Swap ,基于标记-清除算法实现
各个阶段耗时:并发标记/并发清除>重新标记>初始标记
对CPU资源非常敏感
标记-清除算法导致空间碎片
并发收集,低停顿
无法处理浮动垃圾,可能出现Concurrent mode Failure ,导致一次Full GC
G1 收集器
运作过程
初始标记
仅仅是标记一下 GC Roots能直接关联到的对象,速度很快,Stop The World的时间很短
并发标记
根据GC Roots追溯所有对象的过程,这个过程耗时最长
最终标记
修正并发标记过程期间因用户程序继续运行导致标记产生变动的那一部分标记记录
筛选回收
对于各个region对回收价值和回收成本进行排序,根据用户所期望的GC停顿时间来制定回收计划
特点
G1将整个JAVA堆(包括新生代和老年代)划分为多个大小相等的内存块(Region),<br>每个Region是逻辑连续的一段内存,后台维护一个优先列表,每次根据允许收集的时间<br>优先回收垃圾最多的区域
可预测的停顿
分代收集
并行与并发
面向服务端应用的垃圾收集器
空间整合:从整体上看是基于“标记-整理”算法,从局部(两个Region)上看是使用的“复制”算法
问题总结
局部变量表与操作数栈有什么区别
局部变量表是存放编译器可知的各种基本数据类型,对象引用类型和方法返回地址。<br>通过索引访问;局部变量表所需内存空间在编译期就已经确定。
操作数栈是工作区,在这里根据指令对数据进行压栈、出栈,进行数据计算;<br>通过入栈、出栈方式访问
StackOverflowError 与 OutOfMemoryError有什么区别
StackOverFlowError是栈溢出,指请求的栈深度超过JVM规定的栈深度
OutOfMemoryError,是指请求的内存空间不住
内存泄漏跟内存溢出的区别
内存泄漏,是指某个内存地址不能被垃圾收集器收集
内存溢出是表示程序运行,申请的内存空间不足。内存泄漏会导致内存溢出
符号引用和直接引用的区别
符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可
(1)直接指向目标的指针(比如,指向“类型”【Class对象】、类变量、类方法的直接引用可能是指向方法区的指针)<br>(2)相对偏移量(比如,指向实例变量、实例方法的直接引用都是偏移量)<br>(3)一个能间接定位到目标的句柄
常量池和运行时常量池的区别
存在于静态的存储文件中,class
存在于内存中,可以动态添加
Minor GC和FullGC的区别
Minor 新生代GC,发生频繁且回收时间短
Full GC 发生在老年代,耗费时间长。通常会伴随一次Minor GC
垃圾回收
why?为什么要了解垃圾回收
提高系统性能,突破应用性能瓶颈
排查各种内存溢出,内存泄漏的问题
what?哪些内存需要回收
JAVA堆和方法区
JAVA堆是对无用对象的回收,JAVA方法区是对废弃常量以及无用的类进行回收
what2?哪些对象需要回收(对象可达性判断方法)
引用计数法
原理:给对象添加引用计数器,每当对象有一个引用,计数器+1;<br>引用失效,计数器-1;当引用计数器为0时,对象可回收。
优点:实现简单,判断效率高
缺点:存在循环引用问题
可达性分析法(GC Roots)
原理:通过一系列称为Gc Roots的对象作为起始点,<br>从起始点搜索的路径称为引用链。当一个对象没有与任何引用链相连时,<br>则证明此对象是不可用的,可以被回收
GC Roots对象种类
JAVA虚拟机栈引用的对象
本地方法中引用的对象
类静态属性引用的对象
常量引用的对象
when?什么时候进行回收
达到Minor GC或者Full GC的触发条件时
Minor GC触发条件
当新生代(Eden Space)空间不足时
Full GC触发条件
调用System.gc(),向系统建议执行Full GC,但是不一定都执行
当老年代空间不足
当方法区空间不足
将对象转存到老年代,但老年代空间不足保存该对象时
how?如何回收(垃圾回收算法)
标记-清除算法
原理:该算法分为【标记】和【清除两个阶段】,<br>首先标记所有需要回收的对象,在标记完成后统一进行回收
特点
效率:标记和清除两个阶段效率都不高
空间:标记和清除后未进行碎片整理,产生大量不连续的内存片段,<br>分配大对象时空间不够会经常触发垃圾回收,影响程序效率
标记-整理算法
原理:该算法分为【标记】和【整理】两个阶段,标记所有需要回收的对象,<br>整理不对标记的对象直接进行清理,而是先让存活的对象移动到一遍,<br>清除掉端边界以外的内存
特点
效率:两个阶段效率都不高
空间:解决了内存空间碎片的问题
复制算法-新生代采用的算法
原理:新生代分为Eden、survivor1(from)、Survivor2(to)三部分,每次使用Eden或者<br>其中一块Survivor进行回收时,将存活对象复制到另外一块Survivor,然后清理刚刚使用Eden或者Survivor<br>
特点
效率:每次对整个半区进行回收,内存回收时,不需要考虑内存碎片的情况,<br>内存分配时只需要移动顶端指针,效率高
空间:未进行垃圾回收时,会有一部分空间未使用上
分代收集算法
原理:将JAVA堆分为新生代和老年代,根据各个代的特点使用合适的回收算法
特点:利用各个年代的特点,采用合适的算法
新生代:垃圾收集时发现只有少量的对象存活,选择复制算法,<br>只需要付出少量的对象复制成本,就可以完成垃圾回收
老年代:因为对象存活率高,没有额外空间进行分配担保,使用标记-清理算法和标记整理算法