private static void clearOutPmfClassLoaderCache (PersistenceManagerFactory pmf) {
    if (!(pmf instanceof JDOPersistenceManagerFactory)) {
      return;
    }
    // NOTE : This is hacky, and this section of code is fragile depending on DN code varnames
    // so it's likely to stop working at some time in the future, especially if we upgrade DN
    // versions, so we actively need to find a better way to make sure the leak doesn't happen
    // instead of just clearing out the cache after every call.
    JDOPersistenceManagerFactory jdoPmf = (JDOPersistenceManagerFactory) pmf;
    NucleusContext nc = jdoPmf.getNucleusContext();
    try {
      Field pmCache = pmf.getClass().getDeclaredField("pmCache");
      pmCache.setAccessible(true);
      Set<JDOPersistenceManager> pmSet = (Set<JDOPersistenceManager>) pmCache.get(pmf);
      for (JDOPersistenceManager pm : pmSet) {
        org.datanucleus.ExecutionContext ec = pm.getExecutionContext();
        if (ec instanceof org.datanucleus.ExecutionContextThreadedImpl) {
          ClassLoaderResolver clr =
              ((org.datanucleus.ExecutionContextThreadedImpl) ec).getClassLoaderResolver();
          clearClr(clr);
        }
      }
      org.datanucleus.plugin.PluginManager pluginManager =
          jdoPmf.getNucleusContext().getPluginManager();
      Field registryField = pluginManager.getClass().getDeclaredField("registry");
      registryField.setAccessible(true);
      org.datanucleus.plugin.PluginRegistry registry =
          (org.datanucleus.plugin.PluginRegistry) registryField.get(pluginManager);
      if (registry instanceof org.datanucleus.plugin.NonManagedPluginRegistry) {
        org.datanucleus.plugin.NonManagedPluginRegistry nRegistry =
            (org.datanucleus.plugin.NonManagedPluginRegistry) registry;
        Field clrField = nRegistry.getClass().getDeclaredField("clr");
        clrField.setAccessible(true);
        ClassLoaderResolver clr = (ClassLoaderResolver) clrField.get(nRegistry);
        clearClr(clr);
      }
      if (nc instanceof org.datanucleus.PersistenceNucleusContextImpl) {
        org.datanucleus.PersistenceNucleusContextImpl pnc =
            (org.datanucleus.PersistenceNucleusContextImpl) nc;
        org.datanucleus.store.types.TypeManagerImpl tm =
            (org.datanucleus.store.types.TypeManagerImpl) pnc.getTypeManager();
        Field clrField = tm.getClass().getDeclaredField("clr");
        clrField.setAccessible(true);
        ClassLoaderResolver clr = (ClassLoaderResolver) clrField.get(tm);
        clearClr(clr);
        Field storeMgrField = pnc.getClass().getDeclaredField("storeMgr");
        storeMgrField.setAccessible(true);
        org.datanucleus.store.rdbms.RDBMSStoreManager storeMgr =
            (org.datanucleus.store.rdbms.RDBMSStoreManager) storeMgrField.get(pnc);
        Field backingStoreField =
            storeMgr.getClass().getDeclaredField("backingStoreByMemberName");
        backingStoreField.setAccessible(true);
        Map<String, Store> backingStoreByMemberName =
            (Map<String, Store>) backingStoreField.get(storeMgr);
        for (Store store : backingStoreByMemberName.values()) {
          org.datanucleus.store.rdbms.scostore.BaseContainerStore baseStore =
              (org.datanucleus.store.rdbms.scostore.BaseContainerStore) store;
          clrField = org.datanucleus.store.rdbms.scostore.BaseContainerStore.class
              .getDeclaredField("clr");
          clrField.setAccessible(true);
          clr = (ClassLoaderResolver) clrField.get(baseStore);
          clearClr(clr);
        }
      }
      Field classLoaderResolverMap =
          AbstractNucleusContext.class.getDeclaredField("classLoaderResolverMap");
      classLoaderResolverMap.setAccessible(true);
      Map<String, ClassLoaderResolver> loaderMap =
          (Map<String, ClassLoaderResolver>) classLoaderResolverMap.get(nc);
      for (ClassLoaderResolver clr : loaderMap.values()) {
        clearClr(clr);
      }
      classLoaderResolverMap.set(nc, new HashMap<String, ClassLoaderResolver>());
      LOG.debug("Removed cached classloaders from DataNucleus NucleusContext");
    } catch (Exception e) {
      LOG.warn("Failed to remove cached classloaders from DataNucleus NucleusContext", e);
    }
  }
Removed cached classloaders from DataNucleus
DataNucleus caches classloaders in NucleusContext.
In UDFs, this can result in classloaders not getting GCed resulting in PermGen leaks.
This is particularly an issue when using embedded metastore with HiveServer2,
since the current classloader gets modified with each new add jar,
becoming the classloader for downstream classes, which DataNucleus ends up using.
The NucleusContext cache gets freed up only on calling a close on it.
We're not closing NucleusContext since it does a bunch of other things which we don't want.
We're not clearing the cache HashMap by calling HashMap#clear to avoid concurrency issues.
standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/PersistenceManagerProvider.java