JVM中垃圾回收的判定标准

最终目的是将内存中无用的对象回收掉。具体的判定方法有:

  • 引用计数法,不采用,指的是维护对象被引用的次数,次数为0则意味着是垃圾。
  • 可达性算法-GC Roots tracing,指的是从GC Roots开始往下遍历所有引用的对象,(每个GC Root就是一个树状图),所有被引用到的对象就是需要存活的对象,其他对象可以被回收。GC Root指的是,虚拟机栈(栈帧中的本地变量表)中引用的对象,方法区中非基本类型的类静态变量(一个地址)所引用的对象,本地方法栈中JNI(即一般说的Native方法)引用的对象。

JVM中内存相关参数

  • -Xms Java堆内存初始大小
  • -Xmx Java堆内存最大大小
  • -Xmn Java堆内存中的新生代大小,扣除它就是老年代大小
  • -XX:PermSize(1.8之后:-XX:MetaspaceSize) 永久代初始大小
  • -XX:MaxPerSize(1.8之后:-XX:MaxMetaspaceSize) 永久代最大大小
  • -Xss 每个线程的栈内存大小

注:通常情况下,Xms和Xmx,-XX:PermSize和-XX:MaxPerSize都会设置为一样。

  • -XX:MaxTenuringThreshold 多少岁进入老年代-默认15
  • -XX:PretenureSizeThreshold 超过多少字节的大对象直接进入老年代
  • -XX:HandlePromotionFailure MinorGC时,如果老年代剩余空间小于新生代对象总大小,但是如果大于之前平均进入老年代对象的大小,是否尝试进行MinorGC(默认开启)
  • -XX:SurvivorRatio=8 Eden区的比例
  • -XX:+UseParNewGC 指定使用ParNewGC
  • -XX:ParallelGCThreads,指定ParNewGC线程数量,一般不指定ParNewGC会自动根据CPU核数适配

上面看不懂的参数不要深究,等下提到回过头再来看,这里只是将所有参数罗列出来方便查找。

JVM中的内存分代模型

JVM中,将对象在内存中分为了三代:

  • 年轻代:很快被回收的对象,存在于堆,具体还在内存中分为了1个eden区,和2个survivor区。
  • 老年代:长期存在的对象,存在于堆
  • 永久代:指的就是方法区(存放Class元数据),回收条件较苛刻,需满足:该类所有实例对象所有已经从堆内存被回收,该类classLoader已经被回收,该类Class对象没有任何引用

为什么要分代勒,因为针对每个年龄代,都有不同的垃圾回收算法,以及内存分配机制。如果将所有对象放在一起,第一是会造成频繁遍历判断回收的开销,第二是会造成复制、移动的开销,为什么会有复制、移动,因为回收内存必然会造成内存碎片,而内存碎片会导致空间浪费,所以必须通过复制、移动来清理随便,使得空闲内存连续。

JVM中具体的内存分配模型

JVM从零开始 -垃圾回收机制以及内存分代模型-LMLPHP

如上图,至于年轻代为什么要如此分配,与特定的回收算法有关。

对象在内存分代中如何流转

年轻代

大部分对象(除了元数据,如"xxx"字符串的地址)刚创建的时候都会分配在年轻代的Eden区,只要年轻代空间不够就会触发MinorGC(只回收年轻代内存)。

老年代

老年代的对象都是从年轻代根据一定的规则流转过来的。具体有几类流转方式:

  • 超过指定年龄(参数-XX:MaxTenuringThreshold 配置,默认15),这里年龄指的是没有被垃圾回收,存活下来一次理解为增加一岁。流转到老年代。

  • 大对象直接进入,超过参数指定字节数(-XX:PretenureSizeThreshold)设置的字节数的大对象会直接进入老年代,这是因为对象越大,复制开销就越大。

  • 动态年龄判断规则进入,意思是不一定要到指定年龄再流转到15,如果某一年龄以上的对象到达一定大小,也会提前进入老年代。当躲过一轮GC的对象加起来超过surrvivor区50%,如年龄1+年龄2+年龄n一直累加,直到年龄n的时候发现加起来超过了surrvivor空间的50%,则年龄n以上的对象直接进入老年代

  • 年轻代放不下,直接进入老年代。这里面有一定的规则,等会介绍。

当年轻代分配担保条件不满足时或满足了年轻代分配担保条件但在实施过程中发现老年代空间不够时触发fullGC。

永久代

永久代一般

睡觉了先,上面总结的不是很好,明天接着改

09-03 08:50