一.Java堆内存分布图
二.对象的分配规则
1.对象主要分配在新生代的Eden区上
2.如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。TLAB的全称是Thread Local Allocation Buffer,即线程本地分配缓存区,这是一个线程专用的内存分配区域。
3.少数情况下也可能直接分配在老年代(对象过大,超过Eden区大小或者From区大小)
三.GC参数指定垃圾回收
-Xms20M、-Xmx20M、-Xmn10M这3个参数限制了Java堆大小为20MB,不可扩展,其中10MB分配给新生代,剩下10M分配给老年代。-Xx:SurvivorRatio=8决定了新生代中Eden区与两个Survivor区的空间比例是8:1
四.新生代与老年代回收
新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度比较快。
老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般比Minor GC慢10倍以上。
五.指定大对象超过一定大小直接分配在老年代参数
虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值得对象直接在老年代分配。这样做的目的是避免在Eden区及两个Survivor区之间发生大量的内存复制
六.逃逸分析
逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,称为方法逃逸。甚至还有可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸。
七.栈上分配
栈上分配就是把方法中的变量和对象分配到栈上,方法执行完后自动销毁,而不需要垃圾回收的介入,从而提高系统性能
八.FullGC触发条件
1.调用System.gc():此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发Full GC。因此强烈建议能不使用此方法就不要使用,让虚拟机自己去管理它的内存,可通过-XX:+DisableExplicitGC来禁止RMI调用System.gc()
2.老年代空间不足:老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等,当执行Full GC后空间仍然不足,则抛出oom。为避免以上原因引起的Full GC,调优时应尽量做到让对象在Minor GC阶段被回收,让对象在新生代多存活一段时间以及不要创建过大的对象以及数组
3.空间分配担保失败:使用复制算法的Minor GC需要老年代的内存空间作担保,如果出现了HandlePromotionFailure担保失败,则会触发Full GC
九.频繁创建大对象导致频繁FullGC解决办法:
1.最大内存-xmx大小缩小,从而缩短FULL GC
2.集群部署这样每个节点的最大内存不会太大,发生FULL GC执行的时间不会太长