年轻代,主要分为两个区域:
- 伊甸园区
所有新创建的对象都会存活在伊甸园区,并且伊甸园区的保存空间一定是最大的,因为产生新对象的几率是很高的。由于伊甸园区的对象可能只是临时创建,为了方便回收,所以其拥有一个Monor GC的回收操作处理。 - 存活区
在经过多次的Minor GC后依然被保留的对象,就认为不应该被回收,将其保存到存活区。
存活区分为两类:存活0区(S0区、From Space)、存活1区(S1区、To Space)
这两个存活区主要是负责对象的晋级,向老年代晋级。由于这两个存活区有一块是专门负责对象回收的一个专门负责晋升,所以有一块总是空的,并且S0、S1是会互换的(会在年轻代算法时讲到)。
由于伊甸园保存的对象一般较多,所以保存的比率是:8:1:1,两个存活区的大小是相同的。
年轻代GC算法
算法不是固定的,取决于使用的电脑的硬件环境,例如单CPU和多CPU的算法是不同的。
我们这次只了解其中的一种,复制算法(Copying):从根集合扫描出存活对象,这样经过全盘扫描才可以找到那些对象是垃圾,接着将找到的对象复制到一块完全未使用的空间
根集合:动态对象数组,比如list、队列、栈等。堆内存本质就是动态对象数组。
图中从上到下分为年轻代、存活区、老年代三部分。首先,年轻代中红色为不存活对象占用的内存空间,绿色为存活对象占用的内存空间。将绿色活跃对象复制到存活区的To Space,将存活区的From Space中活跃对象晋升到老年代,不活跃对象进行回收,这时候堆内存就成了下图的样子
年轻代中的不活跃对象和活跃对象,放到了存活区中右边的区域,存活区左边的对象不活跃的回收活跃的晋升后变成了空的,两边名称进行了交换。老年代会保存有对象。
现在也可以明白为什么存活区有一个是空的了,因为一个是负责对象回收和晋升到老年代,一个是负者接收年代代的对象
年轻代优化算法
在实际运行中,由于Eden区总是保存大量的新生对象,所以HotSpot虚拟机为了可以加快此空间的内存分配,而使用了“Bump-The-Pointer”和“TLAB(Thread-Local Allocation Buffers”两种技术负责年轻代优化。
- Bump-The-Pointer
该技术的主要特征是跟踪在Eden区保存的最后一个对象,这个最后保存的对象一般会保存在Eden区的顶部,这样每次创建新对象只需要检查最后保存的对象后面是否有足够的空间就可以很快的判断出Eden区中是否还有剩余空间,这种做法可以极大的提高内存分配速度。
缺点:在Java的内存中,所有的堆内存是线程共享的区域,因此不适合多线程操作的情况。 - TLAB(Thread-Local Allocation Buffers)
将Eden区分为多个数据块,每个数据块再分别使用“Bump-The-Pointer”技术进行对象保存与内存分配。
缺点:会产生碎片
年轻代的控制参数
01 -Xmn 设置年轻代堆内存大小,默认物理内存的1/64
02 -Xss 设置每个线程大小,JDK1.5后默认为每个线程1M大小,减少此值可以产生更 多的线程对象,可是不能无限生成
03 -XX:SurvivorRatio 设置Eden与Survivor空间的大小比例,默认“8:1:1”,不建议修改
04 -XX:NewSize 设置新生代内存区大小
05 -XX:NewRatio 设置年轻代与老年代的比率
-Xss 的意思是,如果内存是100M,1M可以产生100个Thread对象,512k就可以产生200个线程对象,再小可以产生更多的对象。可是实际业务中一个单表查询,返回十个实体对象加上先后才能本身对象,不够用就会产生溢出,太小了也不合适,所以建议不要修改。
-XX:SurvivorRatio、-XX:NewSize、-XX:NewRatio改的意义也不大,其实最有用的部分在于线程的大小分配,可是又不能随便修改,由于年轻代算法问题,理论上服务器是不能接受无限多的线程(用户)。
Heap
PSYoungGen total 1456640K
eden space 1165824K
from space 290816K
to space 290816K