一、概述
说起java人们总会想到它的垃圾收集器,从java诞生以来,GC就是它的伴生产物。对于GC我一直有几个疑问,那就是:1、哪些内存是需要被回收的?2、是在什么时候会被回收 ?3、是通过怎么样的机制进行回收?带着这些问题,深入的去学习。
二、对象和类的的生命周期
2.1、java对象的生命周期
java对象在JVM的生命周期有:
1、创建(created):
对象通过 new、反射、clone()等方式被创建。创建过程中,系统需要给它,①分配存储空间;②构建对象;③从父类到子类static成员的初始化;④父类变量的初始化,递归调用父类构造方法;⑤初始化子成员的变量,递归调用子类构造方法。
2、应用(in use):关于应用阶段,系统至少需要维护一个对象的强引用,当然关于弱引用,软引用和虚引用会文章后面进行介绍。
3、不可见(invisiable):不可见阶段就是程序不再有该类的任何引用。如果一个对象不需要被调用,那么就可以把它指向null,方面后期的回收。
4、不可达(unreachable):是指JVM 根据GC roots 再也找不到直接或者间接的强引用。这些对象属于垃圾回收对象,但并不会马上被回收。
5、收集(collected):这个阶段对象准备好并且垃圾回收器已经做好了内存重新分配的准备,则对象进入收集阶段。如果被判定使用finalize 方法,那么这个对象就会被放在一个F-queue队列中,执行虚拟机的方法则该对象不会被回收。需要注意的是finalize 方法只有第一次执行有效。还有就是不建议使用该方法。
(注:不建议重写finalize方法,会影响jvm对象分配和回收速度,原因是,在分配对象时,会在垃圾回收器上注册该对象,以便在垃圾回收时能执行finalize方法,在方法的执行时,需要消耗CPU,其次,在finalize方法当中可能持有其他的强引用持有该对象,会造成对象的复活,由收集阶段变为应用阶段,不利于后面的代码管理)
6、终结 (finalized):当执行完finalize方法之后,对象仍然处于不可达状态,则对象进入到终结阶段,等待垃圾回收器对该对象的空间进行回收。
7、对象空间重分配(de-allocated):
垃圾回收器对该对象所占用的空间进行了回收或者重新分配,这个对象在程序当中彻底结束了,称之为对象空间重新分配阶段。
2.2 对象的引用
对象的引用主要包括,强引用、弱引用、软引用和虚引用。
①强引用:一般指的是创建一个对象并赋予该对象一个应用变量。当强引用有引用变量指向时,则永远不会 执行GC;
比如:
Object ob=new Object();
String str="hello world!";
②软引用:就是借助于 SoftReference类来实现的对象。如果一个对象具有软引用,在内存空间足够的时候,就不会执行GC,当内存不足时,就会被GC。
软引用可用来实现内存敏感的高速缓存,比如网页缓存、图片缓存等。使用软引用能防止内存泄漏。
Object obj=new Object();
SoftReference rel=new SoftReference (obj);
③弱引用:用来描述非必需的对象,无论内存是否充足,都会回收弱引用关联的对象。在java中 使用WeakReference 类来实现。
④虚引用:如果一个对象跟虚引用关联,在任何时候都可能会被GC。
3.1类的生命周期
类的生命周期就是Class 文件在JVMz中从加载到销毁的过程,主要包括下面几个阶段:
1、加载:虚拟机将class文件的信息加载到方法区中,然后在内存当中生成一个java.lang.Class对象,作为方法区当中这个类的入口。不同的虚拟机,加载机制不一样,有的使用预加载,不管是否用到先加载到虚拟机,有的是用到才进行加载。
2、连接:连接阶段和加载阶段是交叉运行的,但是加载阶段一定是早于连接阶段开始的,连接阶段一定是晚于加载阶段结束的;连接阶段有可以分为下面三个阶段:
①验证:验证该类是否符合JVM规范(比如 属性是否重复,是否存在相同的方法,继承是否合理等)
②准备:该阶段主要是为类的静态变量分配内存,设置JVM的默认值。静态变量的初始值并不是程序当中设置的,而是每个类型的默认初始值,比如int,long之类的默认值是0,引用类型默认值是null,常量的值就是我们程序当中设定的值。
③解析:把常量池当中的符号引用解析为直接引用,就是将所有的类或者接口,字段名,方法名转换为内存地址
3、初始化:这个阶段就是给静态变量赋值(执行顺序:父类静态域,静态代码块,子类静态域,静态代码块);
4、使用:
①对象实例化:执行类中的构造方法,如果有父类则会执行父类的构造方法,在内存中为父类开辟内存空间;然后根据构造函数的代码将真正的赋予实例变量,然后引用变量获取对象的首地址,同过操作对象来调用实例变量和方法。
②垃圾回收:当对象不再被使用的时候,就会被虚拟机标记上记号,在堆中等待gc回收
③ 对象终结:当对象被gc回收之后,对象就不存在了。
5、卸载
当满足以下条件的时候,类就会被卸载掉
①该类的所有实例都已经被回收,在java堆中不存在这个类的任何实例。
②加载该类的ClassLoader被回收。
③该类对应的java.lang.Class对象没有被任何地方引用,无法通过反射访问该类 。
三、垃圾收集
3.1垃圾收集算法
1、标记-清除算法
该算法分为两个阶段,先对所有需要回收的对象进行标记,然后再统一回收所有对象。
该算法有两个不足的地方:①标记和清除的效率不高。 ②在标记清除完以后留下大量不连续的内存碎片。
2、复制算法
3、标记整理算法
4、分代收集算法:分代算法就是根据不同区域或者阶段使用不同的算法,在新生代有大量的对象产生和销毁,采用复制算法效率最高。而在老年代对象的存存活率比较高,那就必须使用 标记-清除 或者标记-整理 来处理。
3.2垃圾收集器
四、内存分配与回收策略
1、对象优先在Eden分配
大多数情况下对象将在新生代Eden区分配内存,当该区域没有足够当空间当时候,虚拟机将进行一次Minor GC。
2、大对象直接进入老年代
java中所谓当大对象是指 需要大量连续内存空间当Java对象。最典型当就是很长当字符串或者数组。
3、长期存活当对象将进入老年代
4、动态对象年龄判定
如果相同年龄当所有对象当大小的总和大于Survivor空间的一半,年龄大于或者等于的该年龄的对象就可以直接进入老年代。
5、空间分配担保