基础知识
java堆中分三大块:
新生代(Young Gen)又包括Eden区survivo区(部分虚拟机)对象基本首先存放在这。
老年代(Tenured Gen)存放一些存活时间较长,内存较大的对象。
永久代(perm Gen)持久代主要存放类定义、字节码和常量等很少会变更的信息。
1.哪些内存需要回收?
判断一个对象是否会被回收,需要考虑:作用域,引用状态,虚拟机内存。
1.1引用计数算法
思路:每当对象被引用,引用计数器就会加1。如果出现两个对象相互引用的情况的时候,会出现问题。
1.2可达性分析算法
思路:GC roots 的对象作为起点,向下搜索,所走过的路径叫引用链,当一个对象没有与任何一个GC roots 连接,则对象不可用
GC roots 对象包括这几种:
虚拟机栈(栈帧中的本地方法变量)中引用的对象。
方法区中类静态属性引用对象。
方法区中常量引用的对象。
本地方法栈中JNI(native修饰的方法)引用的对象。
(简单来说就是所有引用类型对象)
1.3再谈引用
引用分为强引用、软引用、弱引用和虚引用四种。
-
强引用类似于“Object obj = new Object()”这类引用,垃圾收集器永远不会回收被强引用的对象。
- 软引用描述一些还有用但非必须的对象。在系统将要发生内存溢出异常前,将会把这些软引用关联着的对象列入回收范围中进行第二次回收。使用SoftReference类实现软引用。
- 弱引用描述非必须对象。被弱引用关联的对象只能生存到下一次垃圾收集发生之前。WeakReference类实现弱引用。
-
虚引用的唯一目的是能在这个被虚引用关联的对象被回收时收到一个系统通知。PhantomReference类实现虚引用。
1.4生存还是死亡
两次标记:第一次筛选出没有引用对象,第二次筛选出没有调用过finalize()方法的对象。
两次标记完成后对象进入F-queue队列懒性执行,现在还可以给对象赋值引用,救活他,只能救活一次。
这里有个疑问,为什么只能救活一次?
1.5回收方法区(永久代回收)
永久代回收的内容=废弃常量+无用类。
判断废弃常量:没有任何对象引用。
判断无用类:不存在类的实例,加载该类的ClassLoader已经被回收,该类对应的java.lang.class对象没有任何地方引用。
2.什么时候回收?
2.1标记-清除算法
两大不足:效率问题,标记和清除效率都不高,空间问题,产生大量不连续的碎片。
2.2复制算法
不足:会浪费一部分内存去作为缓存。
解决方案:将内存分为3部分,新生代eden空间80%,两个相同大小的老年代survior空间各10%。每次清理的时候,会将新生代的80%清理和10%的老年代进行回收,把存活的复制到另一个老年代10%中,如果超出了10%,就会向其它内存借内存。
2.3标记-整理算法
将所有存活的对象向一个方向移动过。
2.4分代收集法
当前商业虚拟机通用的方法。根据对象的存活周期的不同划分为老年代和新生代,新生代使用复制算法,老年代使用标记-清理法或标记-整理法
3.如何回收?
3.1枚举根节点
枚举根节点:GC roots 的一个枚举。
oopmap:是一个存放调研信息的对象。
安全点:GC执行的时间点。选取是根据程序是否长时间运行,例如,循环调用。
在JIT编译过程中,使用一种OopMap的数据结构存放在常量池和方法区里的引用类型的信息。GC发生时首先让所有的线程都停顿下来,然后让还没到安全点的线程恢复,跑到安全点。几乎没有虚拟机采用这种方式响应GC。然后接着运行垃圾收集器。
3.2垃圾收集器
相连的两个收集器可以互相合作工作。
1.serial收集器
一个单线程的收集器。关闭所有的用户线程,然后启动单一的GC线程,直到线程结束。
优点:对于限定于单个cpu,简单而高效,对client模式下的虚拟机是一个很好的选择。
缺点:很多,多个cpu,只能一个cpu工作。
2.parNew收集器
parNew收集器其实是parNew的多线程版本。
优点:可以多线程同时回收垃圾。
缺点:需要停止用户线程。
3.parallel Scavenge 收集器
paraller Scavenge收集器是一个新生代收集器,使用复制算法的,多线程的收集器,他的目标达到一个可控制的吞吐量。
4.serial Old 收集器
serial Old收集器是Serial 的老年代版本,他是一个单线程收集器,使用”标记-整理“算法
5.parallel old收集器
parallel old是parallel Scavenge收集器的老年代版,使用多线程和“标记-整理”算法。
6.CMS收集器
优点:并发收集,低停顿。
缺点:对cpu资源非常敏感,无法处理浮动垃圾,大量空间碎片产生。
7.G1收集器(jdk1.7)
优点:并发与并行,分代收集,空间整合,可预测停顿。
3.3BC日志
对于Tomcat,我们可以在catalina.bat(linux下是catalina.sh)中设置VM的arguments来打印输出GC日志。在catalina.bat的开始位置配置
-XX:+PrintGCDetails -verbose:gc -Xloggc:d:/gc.log
3.4垃圾收集器的参数总结
3.5内存分配与回收策列
大多数情况下对象都在Eden分配,当Eden没有足够的空间时,虚拟机会发出一次minor GC(老年代 major GC/Full GC),通过担保空间机制,将eden的对象复制到surivivor.通过虚拟机的参数-XX:PretenureSizeThreshold可以将大对象直接进入老年代, 虚拟机给每个对象都定义了一个对象年龄计数器,通过设置虚拟机参数-XX:MaxTenuringThreshold可以将多次存活的对象直接进入老年代。