这是我参与更文挑战的第 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虚拟机中并没有应用栈上分配技术,当前的优化效果主要是因为标量替换。对此,目前来说对象实例都是分配在堆上的。