diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp
index 906b473e51b..3321c32a687 100644
--- a/src/hotspot/os/aix/os_aix.cpp
+++ b/src/hotspot/os/aix/os_aix.cpp
@@ -1112,7 +1112,9 @@ void *os::dll_load(const char *filename, char *ebuf, int ebuflen) {
   }
 
   if (!filename || strlen(filename) == 0) {
-    ::strncpy(ebuf, "dll_load: empty filename specified", ebuflen - 1);
+    if (ebuf != nullptr && ebuflen > 0) {
+      ::strncpy(ebuf, "dll_load: empty filename specified", ebuflen - 1);
+    }
     return nullptr;
   }
 
@@ -1126,7 +1128,9 @@ void *os::dll_load(const char *filename, char *ebuf, int ebuflen) {
     dflags |= RTLD_MEMBER;
   }
 
-  void * result= ::dlopen(filename, dflags);
+  void* result;
+  const char* error_report = nullptr;
+  result = Aix_dlopen(filename, dflags, &error_report);
   if (result != nullptr) {
     Events::log_dll_message(nullptr, "Loaded shared library %s", filename);
     // Reload dll cache. Don't do this in signal handling.
@@ -1135,7 +1139,6 @@ void *os::dll_load(const char *filename, char *ebuf, int ebuflen) {
     return result;
   } else {
     // error analysis when dlopen fails
-    const char* error_report = ::dlerror();
     if (error_report == nullptr) {
       error_report = "dlerror returned no error description";
     }
@@ -3021,31 +3024,3 @@ void os::jfr_report_memory_info() {}
 
 #endif // INCLUDE_JFR
 
-// Simulate the library search algorithm of dlopen() (in os::dll_load)
-int os::Aix::stat64x_via_LIBPATH(const char* path, struct stat64x* stat) {
-  if (path[0] == '/' ||
-      (path[0] == '.' && (path[1] == '/' ||
-                          (path[1] == '.' && path[2] == '/')))) {
-    return stat64x(path, stat);
-  }
-
-  const char* env = getenv("LIBPATH");
-  if (env == nullptr || *env == 0)
-    return -1;
-
-  int ret = -1;
-  size_t libpathlen = strlen(env);
-  char* libpath = NEW_C_HEAP_ARRAY(char, libpathlen + 1, mtServiceability);
-  char* combined = NEW_C_HEAP_ARRAY(char, libpathlen + strlen(path) + 1, mtServiceability);
-  char *saveptr, *token;
-  strcpy(libpath, env);
-  for (token = strtok_r(libpath, ":", &saveptr); token != nullptr; token = strtok_r(nullptr, ":", &saveptr)) {
-    sprintf(combined, "%s/%s", token, path);
-    if (0 == (ret = stat64x(combined, stat)))
-      break;
-  }
-
-  FREE_C_HEAP_ARRAY(char*, combined);
-  FREE_C_HEAP_ARRAY(char*, libpath);
-  return ret;
-}
diff --git a/src/hotspot/os/aix/os_aix.hpp b/src/hotspot/os/aix/os_aix.hpp
index f2596874b05..a1db2b2be3c 100644
--- a/src/hotspot/os/aix/os_aix.hpp
+++ b/src/hotspot/os/aix/os_aix.hpp
@@ -175,8 +175,6 @@ class os::Aix {
   static bool platform_print_native_stack(outputStream* st, const void* context, char *buf, int buf_size, address& lastpc);
   static void* resolve_function_descriptor(void* p);
 
-  // Simulate the library search algorithm of dlopen() (in os::dll_load)
-  static int stat64x_via_LIBPATH(const char* path, struct stat64x* stat);
 };
 
 #endif // OS_AIX_OS_AIX_HPP
diff --git a/src/hotspot/os/aix/porting_aix.cpp b/src/hotspot/os/aix/porting_aix.cpp
index ab84dc81027..68233097b49 100644
--- a/src/hotspot/os/aix/porting_aix.cpp
+++ b/src/hotspot/os/aix/porting_aix.cpp
@@ -21,6 +21,12 @@
  * questions.
  *
  */
+// needs to be defined first, so that the implicit loaded xcoff.h header defines
+// the right structures to analyze the loader header of 64 Bit executable files
+// this is needed for rtv_linkedin_libpath() to get the linked (burned) in library
+// search path of an XCOFF executable
+#define __XCOFF64__
+#include <xcoff.h>
 
 #include "asm/assembler.hpp"
 #include "compiler/disassembler.hpp"
@@ -891,3 +897,275 @@ bool AixMisc::query_stack_bounds_for_current_thread(stackbounds_t* out) {
   return true;
 
 }
+
+// variables needed to emulate linux behavior in os::dll_load() if library is loaded twice
+static pthread_mutex_t g_handletable_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+struct TableLocker {
+  TableLocker() { pthread_mutex_lock(&g_handletable_mutex); }
+  ~TableLocker() { pthread_mutex_unlock(&g_handletable_mutex); }
+};
+struct handletableentry{
+    void*   handle;
+    ino64_t inode;
+    dev64_t devid;
+    uint    refcount;
+};
+constexpr unsigned init_num_handles = 128;
+static unsigned max_handletable = 0;
+static unsigned g_handletable_used = 0;
+// We start with an empty array. At first use we will dynamically allocate memory for 128 entries.
+// If this table is full we dynamically reallocate a memory reagion of double size, and so on.
+static struct handletableentry* p_handletable = nullptr;
+
+// get the library search path burned in to the executable file during linking
+// If the libpath cannot be retrieved return an empty path
+static const char* rtv_linkedin_libpath() {
+  constexpr int bufsize = 4096;
+  static char buffer[bufsize];
+  static const char* libpath = 0;
+
+  // we only try to retrieve the libpath once. After that try we
+  // let libpath point to buffer, which then contains a valid libpath
+  // or an empty string
+  if (libpath != nullptr) {
+    return libpath;
+  }
+
+  // retrieve the path to the currently running executable binary
+  // to open it
+  snprintf(buffer, 100, "/proc/%ld/object/a.out", (long)getpid());
+  FILE* f = nullptr;
+  struct xcoffhdr the_xcoff;
+  struct scnhdr the_scn;
+  struct ldhdr the_ldr;
+  constexpr size_t xcoffsz = FILHSZ + _AOUTHSZ_EXEC;
+  STATIC_ASSERT(sizeof(the_xcoff) == xcoffsz);
+  STATIC_ASSERT(sizeof(the_scn) == SCNHSZ);
+  STATIC_ASSERT(sizeof(the_ldr) == LDHDRSZ);
+  // read the generic XCOFF header and analyze the substructures
+  // to find the burned in libpath. In any case of error perform the assert
+  if (nullptr == (f = fopen(buffer, "r")) ||
+      xcoffsz != fread(&the_xcoff, 1, xcoffsz, f) ||
+      the_xcoff.filehdr.f_magic != U64_TOCMAGIC ||
+      0 != fseek(f, (FILHSZ + the_xcoff.filehdr.f_opthdr + (the_xcoff.aouthdr.o_snloader -1)*SCNHSZ), SEEK_SET) ||
+      SCNHSZ != fread(&the_scn, 1, SCNHSZ, f) ||
+      0 != strcmp(the_scn.s_name, ".loader") ||
+      0 != fseek(f, the_scn.s_scnptr, SEEK_SET) ||
+      LDHDRSZ != fread(&the_ldr, 1, LDHDRSZ, f) ||
+      0 != fseek(f, the_scn.s_scnptr + the_ldr.l_impoff, SEEK_SET) ||
+      0 == fread(buffer, 1, bufsize, f)) {
+    buffer[0] = 0;
+    assert(false, "could not retrieve burned in library path from executables loader section");
+  }
+
+  if (f) {
+    fclose(f);
+  }
+  libpath = buffer;
+
+  return libpath;
+}
+
+// Simulate the library search algorithm of dlopen() (in os::dll_load)
+static bool search_file_in_LIBPATH(const char* path, struct stat64x* stat) {
+  if (path == nullptr)
+    return false;
+
+  char* path2 = os::strdup(path);
+  // if exist, strip off trailing (shr_64.o) or similar
+  char* substr;
+  if (path2[strlen(path2) - 1] == ')' && (substr = strrchr(path2, '('))) {
+    *substr = 0;
+  }
+
+  bool ret = false;
+  // If FilePath contains a slash character, FilePath is used directly,
+  // and no directories are searched.
+  // But if FilePath does not start with / or . we have to prepend it with ./
+  if (strchr(path2, '/')) {
+    stringStream combined;
+    if (*path2 == '/' || *path2 == '.') {
+      combined.print("%s", path2);
+    } else {
+      combined.print("./%s", path2);
+    }
+    ret = (0 == stat64x(combined.base(), stat));
+    os::free(path2);
+    return ret;
+  }
+
+  const char* env = getenv("LIBPATH");
+  if (env == nullptr) {
+    // no LIBPATH, try with LD_LIBRARY_PATH
+    env = getenv("LD_LIBRARY_PATH");
+  }
+
+  stringStream Libpath;
+  if (env == nullptr) {
+    // no LIBPATH or LD_LIBRARY_PATH given -> try only with burned in libpath
+    Libpath.print("%s", rtv_linkedin_libpath());
+  } else if (*env == 0) {
+    // LIBPATH or LD_LIBRARY_PATH given but empty -> try first with burned
+    //  in libpath and with current working directory second
+    Libpath.print("%s:.", rtv_linkedin_libpath());
+  } else {
+    // LIBPATH or LD_LIBRARY_PATH given with content -> try first with
+    // LIBPATH or LD_LIBRARY_PATH and second with burned in libpath.
+    // No check against current working directory
+    Libpath.print("%s:%s", env, rtv_linkedin_libpath());
+  }
+
+  char* libpath = os::strdup(Libpath.base());
+
+  char *saveptr, *token;
+  for (token = strtok_r(libpath, ":", &saveptr); token != nullptr; token = strtok_r(nullptr, ":", &saveptr)) {
+    stringStream combined;
+    combined.print("%s/%s", token, path2);
+    if ((ret = (0 == stat64x(combined.base(), stat))))
+      break;
+  }
+
+  os::free(libpath);
+  os::free(path2);
+  return ret;
+}
+
+// specific AIX versions for ::dlopen() and ::dlclose(), which handles the struct g_handletable
+// This way we mimic dl handle equality for a library
+// opened a second time, as it is implemented on other platforms.
+void* Aix_dlopen(const char* filename, int Flags, const char** error_report) {
+  assert(error_report != nullptr, "error_report is nullptr");
+  void* result;
+  struct stat64x libstat;
+
+  if (false == search_file_in_LIBPATH(filename, &libstat)) {
+    // file with filename does not exist
+  #ifdef ASSERT
+    result = ::dlopen(filename, Flags);
+    assert(result == nullptr, "dll_load: Could not stat() file %s, but dlopen() worked; Have to improve stat()", filename);
+  #endif
+    *error_report = "Could not load module .\nSystem error: No such file or directory";
+    return nullptr;
+  }
+  else {
+    unsigned i = 0;
+    TableLocker lock;
+    // check if library belonging to filename is already loaded.
+    // If yes use stored handle from previous ::dlopen() and increase refcount
+    for (i = 0; i < g_handletable_used; i++) {
+      if ((p_handletable + i)->handle &&
+          (p_handletable + i)->inode == libstat.st_ino &&
+          (p_handletable + i)->devid == libstat.st_dev) {
+        (p_handletable + i)->refcount++;
+        result = (p_handletable + i)->handle;
+        break;
+      }
+    }
+    if (i == g_handletable_used) {
+      // library not yet loaded. Check if there is space left in array
+      // to store new ::dlopen() handle
+      if (g_handletable_used == max_handletable) {
+        // No place in array anymore; increase array.
+        unsigned new_max = MAX2(max_handletable * 2, init_num_handles);
+        struct handletableentry* new_tab = (struct handletableentry*)::realloc(p_handletable, new_max * sizeof(struct handletableentry));
+        assert(new_tab != nullptr, "no more memory for handletable");
+        if (new_tab == nullptr) {
+          *error_report = "dlopen: no more memory for handletable";
+          return nullptr;
+        }
+        max_handletable = new_max;
+        p_handletable = new_tab;
+      }
+      // Library not yet loaded; load it, then store its handle in handle table
+      result = ::dlopen(filename, Flags);
+      if (result != nullptr) {
+        g_handletable_used++;
+        (p_handletable + i)->handle = result;
+        (p_handletable + i)->inode = libstat.st_ino;
+        (p_handletable + i)->devid = libstat.st_dev;
+        (p_handletable + i)->refcount = 1;
+      }
+      else {
+        // error analysis when dlopen fails
+        *error_report = ::dlerror();
+        if (*error_report == nullptr) {
+          *error_report = "dlerror returned no error description";
+        }
+      }
+    }
+  }
+  return result;
+}
+
+bool os::pd_dll_unload(void* libhandle, char* ebuf, int ebuflen) {
+  unsigned i = 0;
+  bool res = false;
+
+  if (ebuf && ebuflen > 0) {
+    ebuf[0] = '\0';
+    ebuf[ebuflen - 1] = '\0';
+  }
+
+  {
+    TableLocker lock;
+    // try to find handle in array, which means library was loaded by os::dll_load() call
+    for (i = 0; i < g_handletable_used; i++) {
+      if ((p_handletable + i)->handle == libhandle) {
+        // handle found, decrease refcount
+        assert((p_handletable + i)->refcount > 0, "Sanity");
+        (p_handletable + i)->refcount--;
+        if ((p_handletable + i)->refcount > 0) {
+          // if refcount is still >0 then we have to keep library and just return true
+          return true;
+        }
+        // refcount == 0, so we have to ::dlclose() the lib
+        // and delete the entry from the array.
+        break;
+      }
+    }
+
+    // If we reach this point either the libhandle was found with refcount == 0, or the libhandle
+    // was not found in the array at all. In both cases we have to ::dlclose the lib and perform
+    // the error handling. In the first case we then also have to delete the entry from the array
+    // while in the second case we simply have to nag.
+    res = (0 == ::dlclose(libhandle));
+    if (!res) {
+      // error analysis when dlopen fails
+      const char* error_report = ::dlerror();
+      if (error_report == nullptr) {
+        error_report = "dlerror returned no error description";
+      }
+      if (ebuf != nullptr && ebuflen > 0) {
+        snprintf(ebuf, ebuflen - 1, "%s", error_report);
+      }
+      assert(false, "os::pd_dll_unload() ::dlclose() failed");
+    }
+
+    if (i < g_handletable_used) {
+      if (res) {
+        // First case: libhandle was found (with refcount == 0) and ::dlclose successful,
+        // so delete entry from array
+        g_handletable_used--;
+        // If the entry was the last one of the array, the previous g_handletable_used--
+        // is sufficient to remove the entry from the array, otherwise we move the last
+        // entry of the array to the place of the entry we want to remove and overwrite it
+        if (i < g_handletable_used) {
+          *(p_handletable + i) = *(p_handletable + g_handletable_used);
+          (p_handletable + g_handletable_used)->handle = nullptr;
+        }
+      }
+    }
+    else {
+      // Second case: libhandle was not found (library was not loaded by os::dll_load())
+      // therefore nag
+      assert(false, "os::pd_dll_unload() library was not loaded by os::dll_load()");
+    }
+  }
+
+  // Update the dll cache
+  LoadedLibraries::reload();
+
+  return res;
+} // end: os::pd_dll_unload()
+
diff --git a/src/hotspot/os/aix/porting_aix.hpp b/src/hotspot/os/aix/porting_aix.hpp
index 2c4c0e002a8..109eceee3fc 100644
--- a/src/hotspot/os/aix/porting_aix.hpp
+++ b/src/hotspot/os/aix/porting_aix.hpp
@@ -115,4 +115,6 @@ class AixMisc {
 
 };
 
+void* Aix_dlopen(const char* filename, int Flags, const char** error_report);
+
 #endif // OS_AIX_PORTING_AIX_HPP
diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
index ec8fd440bf3..4a2922cb728 100644
--- a/src/hotspot/os/bsd/os_bsd.cpp
+++ b/src/hotspot/os/bsd/os_bsd.cpp
@@ -2488,3 +2488,25 @@ void os::jfr_report_memory_info() {
 }
 
 #endif // INCLUDE_JFR
+
+bool os::pd_dll_unload(void* libhandle, char* ebuf, int ebuflen) {
+
+  if (ebuf && ebuflen > 0) {
+    ebuf[0] = '\0';
+    ebuf[ebuflen - 1] = '\0';
+  }
+
+  bool res = (0 == ::dlclose(libhandle));
+  if (!res) {
+    // error analysis when dlopen fails
+    const char* error_report = ::dlerror();
+    if (error_report == nullptr) {
+      error_report = "dlerror returned no error description";
+    }
+    if (ebuf != nullptr && ebuflen > 0) {
+      snprintf(ebuf, ebuflen - 1, "%s", error_report);
+    }
+  }
+
+  return res;
+} // end: os::pd_dll_unload()
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index bde690d70de..aa8be1d897d 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -5570,3 +5570,25 @@ bool os::trim_native_heap(os::size_change_t* rss_change) {
   return false; // musl
 #endif
 }
+
+bool os::pd_dll_unload(void* libhandle, char* ebuf, int ebuflen) {
+
+  if (ebuf && ebuflen > 0) {
+    ebuf[0] = '\0';
+    ebuf[ebuflen - 1] = '\0';
+  }
+
+  bool res = (0 == ::dlclose(libhandle));
+  if (!res) {
+    // error analysis when dlopen fails
+    const char* error_report = ::dlerror();
+    if (error_report == nullptr) {
+      error_report = "dlerror returned no error description";
+    }
+    if (ebuf != nullptr && ebuflen > 0) {
+      snprintf(ebuf, ebuflen - 1, "%s", error_report);
+    }
+  }
+
+  return res;
+} // end: os::pd_dll_unload()
diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp
index 41d292f5110..56f6d32bd4d 100644
--- a/src/hotspot/os/posix/os_posix.cpp
+++ b/src/hotspot/os/posix/os_posix.cpp
@@ -50,6 +50,7 @@
 #include "utilities/vmError.hpp"
 #ifdef AIX
 #include "loadlib_aix.hpp"
+#include "os_aix.hpp"
 #endif
 #ifdef LINUX
 #include "os_linux.hpp"
@@ -734,25 +735,20 @@ void os::dll_unload(void *lib) {
   if (l_path == nullptr) {
     l_path = "<not available>";
   }
-  int res = ::dlclose(lib);
 
-  if (res == 0) {
+  char ebuf[1024];
+  bool res = os::pd_dll_unload(lib, ebuf, sizeof(ebuf));
+
+  if (res) {
     Events::log_dll_message(nullptr, "Unloaded shared library \"%s\" [" INTPTR_FORMAT "]",
                             l_path, p2i(lib));
     log_info(os)("Unloaded shared library \"%s\" [" INTPTR_FORMAT "]", l_path, p2i(lib));
   } else {
-    const char* error_report = ::dlerror();
-    if (error_report == nullptr) {
-      error_report = "dlerror returned no error description";
-    }
-
     Events::log_dll_message(nullptr, "Attempt to unload shared library \"%s\" [" INTPTR_FORMAT "] failed, %s",
-                            l_path, p2i(lib), error_report);
+                            l_path, p2i(lib), ebuf);
     log_info(os)("Attempt to unload shared library \"%s\" [" INTPTR_FORMAT "] failed, %s",
-                  l_path, p2i(lib), error_report);
+                  l_path, p2i(lib), ebuf);
   }
-  // Update the dll cache
-  AIX_ONLY(LoadedLibraries::reload());
   LINUX_ONLY(os::free(l_pathdup));
 }
 
diff --git a/src/hotspot/share/prims/jvmtiAgent.cpp b/src/hotspot/share/prims/jvmtiAgent.cpp
index 86c5a32a1df..f70935982b5 100644
--- a/src/hotspot/share/prims/jvmtiAgent.cpp
+++ b/src/hotspot/share/prims/jvmtiAgent.cpp
@@ -74,10 +74,6 @@ JvmtiAgent::JvmtiAgent(const char* name, const char* options, bool is_absolute_p
   _options(copy_string(options)),
   _os_lib(nullptr),
   _os_lib_path(nullptr),
-#ifdef AIX
-  _inode(0),
-  _device(0),
-#endif
   _jplis(nullptr),
   _loaded(false),
   _absolute_path(is_absolute_path),
@@ -122,24 +118,6 @@ const char* JvmtiAgent::os_lib_path() const {
   return _os_lib_path;
 }
 
-#ifdef AIX
-void JvmtiAgent::set_inode(ino64_t inode) {
-  _inode = inode;
-}
-
-void JvmtiAgent::set_device(dev64_t device) {
-  _device = device;
-}
-
-ino64_t JvmtiAgent::inode() const {
-  return _inode;
-}
-
-dev64_t JvmtiAgent::device() const {
-  return _device;
-}
-#endif
-
 bool JvmtiAgent::is_loaded() const {
   return _loaded;
 }
@@ -294,20 +272,6 @@ static bool load_agent_from_executable(JvmtiAgent* agent, const char* on_load_sy
   return os::find_builtin_agent(agent, &on_load_symbols[0], num_symbol_entries);
 }
 
-#ifdef AIX
-// save the inode and device of the library's file as a signature. This signature can be used
-// in the same way as the library handle as a signature on other platforms.
-static void save_library_signature(JvmtiAgent* agent, const char* name) {
-  struct stat64x libstat;
-  if (0 == os::Aix::stat64x_via_LIBPATH(name, &libstat)) {
-    agent->set_inode(libstat.st_ino);
-    agent->set_device(libstat.st_dev);
-  } else {
-    assert(false, "stat64x failed");
-  }
-}
-#endif
-
 // Load the library from the absolute path of the agent, if available.
 static void* load_agent_from_absolute_path(JvmtiAgent* agent, bool vm_exit_on_error) {
   DEBUG_ONLY(assert_preload(agent);)
@@ -317,7 +281,6 @@ static void* load_agent_from_absolute_path(JvmtiAgent* agent, bool vm_exit_on_er
   if (library == nullptr && vm_exit_on_error) {
     vm_exit(agent, " in absolute path, with error: ", nullptr);
   }
-  AIX_ONLY(if (library != nullptr) save_library_signature(agent, agent->name());)
   return library;
 }
 
@@ -330,13 +293,11 @@ static void* load_agent_from_relative_path(JvmtiAgent* agent, bool vm_exit_on_er
   // Try to load the agent from the standard dll directory
   if (os::dll_locate_lib(&buffer[0], sizeof buffer, Arguments::get_dll_dir(), name)) {
     library = os::dll_load(&buffer[0], &ebuf[0], sizeof ebuf);
-    AIX_ONLY(if (library != nullptr) save_library_signature(agent, &buffer[0]);)
   }
   if (library == nullptr && os::dll_build_name(&buffer[0], sizeof buffer, name)) {
     // Try the library path directory.
     library = os::dll_load(&buffer[0], &ebuf[0], sizeof ebuf);
     if (library != nullptr) {
-      AIX_ONLY(save_library_signature(agent, &buffer[0]);)
       return library;
     }
     if (vm_exit_on_error) {
@@ -554,11 +515,7 @@ static bool invoke_Agent_OnAttach(JvmtiAgent* agent, outputStream* st) {
     agent->set_os_lib_path(&buffer[0]);
     agent->set_os_lib(library);
     agent->set_loaded();
-  #ifdef AIX
-    previously_loaded = JvmtiAgentList::is_dynamic_lib_loaded(agent->device(), agent->inode());
-  #else
     previously_loaded = JvmtiAgentList::is_dynamic_lib_loaded(library);
-  #endif
   }
 
   // Print warning if agent was not previously loaded and EnableDynamicAgentLoading not enabled on the command line.
diff --git a/src/hotspot/share/prims/jvmtiAgent.hpp b/src/hotspot/share/prims/jvmtiAgent.hpp
index 95e910354e6..9baf6698868 100644
--- a/src/hotspot/share/prims/jvmtiAgent.hpp
+++ b/src/hotspot/share/prims/jvmtiAgent.hpp
@@ -43,10 +43,6 @@ class JvmtiAgent : public CHeapObj<mtServiceability> {
   const char* _options;
   void* _os_lib;
   const char* _os_lib_path;
-#ifdef AIX
-  ino64_t _inode;
-  dev64_t _device;
-#endif
   const void* _jplis;
   bool _loaded;
   bool _absolute_path;
@@ -84,12 +80,6 @@ class JvmtiAgent : public CHeapObj<mtServiceability> {
   void initialization_end();
   const Ticks& initialization_time() const;
   const Tickspan& initialization_duration() const;
-#ifdef AIX
-  void set_inode(ino64_t inode);
-  void set_device(dev64_t device);
-  unsigned long inode() const;
-  unsigned long device() const;
-#endif
 
   bool load(outputStream* st = nullptr);
   void unload();
diff --git a/src/hotspot/share/prims/jvmtiAgentList.cpp b/src/hotspot/share/prims/jvmtiAgentList.cpp
index b7312b9b75e..a32eeb7076c 100644
--- a/src/hotspot/share/prims/jvmtiAgentList.cpp
+++ b/src/hotspot/share/prims/jvmtiAgentList.cpp
@@ -243,19 +243,6 @@ bool JvmtiAgentList::is_dynamic_lib_loaded(void* os_lib) {
   }
   return false;
 }
-#ifdef AIX
-bool JvmtiAgentList::is_dynamic_lib_loaded(dev64_t device, ino64_t inode) {
-  JvmtiAgentList::Iterator it = JvmtiAgentList::agents();
-  while (it.has_next()) {
-    JvmtiAgent* const agent = it.next();
-    if (!agent->is_static_lib() && device != 0 && inode != 0 &&
-        agent->device() == device && agent->inode() == inode) {
-      return true;
-    }
-  }
-  return false;
-}
-#endif
 
 static bool match(JvmtiEnv* env, const JvmtiAgent* agent, const void* os_module_address) {
   assert(env != nullptr, "invariant");
diff --git a/src/hotspot/share/prims/jvmtiAgentList.hpp b/src/hotspot/share/prims/jvmtiAgentList.hpp
index 95dad006ec3..cf698c69c01 100644
--- a/src/hotspot/share/prims/jvmtiAgentList.hpp
+++ b/src/hotspot/share/prims/jvmtiAgentList.hpp
@@ -78,9 +78,6 @@ class JvmtiAgentList : AllStatic {
 
   static bool is_static_lib_loaded(const char* name);
   static bool is_dynamic_lib_loaded(void* os_lib);
-#ifdef AIX
-  static bool is_dynamic_lib_loaded(dev64_t device, ino64_t inode);
-#endif
 
   static JvmtiAgent* lookup(JvmtiEnv* env, void* f_ptr);
 
diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp
index 9dbf895965a..f445669ab23 100644
--- a/src/hotspot/share/runtime/os.hpp
+++ b/src/hotspot/share/runtime/os.hpp
@@ -1055,6 +1055,7 @@ class os: AllStatic {
                                 char pathSep);
   static bool set_boot_path(char fileSep, char pathSep);
 
+  static bool pd_dll_unload(void* libhandle, char* ebuf, int ebuflen);
 };
 
 // Note that "PAUSE" is almost always used with synchronization