前言
java的内存分配和垃圾回收往往是影响系统性能和并发能力的主要因素,虚拟机提供许多的参数就是为了根据不同环境和请教下进行调优,没有最好的调优也没有固定的调优。需要我们深入的去了解jvm的各个垃圾回收机制和内存分配等知识。在java运行内存区域里面,java虚拟机栈、程序计数器、本地方法栈这3个伴随着线程生或者灭,是具备确定性的,所以我们主要研究java堆上面的这块内存怎么分配和回收。
判断对象存活的方法
- 1 引用计数法:这个经典的对象存活判定算法的思路主要是给一个对象添加一个引用计数器,每当有一个引用该计数器就+1,而当一个引用失效时该计数器就-1,最后在任何时刻一旦该对象的引用计数器为0的时候该对象就不能使用。但是java里面并非采用该算法,因为该算法无法解决对象之间的相互引用的问题,也就无法导致相互引用的对象无法被GC。
- 2 可达性分析法:商业语言例如java、c#都是采用一个“GC roots”的对象来判定该对象在一系列的引用链中不可达时,就判定该对象是不可用的,这些对象就被认为需要回收。(但是一个对象的真正销毁需要经历两次标记过程)如下图示意;
** java中有4种可以作为GC Roots的对象
- 虚拟机栈中引用的对象
- 方法区类静态属性引用的对象
- 方法区常量引用的对象
- 本地方法栈中JNI引用的对象
java的引用分类
- 1 强引用 类似 Object o = new Object();这类引用在代码广泛存在的, GC是不会回收的
- 2 软引用 存在一些有用但非必须的对象,在系统发生内存溢出异常强,这些对象列入第二次回收的范围
- 2 弱引用 非必需对象且只能生存到下次GC发生之前
- 3 虚引用 又称虚灵引用和幻影引用,一个对象设置成了虚引用的唯一目的就是当该对象被GC时收到一个系统通知
回收方法区
根据jvm虚拟机而言,其实一次GC能将堆中70~95%空间回收。但是方法区的废弃常量和无用的类都需要被回收。
无用的类,满足下面3个条件:
- 1 该类的实例已经被回收,java堆不存在任何该类的实例
- 2 加载该类的classloader已经被回收
- 3 该类对于的java.lang.Class对象没有被引用,也就是无法用反射访问该类的方法。
** 垃圾收集算法
- 1 标记-清除:首先标记出需要清除的对象,标记后统一回收。存在了效率问题 和内存空间碎片化的问题!
- 2 复制算法:为了解决效率问题,复制算法将内存划分多块,一块满了之后复制到一块空的上面,然后回收完满的那块。在商业虚拟机里面,堆内存的新生代被分为了 一个Eden和2块Survivor区域,HotSpot默认Eden:survivor=8:1也就是说新生代的90%内存是可用内存,回收时将Eden和survivor复制到另一块survivor区域,然后再清理,如果超过了10%的话就需依赖其他内存区域如老年代。
- 3 标记-整理算法:加入复制算法里面有一种极端情况如对象存活率很高的情况下,复制操作算法很低,就特定设计出将标记清除算法后,先将存活对象向另一端移动,然后清掉边界外的内存,这就是标记-整理算法避免了高对象存活率复制的低效率。
- 4 分代收集算法:商业虚拟机垃圾收集采用分代收集算法,其实就是分为老年代采用标记-清理或者标记-整理,而新生代只有少量对象就采用复制算法。
** 垃圾收集器