Java VM的内存分为两种:栈 Stack和堆 Heap。栈主要是存放内存指令区域,Heap存放内存数据。栈中的数据都是定长的。类的静态属性存放于栈中,非静态属性存放于Heap数据区。Heap内的数据只有通过stack中的地址才能访问,因为静态属性必须位于stack指令区中。
Java VM的垃圾回收机制主要有两种:引用计数和引用对象遍历。这都是针对Heap数据区的,因为stack的内存是顺序分配的,而且是定长的,所以不存在回收的问题。
引用计数
引用计数方式是在早起的Java VM中采用的垃圾回收机制。每个对象在创建时都有一个对应的引用次数计数,在将对象赋予一个新的引用时计数器加1,在响应对象超过生命周期或者被赋予新值时,计数器减一。在每次垃圾回收就把计数为0的对象进行回收。
这样的方式存在一个问题就是循环引用的问题,此时无法识别循环引用,也就会造成内存的泄露。优势是可以很快的执行。
引用对象遍历
对象引用从一组对象开始,根据每一个引用链条,遍历所有的可遍历到的对象,在遍历时需要记录下那些对象可以遍历,那些对象不可以遍历,称作标记。可以遍历到的对象就不必回收,遍历不到的就是需要回收的对象。这种回收机制比较耗时,很多VM都采取多线程的方式进行回收操作。
Heap堆的具体的回收过程
Java VM会把内存空间分为堆和栈,垃圾回收只是针对堆的操作,这一点上文已经阐述。堆又划分为新生代Young Generation, 旧生代Old Generation以及。新生代继续分为Eden Space和Survivor Space,而Survivor Space又分为From Space和To Space.垃圾回收就是在新生代和旧生代之间进行,而两者又采用不同的回收策略。
新生代的回收
新创建的对象都放在Eden Space中,当Eden Space中满了时,会把仍然存在的对象copy到survivor区中其中一个区中,当这个区中也满了,就复制到另一个Survivor
区中,当这个区也满了时会将这个区的对象复制到Old Generation中。两个Survivor区至少有一个是空的,而且根据需要可以设置多个Survivor区。
在新生代中经过多次回收后仍然存活的对象会被复制到年老代中,可以说年老代中的对象都是一些生命周期比较长的对象。
分代回收的好处
分代回收的思想可以避免一些生命周期比较长的对象被频繁的遍历,所以大大加快了回收的速度。