1、内存管理
1. 对象空间的分配:使用new关键字创建对象即可
2. 对象空间的释放:将对象赋值null即可。垃圾回收器将负责回收所有”不可达”对象的内存空间。
2、垃圾回收过程
1. 发现无用的对象 重点是怎么发现无用的对象?
2. 回收无用对象占用的内存空间。
3、垃圾回收相关算法
3.1 引用计数法(几乎被抛弃)
引用计数的含义是跟踪记录每个值(对象)被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则该值的引用次数就是1;如果同一个值又被赋给另一个变量,则该值的引用次数加1;如果包含对该值引用的变量又取得了另外一个值,则该值的引用次数减1。当该值的引用次数变为0时,则可以回收其占用的内存空间。当垃圾回收器下一次运行时,就会释放那些引用次数为0的值所占用的内存。
存在的问题:相互引用不会完全准确,因为如果出现两个对象相互引用的问题就不行了。
public class ReferenceCountingGC { public Object instance = null; public static void testGC() {
ReferenceCountingGC objA = new ReferenceCountingGC(); //step 1 ReferenceCountingGC objB = new ReferenceCountingGC(); //step 2
//相互引用 objA.instance = objB;//step 3 objB.instance = objA;//step 4
objA = null; //step 5
objB = null; //step 6
//假设在这行发生CG,objA和objB是否能被回收? 不能!!!! System.gc(); } } |
分析上述代码:
很明显,到最后两个实例都不再用了(都等于null了),但是GC却无法回收,因为引用数不是0,而是1,这就造成了内存泄漏。也很明显,现在虚拟机都不采用此方式。
内存泄露:当新生代中无足够空间为对象创建分配内存,年老代中内存回收也无法回收到足够的内存空间,并且新生代和年老代空间无法在扩展时,堆就会产生OutOfMemoryError异常。使用String而不是StringBuilder容易造成内存泄露。
3.2根搜索算法
通过一系列的GC Roots的对象作为起始点,从这些根节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
根搜索算法图解:红色代表不可达对象(可回收对象)
可以作为GC Roots的对象包括以下几点:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中的类静态属性引用的对象或者常量引用的对象。
- 本地方法栈中JNI(就是native方法)引用的对象。
补充:
java方法区在Sun HotSpot虚拟机中被称为永久代,很多人认为该部分的内存是不用回收的,java虚拟机规范也没有对该部分内存的垃圾收集做规定,但是方法区中的废弃常量和无用的类还是需要回收以保证永久代不会发生内存溢出。判断废弃常量的方法:如果常量池中的某个常量没有被任何引用所引用,则该常量是废弃常量。
3.3 分代收集算法
图3.3堆内存的划分细节
垃圾回收过程:
1、新创建的对象,绝大多数都会存储在Eden中。
2、当Eden满了(达到一定比例)不能创建新对象,则触发垃圾回收(GC),将无用对象清理掉,然后剩余对象复制到某个Survivor中,如S1,同时清空Eden区。
3、当Eden区再次满了,会将S1中的不能清空的对象存到另外一个Survivor中,如S2, 同时将S1区中的不能清空的对象,也复制到S2中,保证Eden和S1,均被清空。
4、重复多次(默认15次)Survivor中没有被清理的对象,则会复制到老年代Old(Tenured)区中。
5、当Old区满了,则会触发一个一次完整地垃圾回收(FullGC),之前新生代的垃圾回收称为(minorGC)
补充:
如果空白Survivor空间无法存放下仍然存活的对象时,使用内存分配担保机制,直接将新生代依然存活的对象复制到年老代内存中,同时对于创建大对象时,如果新生代中无足够的连续内存时,也直接在年老代中分配内存空间。
另外: System.gc()只是建议启动垃圾回收器 具体是否调用未知