堆中的新生代还有老年代永久代详解
程序计数器,虚拟机栈,本地方法栈都是线程私有的,随线程的产生而产生,随线程的消失而毁灭。所以这几个区域就可以不用考虑内存回收了,java的内存回收主要是针对于java堆还有方法区。
在java堆中分三个种类,新生代,老年代,还有永久代。
新生代:主要用来存放新创建的对象,一般占用堆内存的三分之一的空间。因为会频繁的创建对象,所以会不断的触发MinorGC进行垃圾回收。
针对新生代的垃圾回收算法是复制算法:把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,一般是15,则赋值到老年代区)同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区)然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。
对象提前晋升到老年代(组团)。
动态年龄判定:如果在 Survivor 区中相同年龄所有对象大小总和大于 Survivor 区大小的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,而无须等到自己的晋升年龄。
在新生代主要有三个区,,Eden区,两个Servivor区
他们的比例一般都是8:1:1
这样分区有什么好处呢:
首先说一下这两个区之间的互相转换,当Eden区满了以后就会进行一次MinorGC,然后把依旧存活的对象放到一个 空闲的Servivor区,然后清空其余的新生代的内存,这样循环下去,一直会有一个空闲的Servivor。
如果没有Servivor,那么新生代每进行一次gc幸存者都会进入到老年代,老年代很快就会被 填满,然后执行fullgc,因为老年代的内存都比较大,频繁触发gc是非常消耗时间的,要解决的这个问题有两种方案,
第一个方案:增加老年代的内存,这样满了之后,触发一次gc要求的时间会更长
第二个方案,减少老年代内存,这样虽然会减少gc时间,但是会是频率增加,也达不到要求。
设置两个Servivor的好处是解决了碎片化的问题。
老年代老年代的特点就是每次回收都只回收少量的对象,当对象的年龄到达15就会进入老年代。还有一种情况就是对象需要分配较大的空间时,也会被放进老年代。老年代的空间要比新生代要大,因为老年代的对象的生命周期都比较长,会被引用的时间比较久。如果老年代的空间太小,会造成频繁gc,会严重影响效率。因为每老年代需要回收的对象都是比较少的,但是每次gc都会把整个老年代扫一遍,这样效率时很低的所以新生代:老年代=1:2.
老年代的gc触发,一般都是伴随着新生代的gc,因为新生代触发一次gc,就可能会有对象进入老年代,导致老年代的内存满了。
老年代采用的垃圾回收算法是标记整理法,简单的说就是标记不回收的对象移动到一端,然后清除边界以外的内存。