java中的初始化与清理
一.初始化顺序
1.首先构造器实际也是静态方法。因此当首次创建Dog对象时或者Dog类的静态方法/静态域首次被访问时,java解释器必须查找类路径,已定位Dog.class文件。
2.然后载入Dog.class,有关静态初始化的所有动作都会执行。因此,静态初始化只在对象首次加载的时候进行一次。
3.当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
4.这块存储空间会被清零,这就自动的将Dog对象中的所有基本类型数据都设置成了默认值,而引用则被设置成了null。
5.执行所有出现于字段定义处的初始化动作。
6.执行构造器。
二.包含继承的初始化顺序
包含继承的初始化在父类与子类中的初始化顺序为:父类当中的静态初始化->子类当中的静态初始化->父类当中的非静态初始化->子类当中的非静态初始化
三.java垃圾回收机制
主要概括为以下三个问题:
1:哪些内存需要回收(使用‘引用计数法’‘可达性分析算法’进行判断);
引用计数法:通过判断对象的引用数量来决定对象是否可以被回收,如果对象的引用数量为0,则可以被当做垃圾回收。当一个对象被创建且该对象实例分配给一个引用变量,该对象实例的引用计数设置为1.当任何其它变量被赋值为这个对象的引用时,对象实例的引用计数加1(a=b,则b引用的对象实例的计数器加1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,该对象实例的引用计数减1.特别的,当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器均减1.
问题:很难解决对象之间相互循环引用的问题。
可达性分析算法:通过一系列名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链。当一个对象没有任何引用链相连时,则证明此对象是不可用的。
“GC Roots”以我的理解就是栈和静态存储区的引用。
2:什么时候回收(堆的新生代,老年代,永久代的垃圾回收时机,MinorGC和FullGC);
新生代:尽可能快速的收集掉那些生命周期短的对象,一般情况下,所有新生成的对象首先都是放在新生代的。新生代内存按照8:1:1的比例分为一个eden区和两个survivor(survivor0,survivor1)区,大部分对象在Eden区中生成。在进行垃圾回收时,先将eden区存活对象复制到survivor0区,如果survivor0区满了,则将eden区和survivor0区存活对象复制到survivor1区,然后清空eden和这个survivor0区,此时survivor0区是空的,然后交换survivor0区和survivor1区的角色(即下次垃圾回收时会扫描Eden区和survivor1区),即保持survivor0区为空,如此往复。特别地,当survivor1区也不足以存放eden区和survivor0区的存活对象时,就将存活对象直接存放到老年代。如果老年代也满了,就会触发一次FullGC,也就是新生代、老年代都进行回收。注意,新生代发生的GC也叫做MinorGC,MinorGC发生频率比较高,不一定等 Eden区满了才触发。
老年代:老年代存放的都是一些生命周期较长的对象,在新生代中经历了n次垃圾回收后任然存活的对象就会被放在老年代中。当老年代满时会触发Major GC,老年代存活时间长,因此Major GC发生的频率比较低。
永久代:永久代主要存放一些静态文件,如java类,方法等。永久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如使用反射、动态代理、CGLib等bytecode框架时,在这种时候需要设置一个比较大的永久代空间来存放这些运行过程中新增的类。
3:如何回收(三种经典垃圾回收算法(标记清除算法,复制算法,标记整理算法),分代收集算法);
标记清除算法:分为标记和清除两个阶段,从根集合进行扫描,对存活对象进行标记,标记完成后再扫描整个空间,并对未被标记的对象进行回收。存在两个不足:
效率不高:标记和清除两个过程效率都不高;
内存碎片较多:此算法不需要进行对象的移动,并且仅对不存活的对象进行处理,因此标记清理之后会产生大量不连续的内存碎片,如果在分配较大对象时无法找到足够的连续内存就不得不提前触发一次垃圾收集动作。
复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中一块,当这块用完了就将其中还存活着的对象移动到另外一块上去,然后把已用过的这块当中的已用内存一次清理掉。这种算法适用于对象存活率低的场景,比如新生代。实际上现在的商用虚拟机都采用这种算法来回收新生代,而实践中的内存分配如上对新生代的描述。
标记整理算法:整理过程类似标记清除算法,但后续步骤不是对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。该算法适合对象存活率高的场景(老年代);
分代收集算法:不同生命周期的对象位于堆中不同的区域,对这些不同区域采用不同策略进行回收可以提高JVM的执行效率。
新生代采用复制算法,老年代采用标记清除或者标记整理算法。