diff --git a/gdb/inferior.c b/gdb/inferior.c
index 880f25df0e..c88a23c241 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -784,7 +784,7 @@ add_inferior_with_spaces (void)
      doesn't really return a new address space; otherwise, it
      really does.  */
   aspace = maybe_new_address_space ();
-  pspace = add_program_space (aspace);
+  pspace = new program_space (aspace);
   inf = add_inferior (0);
   inf->pspace = pspace;
   inf->aspace = pspace->aspace;
@@ -928,7 +928,7 @@ clone_inferior_command (const char *args, int from_tty)
 	 doesn't really return a new address space; otherwise, it
 	 really does.  */
       aspace = maybe_new_address_space ();
-      pspace = add_program_space (aspace);
+      pspace = new program_space (aspace);
       inf = add_inferior (0);
       inf->pspace = pspace;
       inf->aspace = pspace->aspace;
diff --git a/gdb/infrun.c b/gdb/infrun.c
index c663908568..5aeafef08b 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -511,7 +511,7 @@ holding the child stopped.  Try \"set detach-on-fork\" or \
 	  else
 	    {
 	      child_inf->aspace = new_address_space ();
-	      child_inf->pspace = add_program_space (child_inf->aspace);
+	      child_inf->pspace = new program_space (child_inf->aspace);
 	      child_inf->removable = 1;
 	      set_current_program_space (child_inf->pspace);
 	      clone_program_space (child_inf->pspace, parent_inf->pspace);
@@ -630,7 +630,7 @@ holding the child stopped.  Try \"set detach-on-fork\" or \
       else
 	{
 	  child_inf->aspace = new_address_space ();
-	  child_inf->pspace = add_program_space (child_inf->aspace);
+	  child_inf->pspace = new program_space (child_inf->aspace);
 	  child_inf->removable = 1;
 	  child_inf->symfile_flags = SYMFILE_NO_READ;
 	  set_current_program_space (child_inf->pspace);
@@ -986,7 +986,7 @@ handle_vfork_child_exec_or_exit (int exec)
 	{
 	  /* We're staying attached to the parent, so, really give the
 	     child a new address space.  */
-	  inf->pspace = add_program_space (maybe_new_address_space ());
+	  inf->pspace = new program_space (maybe_new_address_space ());
 	  inf->aspace = inf->pspace->aspace;
 	  inf->removable = 1;
 	  set_current_program_space (inf->pspace);
@@ -1020,7 +1020,7 @@ handle_vfork_child_exec_or_exit (int exec)
 	     program space resets breakpoints).  */
 	  inf->aspace = NULL;
 	  inf->pspace = NULL;
-	  pspace = add_program_space (maybe_new_address_space ());
+	  pspace = new program_space (maybe_new_address_space ());
 	  set_current_program_space (pspace);
 	  inf->removable = 1;
 	  inf->symfile_flags = SYMFILE_NO_READ;
diff --git a/gdb/progspace.c b/gdb/progspace.c
index 08a8a7a88e..f6da7e7da2 100644
--- a/gdb/progspace.c
+++ b/gdb/progspace.c
@@ -109,30 +109,21 @@ init_address_spaces (void)
 /* Adds a new empty program space to the program space list, and binds
    it to ASPACE.  Returns the pointer to the new object.  */
 
-struct program_space *
-add_program_space (struct address_space *aspace)
+program_space::program_space (address_space *aspace_)
+: num (++last_program_space_num), aspace (aspace_)
 {
-  struct program_space *pspace;
-
-  pspace = XCNEW (struct program_space);
-
-  pspace->num = ++last_program_space_num;
-  pspace->aspace = aspace;
-
-  program_space_alloc_data (pspace);
+  program_space_alloc_data (this);
 
   if (program_spaces == NULL)
-    program_spaces = pspace;
+    program_spaces = this;
   else
     {
       struct program_space *last;
 
       for (last = program_spaces; last->next != NULL; last = last->next)
 	;
-      last->next = pspace;
+      last->next = this;
     }
-
-  return pspace;
 }
 
 /* Releases program space PSPACE, and all its contents (shared
@@ -141,26 +132,24 @@ add_program_space (struct address_space *aspace)
    is the current program space, since there should always be a
    program space.  */
 
-static void
-release_program_space (struct program_space *pspace)
+program_space::~program_space ()
 {
-  gdb_assert (pspace != current_program_space);
+  gdb_assert (this != current_program_space);
 
   scoped_restore_current_program_space restore_pspace;
 
-  set_current_program_space (pspace);
+  set_current_program_space (this);
 
-  breakpoint_program_space_exit (pspace);
+  breakpoint_program_space_exit (this);
   no_shared_libraries (NULL, 0);
   exec_close ();
   free_all_objfiles ();
   if (!gdbarch_has_shared_address_space (target_gdbarch ()))
-    free_address_space (pspace->aspace);
-  clear_section_table (&pspace->target_sections);
-  clear_program_space_solib_cache (pspace);
+    free_address_space (this->aspace);
+  clear_section_table (&this->target_sections);
+  clear_program_space_solib_cache (this);
     /* Discard any data modules have associated with the PSPACE.  */
-  program_space_free_data (pspace);
-  xfree (pspace);
+  program_space_free_data (this);
 }
 
 /* Copies program space SRC to DEST.  Copies the main executable file,
@@ -235,7 +224,7 @@ delete_program_space (struct program_space *pspace)
       ss = *ss_link;
     }
 
-  release_program_space (pspace);
+  delete pspace;
 }
 
 /* Prints the list of program spaces and their details on UIOUT.  If
@@ -433,5 +422,5 @@ initialize_progspace (void)
      modules have done that.  Do this before
      initialize_current_architecture, because that accesses exec_bfd,
      which in turn dereferences current_program_space.  */
-  current_program_space = add_program_space (new_address_space ());
+  current_program_space = new program_space (new_address_space ());
 }
diff --git a/gdb/progspace.h b/gdb/progspace.h
index 73929c9e96..c64209c5df 100644
--- a/gdb/progspace.h
+++ b/gdb/progspace.h
@@ -135,80 +135,83 @@ DEF_VEC_P (so_list_ptr);
 /* The program space structure.  */
 
 struct program_space
-  {
-    /* Pointer to next in linked list.  */
-    struct program_space *next;
-
-    /* Unique ID number.  */
-    int num;
-
-    /* The main executable loaded into this program space.  This is
-       managed by the exec target.  */
-
-    /* The BFD handle for the main executable.  */
-    bfd *ebfd;
-    /* The last-modified time, from when the exec was brought in.  */
-    long ebfd_mtime;
-    /* Similar to bfd_get_filename (exec_bfd) but in original form given
-       by user, without symbolic links and pathname resolved.
-       It needs to be freed by xfree.  It is not NULL iff EBFD is not NULL.  */
-    char *pspace_exec_filename;
-
-    /* The address space attached to this program space.  More than one
-       program space may be bound to the same address space.  In the
-       traditional unix-like debugging scenario, this will usually
-       match the address space bound to the inferior, and is mostly
-       used by the breakpoints module for address matches.  If the
-       target shares a program space for all inferiors and breakpoints
-       are global, then this field is ignored (we don't currently
-       support inferiors sharing a program space if the target doesn't
-       make breakpoints global).  */
-    struct address_space *aspace;
-
-    /* True if this program space's section offsets don't yet represent
-       the final offsets of the "live" address space (that is, the
-       section addresses still require the relocation offsets to be
-       applied, and hence we can't trust the section addresses for
-       anything that pokes at live memory).  E.g., for qOffsets
-       targets, or for PIE executables, until we connect and ask the
-       target for the final relocation offsets, the symbols we've used
-       to set breakpoints point at the wrong addresses.  */
-    int executing_startup;
-
-    /* True if no breakpoints should be inserted in this program
-       space.  */
-    int breakpoints_not_allowed;
-
-    /* The object file that the main symbol table was loaded from
-       (e.g. the argument to the "symbol-file" or "file" command).  */
-    struct objfile *symfile_object_file;
-
-    /* All known objfiles are kept in a linked list.  This points to
-       the head of this list.  */
-    struct objfile *objfiles;
-
-    /* The set of target sections matching the sections mapped into
-       this program space.  Managed by both exec_ops and solib.c.  */
-    struct target_section_table target_sections;
-
-    /* List of shared objects mapped into this space.  Managed by
-       solib.c.  */
-    struct so_list *so_list;
-
-    /* Number of calls to solib_add.  */
-    unsigned solib_add_generation;
-
-    /* When an solib is added, it is also added to this vector.  This
-       is so we can properly report solib changes to the user.  */
-    VEC (so_list_ptr) *added_solibs;
-
-    /* When an solib is removed, its name is added to this vector.
-       This is so we can properly report solib changes to the user.  */
-    VEC (char_ptr) *deleted_solibs;
-
-    /* Per pspace data-pointers required by other GDB modules.  */
-    REGISTRY_FIELDS;
-  };
+{
+  program_space (address_space *aspace_);
+  ~program_space ();
+
+  /* Pointer to next in linked list.  */
+  struct program_space *next = NULL;
+
+  /* Unique ID number.  */
+  int num = 0;
+
+  /* The main executable loaded into this program space.  This is
+     managed by the exec target.  */
+
+  /* The BFD handle for the main executable.  */
+  bfd *ebfd = NULL;
+  /* The last-modified time, from when the exec was brought in.  */
+  long ebfd_mtime = 0;
+  /* Similar to bfd_get_filename (exec_bfd) but in original form given
+     by user, without symbolic links and pathname resolved.
+     It needs to be freed by xfree.  It is not NULL iff EBFD is not NULL.  */
+  char *pspace_exec_filename = NULL;
+
+  /* The address space attached to this program space.  More than one
+     program space may be bound to the same address space.  In the
+     traditional unix-like debugging scenario, this will usually
+     match the address space bound to the inferior, and is mostly
+     used by the breakpoints module for address matches.  If the
+     target shares a program space for all inferiors and breakpoints
+     are global, then this field is ignored (we don't currently
+     support inferiors sharing a program space if the target doesn't
+     make breakpoints global).  */
+  struct address_space *aspace = NULL;
+
+  /* True if this program space's section offsets don't yet represent
+     the final offsets of the "live" address space (that is, the
+     section addresses still require the relocation offsets to be
+     applied, and hence we can't trust the section addresses for
+     anything that pokes at live memory).  E.g., for qOffsets
+     targets, or for PIE executables, until we connect and ask the
+     target for the final relocation offsets, the symbols we've used
+     to set breakpoints point at the wrong addresses.  */
+  int executing_startup = 0;
+
+  /* True if no breakpoints should be inserted in this program
+     space.  */
+  int breakpoints_not_allowed = 0;
+
+  /* The object file that the main symbol table was loaded from
+     (e.g. the argument to the "symbol-file" or "file" command).  */
+  struct objfile *symfile_object_file = NULL;
+
+  /* All known objfiles are kept in a linked list.  This points to
+     the head of this list.  */
+  struct objfile *objfiles = NULL;
+
+  /* The set of target sections matching the sections mapped into
+     this program space.  Managed by both exec_ops and solib.c.  */
+  struct target_section_table target_sections {};
+
+  /* List of shared objects mapped into this space.  Managed by
+     solib.c.  */
+  struct so_list *so_list = NULL;
+
+  /* Number of calls to solib_add.  */
+  unsigned int solib_add_generation = 0;
+
+  /* When an solib is added, it is also added to this vector.  This
+     is so we can properly report solib changes to the user.  */
+  VEC (so_list_ptr) *added_solibs = NULL;
+
+  /* When an solib is removed, its name is added to this vector.
+     This is so we can properly report solib changes to the user.  */
+  VEC (char_ptr) *deleted_solibs = NULL;
+
+  /* Per pspace data-pointers required by other GDB modules.  */
+  REGISTRY_FIELDS {};
+};
 
 /* An address space.  It is used for comparing if
    pspaces/inferior/threads see the same address space and for
@@ -243,10 +246,6 @@ extern struct program_space *current_program_space;
 #define ALL_PSPACES(pspace) \
   for ((pspace) = program_spaces; (pspace) != NULL; (pspace) = (pspace)->next)
 
-/* Add a new empty program space, and assign ASPACE to it.  Returns the
-   pointer to the new object.  */
-extern struct program_space *add_program_space (struct address_space *aspace);
-
 /* Remove a program space from the program spaces list and release it.  It is
    an error to call this function while PSPACE is the current program space. */
 extern void delete_program_space (struct program_space *pspace);
