(1)对于内存里面的运行时数据区域,哪些需要回收?哪些不需要?怎么分?
区分:
1)内存分配和回收都具有确定性,随着方法结束或者线程结束被回收-->随线程生死的区域,以及栈中的栈帧
例如:程序计数器(Program Counter)、虚拟机栈(Virtual Machine Stack)、本地方法栈(Native Method Stack)、栈帧(Stack Frame)
2)内存的分配和回收都是动态的,即它可能是程序开始运行才知道创建哪些对象,接口有多个实现类、方法有多个分支等,这一部分在java里面是被多个线程共享的内存区域。
例如:java堆和方法区
(2)回收什么样子的数据?
不能再被任何途径使用的对象
(3)怎么判断对象不再被使用?即怎么判断对象“死去”?
判断对象是否“死去”的算法?
1)引用计数算法(Reference Counting,注意存在场景)
利用引用计数器,被引用+1,引用失效-1,为0时就是不可能再被使用 -->场景为python、一些游戏脚本等
注意:主流java虚拟机不选用该算法来管理内存,主要是存在了对象直接互相引用,但是两者的引用计数不为0而无法回收的情况
2)可达性分析算法((Reachability Analysis)
以“GC Roots”为起始点,从这些节点开始向下搜索,当对象到GC Roots没有任何引用链相连则不可用,即可回收
java GC Roots: 虚拟机栈引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象(例如字符串常量)、本地方法栈中JNI引用的对象(就是native关键字修饰的方法)等
(4)怎么收集?垃圾收集算法?
1)标记-清除 算法(Mark-Sweep)
标记要回收,标记完统一回收
缺点: 效率低,标记清除后空间不连续(内存碎片,当需要分配较大对象时,因无法找到足够的连续内存而需要提前触发另一次垃圾回收?)
2)复制算法(Copying)
内存平分成两块,用的时候只用一块,用完了就把还活着的复制到另一块上,再把已使用过的空间一次性清理掉。(有其他优化的内存划分方法)
缺点:占内存
3)标记-整理算法(Mark-Compact)
标记要回收,让所有存活对象都向一端移动,直接清理掉端边界外的内存
4)分代收集(Generational Collection)
按照对象存活周期划分内存(新生代堆->复制算法(因为死得多)、老生代堆->(因为存活率高空间小))