垃圾回收核心思想
公理:当一个对象不再被引用时则可以被回收。
判断一个对象是否被引用的算法:1、引用计数法;2、可达性分析;
引用计数法无法解决循环引用问题,所以目前主流垃圾回收器不采用。
可达性分析需要先找到gc roots ,一般存在栈和方法区中。
JDK1.2之后,对引用的概念进行了扩充:强软弱虚
强引用永远不被回收,软引用在内存溢出时被回收,弱引用是在发生垃圾回收时都会被回收,虚引用是在被回收时发出一个通知。
垃圾收集算法分类(从低级到高级):
标记-清除算法(容易产生内存碎片)
复制算法(无碎片,但内存只能用一半)
标记-整理算法(解决了内存空间利用率问题,但用时长,效率差)
分代回收算法(综合了以上几种算法的优点,是一套组合拳,为目前主流)
JVM调优案例
亿级流量电商网站,在大促时,每秒产生1000笔订单,假设服务器有三台,每台需处理每秒300笔订单。根据经验,一个POJO对象不会超过1kb,300个订单对象就是300kb,考虑到相关联的其他对象,例如积分、用户、库存、优惠券等等,放大20倍,为6mb对象数据,可能还有其他操作,例如订单查询等,再放大10倍,为60mb对象数据,这些对象的存活时间假设都为1秒钟。
启动JVM时设置堆内存3个G,其中老年代2G,年轻代1G,年轻代又分为伊甸园区800mb,S0和S1各100mb。
出现的问题是:每隔几分钟就出现一次FullGC,造成STW(Stop The World),系统卡顿。
分析:
每秒产生的60mb对象数据大约14秒占满伊甸园区,进行一次MinorGC,会有60mb对象存活,此时JVM按动态年龄判断规则会进行一次判断,如果这些数据没有超过S0空间的一半,则进入S0区,如果超过,直接进入老年代。
事实上是超过了S0区的一半,所以直接进入了老年代。也就是说每隔14秒都有一批对象进入了老年代。而进入老年代之后的下一秒,就成为垃圾了。过一些时间,老年代满了就会触发FullGC,造成卡顿。
解决方法是:把年轻代的空间调大一些。
启动命令:
java -Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=512M -jar liandong-server.jar