JVM之堆空间(下)|Java 开发实战

114 阅读4分钟

这是我参与更文挑战的第 4 天,活动详情查看: 更文挑战

4.堆上GC分类

主要分为两大类型,一个是部分收集,一个是整堆收集。

  • 部分收集:
    • 新生代收集(Minor GC):只针对新生代的垃圾收集(Eden,S0,S1)
    • 老年代收集(Major GC):针对老年代的垃圾收集。**注意:**经常和Full GC混淆使用
  • 整堆收集(Full GC):收集整个Java堆

新生代收集(Minor GC)触发机制:

  • 当新生代的内存不足,就会触发Minor GC,这里的新生代主要指Eden区,Servivor区内存满不会触发GC。
  • 因为Java的大部分对象创建和销毁都在新生代,所以Minor GC的触发几率特别高。
  • 每次GC都会引发STW,暂停其他用户的线程,等GC结束才会恢复。

老年代收集(Major GC/Full GC)触发机制:

  • 当老年代的内存不足,就会触发Major GC或Full GC
  • 在Major GC之前都会伴随至少一次Minor GC,也就是在老年代内存不足会先尝试Minor GC,如果空间还不足则触发Major GC。
  • Major GC至少比Minor GC慢三倍之上,带来的STW也会更慢。
  • Major GC 之后还是内存不足报OOM。

Full GC触发机制(需要避免的GC):

  • 调用System.gc()方法,系统建议指向Full GC 但不必然执行。
  • 老年代空间不足。
  • 方法区空间不足。
  • 通过Major GC后进入老年代的大小大于老年代可用的内存。
  • 在Servivor区复制对象时,对象大小大于Servivor时会直接放入老年代,如果老年代内存不够会触发Full GC。

5.堆空间的参数设置

  • -XX:+PrintFlagsInitial :查看所有参数的默认初始值
  • `-XX:+PrintFlagsFinal :查看所有参数的最终值
  • -Xms :初始堆空间内存
  • -Xmx :最大堆空间内存
  • -Xmn :设置新生代内存
  • -XX:NewRatio=:新生代与老年代在堆结构的占比
  • -XX:SurvivorRatio:设置新生代中Eden和S0/S1空间的比例
  • -XX:MaxTenuringThreshold:设置新生代垃圾的最大年龄
  • -XX:+PrintGCDetails:输出详细的GC处理日志 打印gc简要信息:① -XX:+PrintGC ② -verbose:gc
  • -XX:HandlePromotionFailure:是否设置空间分配担保

6.逃逸分析

在JVM中,Java对象分配在堆空间是一个常识,但是如果经过逃逸分析后发现,一个方法并没有逃逸出方法了话,那么就有可能优化为栈上分配。能使用方法内定义的对象,就不要使用方法外的对象。在JDK7之后默认都会开启逃逸分析。

逃逸分析的作用就是分析对象的作用域:

  • 当一个对象在方法内定义后,对象只在方法内部使用,则认为没有发生逃逸。
  • 当一个对象在方法内定义后,对象被外部所引用,则认为发生逃逸。

如果没有发生逃逸会优化的内容:

  • 栈上分配:如果一个对象没有发生逃逸,那么就可能被优化为栈上分配。分配后继续执行栈帧的操作,最执行完毕栈空间被回收,局部变量也顺带被回收,这样就不会触发GC。
  • 同步省略:如果一个对象只能从一个线程被访问到,那么整个对象可以不用同步。比如在同步时使用了一个对象加锁,但是锁对象每次都是在这个方法内new出来,那么这个锁本身就是无效的,JVM会自动去掉锁。整个步骤也叫做锁消除
  • 分离对象(标量替换):Java中原始数据类型就是标量,相对,可以分解的数据叫做聚合量,比如自己创建的对象。分离对象的作用就是把聚合量拆解为标量,然后直接存入栈内,减少堆空间的占用和GC。

总结:在目前的Hotspot虚拟机中并没有应用栈上分配技术,当前的优化效果主要是因为标量替换。对此,目前来说对象实例都是分配在堆上的。