先了解几个名词:
释放垃圾占用的空间,防止内存泄露,有效的使用内存,对堆中已经死亡的或者长时间没有使用的对象进行清除和回收。
系统后台只允许GC线程运行,在用户不可见的情况下把其他线程则会全部暂停,等待GC线程执行完毕后才能再次运行,这对于实时性要求很高的程序来说是难以接受的。
比如2号内存区域是1m,5号内存区域是2m,他们都被回收了,现在来了一个3m的对象是不能存放的,因为他们内存是两个位置,不能合并。
开辟内存空间时,需要的是连续的内存区域,我们需要一个 2M的内存区域,其中有2个 1M 是没法用的。这样就导致,其实我们本身还有这么多的内存的,但却用不了。
方法区(Method Area):静态变量、常量、编译后的代码
本地方法区(Native Method Stack):native方法
栈(VM Stack):变量
堆(Heap):对象实例
程序计数器(Program Counter Register):
为了保证程序能够连续地执行下去,控制程序从哪开始继续执行(类似断点续传)
怎么定义垃圾?
一、引用计数算法
原理:引用一次就+1,删除引用就-1,引用数为0就回收
问题:如果置空两个互相引用的对象,那么两个引用数都不会为0,无法回收
二、可达性分析算法
原理:采用引用链的方法,没有任何引用链相连时,才回收,
改进点:解决了引用计数所无法解决的问题:“循环依赖”
怎么回收垃圾?
一、标记清除算法
原理:标记了就清除
问题:容易产生内存碎片
二、复制算法:
原理:分存平分为AB两块,只用一块,A块用满了就把存活的复制到B块,再把A块内存空间清理干净。
改进点:解决标记清除算法的内存碎片问题
问题:100内存区域,只有50在用,代价太高,有点浪费空间
三、标记整理算法
原理:和标记清除算法一样,但不是直接清理,而是让所有存活的对象都向一端移动,再清理另一端的内存区域。
改进点:解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端问题
问题:但它对内存变动更频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。
终极方案:分代收集
原理:把 Java 堆分为新生代和老年代,根据各个年代的特点采用最适当的收集算法。
新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
老年代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记 — 整理算法来进行回收
改进点:分代收集算法严格来说并不是一种思想或理论,而是融合上述3种基础的算法思想,而产生的针对不同情况所采用不同的算法。