pthread_create() and memory leaks
Prior to your edit of adding pthread_exit(0)
to the end of main()
, your program would finish executing before all the threads had finished running. valgrind
thus reported the resources that were still being held by the threads that were still active at the time the program terminated, making it look like your program had a memory leak.
The call to pthread_exit(0)
in main()
makes the main thread wait for all the other spawned threads to exit before it itself exits. This lets valgrind
observe a clean run in terms of memory utilization.
(I am assuming linux is your operating system below, but it seems you are running some variety of UNIX from your comments.)
The extra virtual memory you see is just linux assigning some pages to your program since it was a big memory user. As long as your resident memory utilization is low and constant when you reach the idle state, and the virtual utilization is relatively constant, you can assume your system is well behaved.
By default, each thread gets 2MB of stack space on linux. If each thread stack does not need that much space, you can adjust it by initializing a pthread_attr_t
and setting it with a smaller stack size using pthread_attr_setstacksize()
. What stack size is appropriate depends on how deep your function call stack grows and how much space the local variables for those functions take.
#define SMALLEST_STACKSZ PTHREAD_STACK_MIN
#define SMALL_STACK (24*1024)
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, SMALL_STACK);
/* ... */
pthread_create(&my_thread, &attr, run_thread, (void *)thread_count);
/* ... */
pthread_attr_destroy(&attr);
c pthreads + valgrind = memory leak : why?
You have a bug in when your threads are detached, causing undefined behavior.
In main you have this line of code:
struct args_for_job_t args[MAX_THREADS];
Which you hand of pointers to your worker threads.
Then main() reaches this part
pthread_exit(NULL);
And main() ceases to exist, but you still may have worker threads around, that accesses the above args
array that's on the stack of main() - which doesn't exist anymore.
Your worker threads might all finish before main() ends in some runs, but not in other runs.
why pthread causes a memory leak
A thread is an allocated resource and you did not free it before exiting. You should call pthread_join
; this would also eliminate the need for your hackish and incorrect sleep loop.
It's possible that even once you fix this, valgrind will still see a "leak", since some implementations of POSIX threads (I'm guessing you're using glibc/NPTL) cache and reuse thread resources rather than freeing them fully. I'm not sure if valgrind works around this or not.
Memory leaks in pthread even if the state is detached
Your expectation is correct that there shouldn't be any leaks in main thread once you call pthread_exit
.
However, what you observe is a quirk of the implementation you're using (which is likely to be glibc) - pthreads library (glibc implementation) re-uses the initially allocated stack for threads - like a cache so that previously allocated stacks can be re-used whenever possible.
Valgrind simply reports what it "sees" (something was allocated but not de-allocated). But it's not a real leak, so you don't need to worry about this.
If you "reverse" the logic (main thread exits as the last thread) then you wouldn't see leaks because the initially allocated stack space is properly free'd by the main thread. But this leak isn't a real leak in any case and you can safely ignore this.
You can also setup a suppression file so that Valgrind doesn't complain about this (which is to inform Valgrind that "I know this isn't not real leak, so don't report this"), such as:
{
Pthread_Stack_Leaks_Ignore
Memcheck:Leak
fun:calloc
fun:allocate_dtv
fun:_dl_allocate_tls
fun:allocate_stack
fun:pthread_create*
}
using pthread with memory leak
Your leak is because the memory you allocate has no corresponding free action.
The code you're using appears to be trying to convey a dynamic allocation back to the caller. Proper use of pthread_join
and its second parameter can recoup that memory pointer, which can then be properly freed.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
void* runMe(void *pv)
{
int *arg = pv;
printf("Hello %d from %x\n", *arg, (unsigned int)pthread_self());
int *ret = malloc( sizeof *ret );
*ret = *arg + 4; // note this value '4' will be different in the quiz!
return ret;
}
int run_threads(int n) {
pthread_t thr[n];
int thr_args[n];
int total = 0;
for (int i=0; i<n; i++) {
thr_args[i] = i;
pthread_create(thr+i, NULL, runMe, thr_args+i);
}
for (int j=0; j<n; j++)
{
// reap pointer from resulting thread.
void *res = NULL;
pthread_join(thr[j], &res);
int *ires = res;
printf("ires = %p; *ires = %d\n", ires, *ires);
free(ires);
total += thr_args[j];
}
return total;
}
int main()
{
run_threads(10);
}
Output (varies)
Hello 9 from b0a71000
Hello 0 from b05df000
Hello 7 from b096d000
Hello 2 from b06e3000
Hello 1 from b0661000
Hello 4 from b07e7000
Hello 3 from b0765000
Hello 5 from b0869000
Hello 6 from b08eb000
Hello 8 from b09ef000
ires = 0x600000; *ires = 4
ires = 0x2009e0; *ires = 5
ires = 0x600010; *ires = 6
ires = 0x500010; *ires = 7
ires = 0x2009f0; *ires = 8
ires = 0x2012c0; *ires = 9
ires = 0x600020; *ires = 10
ires = 0x400040; *ires = 11
ires = 0x400050; *ires = 12
ires = 0x500000; *ires = 13
I also took liberty to fix your incorrect, non-compliant function signature you were using. pthread_create
requires the form:
void *proc(void *)
for the thread procedure. Anything not of that form isn't compliant, and should be avoided.
Related Topics
C++ Undefined References with Static Library
When Should I Write the Keyword 'Static' Before a Non-Member Function
Need Iterator When Using Ranged-Based for Loops
How to Find the Intersection of Two Stl Sets
Use Std::Fill to Populate Vector with Increasing Numbers
Recursively Iterate Over All the Files in a Directory and Its Subdirectories in Qt
How to Use the Mingw Gdb Debugger to Debug a C++ Program in Windows
Obtain a Std::Ostream Either from Std::Cout or Std::Ofstream(File)
How to Correctly Use Cv::Triangulatepoints()
Questions About Hinnant's Stack Allocator
Do I Need to Close a Std::Fstream
How to Prevent Stack Allocation of an Object and Only Allow It to Be Instantiated with 'New'
Delayed Start of a Thread in C++ 11
Do I Need to Protect Read Access to an Stl Container in a Multithreading Environment