上一篇我们介绍了Java对象创建的过程,那么这一篇我们介绍下Java对象的内存分配过程。
上图是内存分配的大致流程,
首先我们看到的是对象是否进行栈上分配,我们知道对象是分配在堆中的,当我们堆中的对象没有被引用而成为垃圾对象时,需要依靠GC来进行回收内存,但当这些未被引用的对象太多时 会给GC带来较大的压力,为了减少这些临时对象在堆上进行分配。JVM进行了一些优化,它通过逃逸分析来判断一个对象是否可以在栈上分配,如果可以那么就在栈上分配,这样随着栈帧的出栈该对象占用的空间就被销毁了,这样就减少了垃圾回收的压力。
接下来我们说说什么是逃逸分析:分析对象的动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,那么这种对象就不适合栈上分配。如下图所示,第一种情况user对象就不适合在栈上分配,第二种情况的user对象就适合在栈上分配。
开启逃逸分析 ‐XX+DoEscapeAnalysis
开启标量替换 ‐XX:+EliminateAllocations (jdk 1.7 之后默认开启)
public User test1() {
User user = new User();
user.setId(1);
user.setName("zhuge");
return user;
}
public void test2() {
User user = new User();
user.setId(1);
user.setName("zhuge");
}
TIP1 : 堆分为新生代和老年代,新生代中又分Eden区 和Survior 区,它们的比例默认是8:1:1
-XX:+UseAdaptiveSizePolicy(默认开启) 会导致这个8:1:1比例自动变化
-XX:-UseAdaptiveSizePolicy 自适应
TIP2 :
Minor GC/Young GC:指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。

minor gc 的大致过程就是按照上图循环往复。需要注意的是,每惊醒一次minor gc 对象的分代年龄就会+1 ,当 达到15(默认)时就会挪向老年代,当然不同的垃圾收集器会有略微的不同,我们可以通过jvm参数来进行控制:
-XX:MaxTenuringThreshold 设置对象进入老年代的年龄阈值
当然除了上面讲述的分配特性还有其它的特性,如大对象直接进入老年代,因为大对象需要大量连续的内存空间,在内存分配复制时十分消耗资源,我们可以通过jvm参数设置大对象的大小
需要注意的是 这个参数的设置 只在Serial 和 ParNew 两个垃圾收集器下有效。
接下我们再说一下,对象的动态年龄判断机制,首先介绍下概念,在S区中一批对象的总大小 超过S区内存大小的50%时,
-XX:TargetSurvivorRatio 可以指定超过的百分比
那么大于等于这批对象年龄最大值的对象就直接进入老年代,用公式表达就是 年龄1+年龄2+...+年龄N >= 1/2 S
年龄N + 年龄N以上 的所有对象进入老年代,这样是为了让那些长期存活的对象今早进入老年代。
上述这些操作一搬会在minor gc 后触发。除了这些还有像老年代空间分配担保机制 等有兴趣的可以自己去了解下。
说完了分配,这里再说下对象内存的回收,堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。
这里有两个方法用来判断,第一个是 引用计数法,

接下来再说个小知识点: