diff --git a/src/env.cpp b/src/env.cpp
index fb1867c93..b661e4a72 100644
--- a/src/env.cpp
+++ b/src/env.cpp
@@ -1408,13 +1408,13 @@ maybe_t<env_var_t> env_stack_t::get(const wcstring &key, env_mode_flags_t mode)
             if (history) history->get_history(result);
             return env_var_t(L"history", result);
         } else if (key == L"pipestatus") {
-            const auto& js = proc_get_last_job_statuses();
+            const auto js = proc_get_last_job_statuses();
             wcstring_list_t result;
-            result.reserve(js->size());
-            for (auto&& i : *js) {
-                result.emplace_back(to_string(i));
+            result.reserve(js.size());
+            for (int i : js) {
+                result.push_back(to_string(i));
             }
-            return env_var_t(L"pipestatus", result);
+            return env_var_t(L"pipestatus", std::move(result));
         } else if (key == L"status") {
             return env_var_t(L"status", to_string(proc_get_last_status()));
         } else if (key == L"umask") {
diff --git a/src/proc.cpp b/src/proc.cpp
index 78addc711..d7ce320ec 100644
--- a/src/proc.cpp
+++ b/src/proc.cpp
@@ -57,7 +57,7 @@
 static int last_status = 0;
 
 /// Statuses of last job's processes to exit.
-static std::shared_ptr<std::vector<int>> last_job_statuses{new std::vector<int>{0}};
+static owning_lock<std::vector<int>> last_job_statuses;
 
 /// The signals that signify crashes to us.
 static const int crashsignals[] = {SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGSYS};
@@ -150,17 +150,16 @@ int proc_get_last_status() { return last_status; }
 
 void proc_set_last_job_statuses(const job_t &last_job) {
     ASSERT_IS_MAIN_THREAD();
-    std::shared_ptr<std::vector<int>> ljs{new std::vector<int>};
-    ljs->reserve(last_job.processes.size());
-    for (auto &&p : last_job.processes) {
-        ljs->emplace_back(p->pid ? proc_format_status(p->status) : p->status);
+    auto lockedstats = last_job_statuses.acquire();
+    lockedstats->clear();
+    lockedstats->reserve(last_job.processes.size());
+    for (const auto &p : last_job.processes) {
+        lockedstats->emplace_back(p->pid ? proc_format_status(p->status) : p->status);
     }
-    last_job_statuses = std::move(ljs);
 }
 
-std::shared_ptr<std::vector<int>> proc_get_last_job_statuses() {
-    ASSERT_IS_MAIN_THREAD();
-    return last_job_statuses;
+std::vector<int> proc_get_last_job_statuses() {
+    return *last_job_statuses.acquire();
 }
 
 // Basic thread safe job IDs. The vector consumed_job_ids has a true value wherever the job ID
diff --git a/src/proc.h b/src/proc.h
index d743e9ce0..6a427284e 100644
--- a/src/proc.h
+++ b/src/proc.h
@@ -352,8 +352,8 @@ int proc_get_last_status();
 /// Sets the status of the last job's processes to exit from last_job.
 void proc_set_last_job_statuses(const job_t &last_job);
 
-/// Returns the status of the last job's processes to exit.
-std::shared_ptr<std::vector<int>> proc_get_last_job_statuses();
+/// Returns the statuses of the last job's processes to exit.
+std::vector<int> proc_get_last_job_statuses();
 
 /// Notify the user about stopped or terminated jobs. Delete terminated jobs from the job list.
 ///