前言

一般来说分布式系统都会用zookeeper做协调操作,无论怎样的分布式协作方案分布式主机之间的联系一般通过发送心跳信息来通知自身节点的存活,那么问题就来了,如果复杂系统产生了大量java对象会导致频繁GC甚至长时间GC,这时候JVM会暂停,如果这个时候心跳连接超时,对于一个高容错的完善的分布式系统此时节点就会下线。

上述问题比较严重,这里主要为了说明避免多次GC和长时间GC问题。除此之外呢,注意文章时间,听说现在新版jdk有新的实验性质的垃圾回收算法,能在jvm不暂停的情况下GC,那么此文章就会过时,我估计这个时候不会很远,具体算法这里不详述

这篇随笔就随便写写,实际上这设计很底层的gc问题,也不是大神能去完全解决此类问题,这里简单聊聊浅薄的想法。

一般来说,这种情况很难出现,很多情况下是框架本身的问题(比如垃圾apache.ignite,实际上gridgain公司自己推出的社区办gridgain.ignite就很好),更多时候是针对框架使用的优化。

但是我是个爱思考的人,在此之外呢,如果真的是自己代码中产生大量对象,那么就需要解决此类问题。

常用场景解决方案

  1. 长时间gc的场景,一般是full gc导致,导致full gc的原因呢,这里就不详述,这种情况一般就配置jvm参数,根据实际情况调大老年代内存之类的处理。
  2. 使用一个对象队列,对对象重复回收使用,但是有重点,主义对象数量,避免oom
  3. 十分危险的方法,使用unsafe对象,分配堆外内存,需要自行申请内存并且释放内存。不推荐使用,unsafe对象的使用,在下只在大型企业应用中见过,像是hadoop,ignite之类,做做小玩意就算了。

特殊情况说明

有一种创建线程时没做限制,假定有个触发动作会创建一个线程,并且线程池没做好线程数量的限制,甚至没用线程池,而且没预估好创建的线程数,线程对象一直创建,这时候不仅仅是GC问题,OOM更可能发生。

这种情况即使没有长时间的GC,频繁生成对象也会导致性能问题。解决方案也很简单,采用消费者的思想,固定几个消费者线程,把原触发创建线程的动作封装成一个对象放入队列,消费者轮询队列获得对象,把处理逻辑放在消费者线程中。看起来线程少了,触发可能不及时,但是实际上在高并发等情况下是既安全又高效的。安全就不必说了,众所周知,CPU一个核执行多线程实际上在同一时刻只能执行一个线程,但是通过不断切换到不同线程执行,所以更宏观的时间上是执行了多线程,而CPU调度切换时需要时间的,往复杂了说还涉及线程状态寄存等方面。要是线程太多,CPU不断切换到不同线程有很耗时的;而固定几个线程,反而效率高,那到底是几个线程呢,具体内容不详述,网上资料比较多,也比较简单,这里不再扩展。