JVM系列(3)——内存分配与回收策略

我们紧接着上文JVM系列(2)——垃圾回收继续进行总结。本文主讲内存分配与回收策略。

一、分代收集算法

根据对象存活周期的不同将内存划分为几块(堆中)。
在这里插入图片描述
(1)在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
(2)在老年代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理算法或者标记-整理算法来进行回收。

二、内存分配

内存分配,主要讲的就是对象在堆上分配(也可能会栈上分配):
(1)对象主要优先分配在新生代的Eden区上,
(2)如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。
(3)少数对象也可能直接分配在老年代中,大对象直接进入老年代,长期存活的对象进入老年代。

2.1 优先分配在新生代的Eden区

Eden:翻译过来是伊甸园的意思。从字面理解,代表着美好、新生。从另一方面理解,那就是对象的新生之地。
大多数情况下,对象分配在新生代的Eden区。当Eden区没有足够的内存空间进行分配的时候,虚拟机会发起一次Minor GC,回收新生代Eden区中的垃圾对象。
Minor GC: 是只发生在新生代的垃圾回收,Java对象大多数都具备朝生夕灭的特点(用的很快,死的也很快),所以Minor GC非常频繁,一般回收速度也很快;

(1)-XX:+PrintGCDetails
配置收集器日志参数,通知虚拟机在发生垃圾收集时打印内存回收日志,并且在进程退出时输出当前内存各区域
分配情况。
(2)-XX:NewRatio=4
表示设置年轻代:老年代的大小比值为1:4,这意味着年轻代占整个堆的1/5
(3)-XX:SurvivorRatio=8
表示设置2个Survivor区:1个Eden区的大小比值为2:8,这意味着Survivor区占整个年轻代的1/5,这个参数默认
为8
(4)-Xmn20M
表示设置年轻代的大小为20M

2.2 线程优先在TLAB上分配

多个线程同时在堆上申请空间,而堆要同步处理,会导致性能下降。
Thread Local Allocation Buffer:线程本地分配缓存,从名称上看是一个线程专用的内存分配区域,是为了加速对象分配而生的。每一个线程都会产生一个TLAB,该线程独享的工作区域。(TLAB是在堆的Eden区中)

设置参数:

-XX:+UseTLAB:打开 TLAB(默认是开启的)。
-XX:TLABWasteTargetPercent 设置 TLAB 空间所占用 Eden 空间的百分比大小。

2.3 老年代

1、大对象直接进入老年代
需要大量连续内存空间的Java对象称为大对象,经常出现大对象容易导致提前触发垃圾收集以获取更大的连续的空间来进行大对象的分配。写程序时尽量避免“短命大对象”。

-XX:PretenureSizeThreshold来设置对象直接进入老年代的阈值,当对象大于这个值直接进入老年代,这样做
是为了避免在Eden区和Survivor区之间发生大量的内存复制。
-XX:PretenureSizeThreshold=3145728
表示对象大于3145728(3M)时直接进入老年代分配,这里只能以字节作为单位

2、长期存活的对象将进入老年代
(1)虚拟机给每个对象定义了一个年龄计数器。对象出生在Eden区、经过一次Minor GC后仍然存活,并能够被Survivor容纳,设置年龄为1,对象在Survivor区每次经过一次Minor GC,年龄就加1,当年龄达到一定程度(默认15),就晋升到老年代。

-XX:MaxTenuringThreshold=1
表示对象年龄大于1,自动进入老年代

(2)动态对象年龄判断:对象的年龄到达了MaxTenuringThreshold可以进入老年代,同时,如果在survivor区中相同年龄所有对象大小的总和大于survivor区的一半,年龄大于等于该年龄的对象就可以直接进入老年代。无需等到MaxTenuringThreshold中要求的年龄。
Major GC:指发生在老年代的 GC,出现了 Major GC,经常 会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里就有直接进行 Major GC 的策略选择过程)。Major GC 的速度一般会比Minor GC慢10倍以上。
Full GC:当老年代满时会引发Full GC,Full GC将会同时回收年轻代、老年代。

三、空间分配担保机制

如图所示:
JVM系列(3)——内存分配与回收策略_第1张图片

采用空间分配担保机制的原因:
新生代采用复制收集算法,但为了内存利用率,只使用一个Survivor空间来作为轮换备份,假如大量对象在Minor GC后仍然存活(最极端情况为内存回收后新生代中所有对象均存活),而Survivor空间是比较小的,这时就需要老年代进行分配担保,把Survivor无法容纳的对象放到老年代。
老年代要进行空间分配担保,前提是老年代得有足够空间来容纳这些对象,但一共有多少对象在内存回收后存活下来是不可预知的,因此只好取之前每次垃圾回收后晋升到老年代的对象大小的平均值作为参考。使用这个平均值与老年代剩余空间进行比较,来决定是否进行Full GC来让老年代腾出更多空间。

四、对象分配优先级

JVM系列(3)——内存分配与回收策略_第2张图片

如图所示:
(1)栈上分配:经过JIT编译后,将线程私有的不可能被其他线程访问的对象打散(拆散为标量类型)间接分配在栈上,而不是分配在堆上。
(2)分配优先级如图所示。

你可能感兴趣的:(jvm,java,java,jvm)