1.java堆的基本结构与一个对象在堆中的分配
其中的Eden,Survivor0和Survivor1被称为新生代,Tenured被称为老年代。
当一个对象被new出来之后,如果他非常的大,那么直接装入Tenured中。否则装入Eden中,在一次GC之后,Eden中存活下来的对象就复制到Survivor0中。接下来的GC中如果Survivor0中存活下来的对象就复制到Survivor1中,接下来存活的对象就在Survivor0和Survivor1中不断移动,如果经过比较多次的GC之后Survivor0和Survivor1中还有对象存在,则将他移动到Tenured中。
Survivor0和Survivor1使用的是标记-复制算法,Tenured中使用的是标记-整理算法。
2.如何判断对象已死?
引用计数算法:
我们可以为每一个对象设置一个count引用计数器,每当一个地方引用它的时候,计数器就加1,当引用失效的时候,计数器就减1;当计数器的值为0的时候,就代表着这个对象不会再被使用了,这个时候就可以对该对象进行回收。
这个算法十分简单,但是并没有被广泛的使用。因为这个看似简单的原理需要考虑很多例外的情况。
可达性算法:
这是当前比较主流的判断对象是否应该被回收的方式。基本思路就是通过一系列被称为“GC Roots”的跟对象作为起始节点集,如果某个对象到GC Roots之间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象已经不可达的时候,则证明这个对象不可能再被使用了。
由上图可以出Object A,Object B,Object C和Object D对于GC Roots都是可达的,因此表示这几个对象还存在对于他们的引用,但是Object E和Object F对于GC Roots都是不可达的,则他们就应该被回收。这个时候就出现了一个问题,什么样的东西可以被看作GC Roots?
可以作为GC Roots的对象:
(1)在虚拟机栈(本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用的参数、局部变量、临时变量等。
(2)在方法区中类静态属性引用的对象,譬如java类的引用类型静态变量。
(3)在方法区中常量引用的对象。
(4)在本地方法栈中的Native方法引用的对象。
(5)java虚拟机内部的引用。
(6)所有被同步锁(synchronized关键字)持有的对象。
(7)反映java虚拟机内部情况的JMXBean、JVMTI中注册的回调,本地代码缓存等。
3.关于引用
jdk1.2之后,java对引用的概念进行了扩充,将引用分为强引用(Strongly Reference),软引用(Soft Reference),弱引用(Weak Reference)和虚引用(Phantom Reference)。这四种引用的强度依次减弱。
强引用(Strongly Reference):就是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object obj = new Object()”这种引用关系。无论何时,只要强引用存在,垃圾收集器就永远不会回收被引用的对象。
软引用(Soft Reference):用来描述一些还有用,但非必须的对象。只要有被弱引用的对象,在系统将要发出内存溢出异常之前,会把这些对象列入回收范围之中进行第二次回收,如果这次回收之后还没有足够的内存,才会抛出内存溢出异常。
弱引用(Weak Reference):与软引用的定义类似,但比软引用更弱,被弱引用的对象只能生存到下一次垃圾收集为止,当垃圾收集器开始工作时,无论内存是否足够,都会回收掉被弱引用的对象。
虚引用(Phantom Reference):也称“幽灵引用”和“幻影引用”。无法通过虚引用来取得一个对象的实例。它存在的唯一目的是为了能在这个对象被垃圾收集器收集的时候收到一个系统的通知。
即使在可达性算法中被判定为不可达的对象,是否就一定救“非死不可”呢?
一个对象在被垃圾收集器回收的过程中总需要经历两个阶段。第一个阶段就