java对象的内存分配流程

java对象的内存分配流程_第1张图片

了解对象的内存分配流程对常见内存溢出问题、jvm优化有很大作用。

内存分配原则

  • 对象栈内分配

通常理解new对象都在堆中分配存储空间,但是当(通过逃逸分析 确定)对象仅在方法内使用而未被外部访问的时候,jvm会将对象分配到栈内。

逃逸分析:就是分析对象动态作用域,当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数(或者return)传递到其他地方中。

对应参数:-XX:+DoEscapeAnalysis;JDK7之后默认开启逃逸分析

  • 减量保证对象在Eden区分配

了解这一原则,必须先掌握gc流程:

  1. 对象首先分配到Edgn,存满之后触发minor gc,剩余仍然存活的对象会被存到from区;
  2. 下次Edgn存满之后再次出发minor gc,回收Edgn和from区,把剩余存活的对象转移到to 区(转移的原因参见之后的垃圾回收算法),然后form区和to区对换;

那么为什么尽量保证对象尽量存活在Edgn区呢?我们先来看看 Minor GC和Full GC 有什么不同呢?
Minor GC/Young GC:指发生新生代的的垃圾收集动作,Minor GC非常频繁,回收速度一般也比较快。
Major GC/Full GC:一般会回收老年代 ,年轻代,方法区的垃圾,Major GC的速度一般会比Minor GC的慢10倍以上

Eden与Survivor区默认8:1:1,但是jvm有默认参数 -XX:+UseAdaptiveSizePolicy(默认开启),会调节默认比例。

  • 大对象直接进入老年代

大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。JVM参数 -XX:PretenureSizeThreshold 可以设置大对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和ParNew两个收集器下有效。

  • 长期存活的对象将进入老年代

这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代

jvm采用分代收集思想来回收内存,因此每个对象必须有个年龄计数器。具体计数规则:

Edgn中对象在minor gc后存活,而且可以被survivor容纳的情况下,年龄+1;之后每次在survivor中经历一次minor gc,就会年龄+1;当年龄增加到一定程度就会移动到老年代中,具体通过参数 -XX:MaxTenuringThreshold配置

  • 对象动态年龄判断

这个规则和上个规则一样:其实是希望那些可能是长期存活的对象,尽早进入老年代

具体实现:survivor区域中,当前存放的的对象占当前区域的50%(具体可以通过 -XX:TargetSurvivorRatio 指定),那么年龄大于等于这批对象中最大值的对象,可以全部移动到老年代中了。

  • 老年代空间分配担保机制

目的:确保对象进入老年代之前有足够的空间;具体实现步骤如下:

年轻代每次minor gc之前JVM都会计算下老年代剩余可用空间
如果这个可用空间小于年轻代里现有的所有对象大小之和(包括垃圾对象)
就会看一个“-XX:-HandlePromotionFailure”(jdk1.8默认就设置了)的参数是否设置了
如果有这个参数,就会看看老年代的可用内存大小,是否大于之前每一次minor gc后进入老年代的对象的平均大小。
如果上一步结果是小于或者之前说的参数没有设置,那么就会触发一次Full gc,对老年代和年轻代一起回收一次垃圾,
如果回收完还是没有足够空间存放新的对象就会发生"OOM"
当然,如果minor gc之后剩余存活的需要挪动到老年代的对象大小还是大于老年代可用空间,那么也会触发full gc,full
gc完之后如果还是没有空间放minor gc之后的存活对象,则也会发生“OOM”

判断哪些对象已经死亡

引用计数法

可能存在相互引用的问题

可达性分析算法

将“GC Roots” 对象作为起点,从这些节点开始向下搜索引用的对象,找到的对象都标记为非垃圾对象,其余未标记的对象都是垃圾对象
GC Roots根节点:线程栈的本地变量、静态变量、本地方法栈的变量等等

参考链接:

java对象的内存分配流程 - 前度刘郎 - 博客园

你可能感兴趣的:(jvm,java,开发语言)