线上内存泄漏引发OOM问题分析和解决( 二 )


FileSystem get(URI uri, Configuration conf) throws IOException{ Key key = new Key(uri, conf); return getInternal(uri, conf, key); }private FileSystem getInternal(URI uri, Configuration conf, Key key) throws IOException{ FileSystem fs; synchronized (this) { fs = map.get(key); } if (fs != null) { return fs; } fs = createFileSystem(uri, conf); synchronized (this) { // refetch the lock again FileSystem oldfs = map.get(key); if (oldfs != null) { // a file system is created while lock is releasing fs.close(); // close the new file system return oldfs; // return the old file system }// now insert the new file system into the map if (map.isEmpty() && !ShutdownHookManager.get().isShutdownInProgress()) { ShutdownHookManager.get().addShutdownHook(clientFinalizer, SHUTDOWN_HOOK_PRIORITY); } fs.key = key; map.put(key, fs); if (conf.getBoolean("fs.automatic.close", true)) { toAutoClose.add(key); } return fs; } }从这段代码中可以看出:

  1. 在Cache类内部维护了一个Map,该Map用于缓存已经连接好的FileSystem对象,Map的Kep为Cache.Key对象 。每次都会通过Cache.Key获取FileSystem,如果未获取到,才会继续创建的流程 。
  2. 在Cache类内部维护了一个Set(toAutoClose),该Set用于存放需自动关闭的连接 。在客户端关闭时会自动关闭该集合中的连接 。
在看完了上面的代码之后,在看一下CACHE这个变量在FileSystem中是怎样引用的:
【线上内存泄漏引发OOM问题分析和解决】 /** FileSystem cache */ static final Cache CACHE = new Cache();也就是说,该CACHE对象会一直存在不会被回收 。而每次创建的FileSystem都会以Cache.Key为key,FileSystem为Value存储在Cache类中的Map中 。那至于在缓存时候是否对于相同hdfs URI是否会存在多次缓存,就需要查看一下Cache.Key的hashCode方法了,如下:
@Override public int hashCode() { return (scheme + authority).hashCode() + ugi.hashCode() + (int)unique; }可见,schema和authority变量为String类型,如果在相同的URI情况下,其hashCode是一致 。unique在FilSystem.getApi下也不用关心,因为每次该参数的值都是0 。那么此处需要重点关注一下ugi.hashCode() 。
至此,来小结一下:
  1. 在获取FileSystem时,FileSystem内置了一个static的Cache,该Cache内部有一个Map,用于缓存已经获取的FileSystem连接 。
  2. 参数fs.hdfs.impl.disable.cache,用于控制FileSystem是否需要缓存,默认情况下是false,即缓存 。
  3. Cache中的Map,Key为Cache.Key类,该类通过schem,authority,UserGroupInformation,unique 4个参数来确定一个Key,如上Cache.Key的hashCode方法 。
但还有一个问题,既然FileSystem提供了Cache来缓存,那么在本例中对于相同的hdfs连接是不会出现每次获取FileSystem都往Cache的Map中添加一个新的FileSystem 。唯一的解释是Cache.key的hashCode每次计算出来了不一样的值,在Cache.Key的hashCode方法中决定相同的hdfs URI计算hashCode是否一致是由UserGroupInformation的hashCode方法决定的,接下来看一下该方法 。


推荐阅读