I recently switched from ruby 1.8.7 (p371) to ruby 1.9.3 (p429), and observed high memory leak(?). My long-running scripts eventually all get killed due to used up memory. Tried to use some profiler (rubyprof, memprof, bleakhouse,etc) to track down but seems they don't have good support for 1.9.3.
Just used valgrind to track the memory usage from C level and observed high leakage, I run this sample codes:
A = ['na', 'ns', 'n/a.jpg']
b = 'N/A.jpg'
if A.include?(b.downcase)
puts 'yes'
end
Very simple. In ruby 1.8.7, I tried valgrind --partial-loads-ok=yes --undef-value-errors=no --leak-check=full ruby test.rb, and it gave the result:
LEAK SUMMARY:
==28315== definitely lost: 0 bytes in 0 blocks
==28315== indirectly lost: 0 bytes in 0 blocks
==28315== possibly lost: 16,464 bytes in 431 blocks
==28315== still reachable: 15,103,747 bytes in 110,826 blocks
==28315== suppressed: 0 bytes in 0 blocks
While in 1.9.3, it gave the following result:
LEAK SUMMARY:
==28228== definitely lost: 2,028,264 bytes in 9,246 blocks
==28228== indirectly lost: 1,221,720 bytes in 23,812 blocks
==28228== possibly lost: 21,251 bytes in 432 blocks
==28228== still reachable: 754,268 bytes in 15,849 blocks
==28228== suppressed: 0 bytes in 0 blocks
and I observed leak in rb_longjmp, rb_is_eq_eval, rb_class_new_instance, rb_ary_each.... methods. Is that mean ruby 1.9.3 have a leakage? Any fix to that?
Related
In Go Should the "Sys" stat or any other stat/combination reported by runtime.ReadMemStats approximately equal the resident memory set reported by ps aux?
Alternatively, assuming some memory may be swapped out, should the Sys stat be approximately greater than or equal to the RSS?
We have a long-running web service that deals with a high frequency of requests and we are finding that the RSS quickly climbs up to consume virtually all of the 64GB memory on our servers. When it hits ~85% we begin to experience considerable degradation in our response times and in how many concurrent requests we can handle. The run I've listed below is after about 20 hours of execution, and is already at 51% memory usage.
I'm trying to determine if the likely cause is a memory leak (we make some calls to CGO). The data seems to indicate that it is, but before I go down that rabbit hole I want to rule out a fundamental misunderstanding of the statistics I'm using to make that call.
This is an amd64 build targeting linux and executing on CentOS.
Reported by runtime.ReadMemStats:
Alloc: 1294777080 bytes (1234.80MB) // bytes allocated and not yet freed
Sys: 3686471104 bytes (3515.69MB) // bytes obtained from system (sum of XxxSys below)
HeapAlloc: 1294777080 bytes (1234.80MB) // bytes allocated and not yet freed (same as Alloc above)
HeapSys: 3104931840 bytes (2961.09MB) // bytes obtained from system
HeapIdle: 1672339456 bytes (1594.87MB) // bytes in idle spans
HeapInuse: 1432592384 bytes (1366.23MB) // bytes in non-idle span
Reported by ps aux:
%CPU %MEM VSZ RSS
1362 51.3 306936436 33742120
Here is the code.
In the golang main function, which in main.go
func main() {
rgc.GetRgcService()
}
where the rgc is in another golang file, named mrgc.go. The code inside is
package rgc
func GetRgcService() (svc *RgcService, err error) {}
The function GetRgcService is a empty function.
However, when I used valgrind to test the memory, I got the following output
==58156== HEAP SUMMARY:
==58156== in use at exit: 1,152 bytes in 4 blocks
==58156== total heap usage: 9 allocs, 5 frees, 1,304 bytes allocated
==58156== 288 bytes in 1 blocks are possibly lost in loss record 4 of 4
==58156== at 0x4A27F63: calloc (vg_replace_malloc.c:593)
==58156== by 0x4010DE1: allocate_dtv (in /home/opt/gcc-4.8.2.bpkg-r2/gcc-4.8.2.bpkg-r2/lib64/ld-2.18.so)
==58156== by 0x40114ED: _dl_allocate_tls (in /home/opt/gcc-4.8.2.bpkg-r2/gcc-4.8.2.bpkg-r2/lib64/ld-2.18.so)
==58156== by 0x4B36DE2: pthread_create##GLIBC_2.2.5 (in /home/opt/gcc-4.8.2.bpkg-r2/gcc-4.8.2.bpkg-r2/lib64/libpthread-2.18.so)
==58156== by 0x4B2937: _cgo_sys_thread_start (gcc_linux_amd64.c:75)
==58156== by 0x45506C: runtime.asmcgocall (/home/map/.jumbo/lib/go/src/runtime/asm_amd64.s:612)
==58156== by 0x50619F: ??? (in /home/users/zhanghuaizhi/ttt.38)
==58156== by 0xC7FFFFFFFF: ???
==58156== by 0xC820067FFF: ???
==58156== by 0x42D69B: runtime.allocm (/home/map/.jumbo/lib/go/src/runtime/proc.go:1260)
==58156== by 0x42DD3A: runtime.newm (/home/map/.jumbo/lib/go/src/runtime/proc.go:1510)
==58156== by 0x42E071: runtime.startm (/home/map/.jumbo/lib/go/src/runtime/proc.go:1583)
==58156==
==58156== LEAK SUMMARY:
==58156== definitely lost: 0 bytes in 0 blocks
==58156== indirectly lost: 0 bytes in 0 blocks
==58156== possibly lost: 1,152 bytes in 4 blocks
==58156== still reachable: 0 bytes in 0 blocks
==58156== suppressed: 0 bytes in 0 blocks
How can I free these memory? Since I need to used this function to do a lot of process. It causes lots of memory leaks, which can not be freed
Nothing was leaked. The memory is still reachable and it's quite common to not free things on exit, it just takes unnecessary time and the OS will deal with it anyway.
This is memory allocated to thread local storage to a thread that's still running, so it would be incorrect to free it. A better question would be "how do I stop this thread?", to which an answer is: you don't, the Go runtime deals with it. It is quite common to not stop threads at exit, it just takes unnecessary time and the OS will deal with it anyway.
It has nothing to do with your code and your function call, it's something that Go runtime allocates for itself.
Go is a garbage collected language and using valgrind on it will not tell you much. It will neither detect real memory leaks nor will it understand which memory is still in use.
You can manually run garbage collection:
https://golang.org/pkg/runtime/#GC
I think that would free up the memory, but as others have said, that memory will get freed eventually when the runtime's scheduled garbage collector runs.
We are running a mixed mode process (managed + unmanaged) on Win 7 64 bit.
Our process is using up too much memory (especially VM). Based on our analysis, the majority of the memory is used by a big native heap. Our theory is that the LFH is saving too many free blocks in committed memory, for future allocations. They sum to about 1.2 GB while our actual allocated native memory is only at most 0.6 GB.
These numbers are from a test run of the process. In production it sometimes exceeded 10 GB of VM - with maybe 6 GB unaccounted for by known allocations.
We'd like to know if this theory of excessive committed-but-free-for-allocation segments is true, and how this waste can be reduced.
Here's the details of our analysis.
First we needed to figure out what's allocated and rule out memory leaks. We ran the excellent Heap Inspector by Jelle van der Beek and we ruled out a leak and established that the known allocations are at most 0.6 deci-GB.
We took a full memory dump and opened in WinDbg.
Ran !heap -stat
It reports a big native heap with 1.83 deci-GB committed memory. Much more than the sum of our allocations!
_HEAP 000000001b480000
Segments 00000078
Reserved bytes 0000000072980000
Committed bytes 000000006d597000
VirtAllocBlocks 0000001e
VirtAlloc bytes 0000000eb7a60118
Then we ran !heap -stat -h 0000001b480000
heap # 000000001b480000
group-by: TOTSIZE max-display: 20
size #blocks total ( %) (percent of total busy bytes)
c0000 12 - d80000 (10.54)
b0000 d - 8f0000 (6.98)
e0000 a - 8c0000 (6.83)
...
If we add up all the 20 reported items, they add up to 85 deci-MB - much less than the 1.79 deci-GB we're looking for.
We ran !heap -h 1b480000
...
Flags: 00001002
ForceFlags: 00000000
Granularity: 16 bytes
Segment Reserve: 72a70000
Segment Commit: 00002000
DeCommit Block Thres: 00000400
DeCommit Total Thres: 00001000
Total Free Size: 013b60f1
Max. Allocation Size: 000007fffffdefff
Lock Variable at: 000000001b480208
Next TagIndex: 0000
Maximum TagIndex: 0000
Tag Entries: 00000000
PsuedoTag Entries: 00000000
Virtual Alloc List: 1b480118
Unable to read nt!_HEAP_VIRTUAL_ALLOC_ENTRY structure at 000000002acf0000
Uncommitted ranges: 1b4800f8
FreeList[ 00 ] at 000000001b480158: 00000000be940080 . 0000000085828c60 (9451 blocks)
...
When adding up up all the segment sizes in the report, we get:
Total Size = 1.83 deci-GB
Segments Marked Busy Size = 1.50 deci-GB
Segments Marked Busy and Internal Size = 1.37 deci-GB
So all the committed bytes in this report do add up to the total commit size. We grouped on block size and the most heavy allocations come from blocks of size 0x3fff0. These don't correspond to allocations that we know of. There were also mystery blocks of other sizes.
We ran !heap -p -all. This reports the LFH internal segments but we don't understand it fully. Those 3fff0 sized blocks in the previous report appear in the LFH report with an asterisk mark and are sometimes Busy and sometimes Free. Then inside them we see many smaller free blocks.
We guess these free blocks are legitimate. They are committed VM that the LFH reserves for future allocations. But why is their total size so much greater than sum of memory allocations, and can this be reduced?
Well, I can sort of answer my own question.
We had been doing lots and lots of tiny allocations and deallocations in our program. There was no leak, but it seems this somehow created a fragmentation of some sort. After consolidating and eliminating most of these allocations our software is running much better and using less peak memory. It is still a mystery why the peak committed memory was so much higher than the peak actually-used memory.
I've been trying to be more meticulous lately about memory management in my code. Just for a laugh, I wrote a simple C source file containing only one function, and used valgrind to see if the C compiler itself had any leaks. To my great surprise, it did!
valgrind --leak-check=full --show-reachable=yes gcc -c example.c
...bunch of junk...
==4587== LEAK SUMMARY:
==4587== definitely lost: 4,207 bytes in 60 blocks
==4587== indirectly lost: 56 bytes in 5 blocks
==4587== possibly lost: 27 bytes in 2 blocks
==4587== still reachable: 29,048 bytes in 47 blocks
==4587== suppressed: 0 bytes in 0 blocks
Clang had leaks too, of only 68 bytes though, all of them reachable.
I thought that if your code has memory leaks, you get thrown in solitary confinement for every byte you lose. Have I misunderstood the implications of memory leaks? Are they actually sort of tolerable as long as it's not a long-running program? Is this actually just valgrind being wrong?
I have seen some of the post regarding valgrind but not single post helped me in understanding interpretation of valgrind output.
I ran two programs with valgrind (Both having memory leaks)
Sample Output for Test 1
==20422== LEAK SUMMARY:
==20422== definitely lost: 448 bytes in 3 blocks
==20422== indirectly lost: 786,460 bytes in 1 blocks
==20422== possibly lost: 1,576,052 bytes in 46 blocks
==20422== still reachable: 1,077,107 bytes in 2,333 blocks
==20422== suppressed: 0 bytes in 0 blocks
==20422== Rerun with --leak-check=full to see details of leaked memory
==20422==
==20422== For counts of detected and suppressed errors, rerun with: -v
==20422== ERROR SUMMARY: 98307 errors from 5 contexts (suppressed: 2 from 2)
Killed
Output for Test 2
==20875== HEAP SUMMARY:
==20875== in use at exit: 1,059,198 bytes in 2,047 blocks
==20875== total heap usage: 3,019 allocs, 972 frees, 4,496,090 bytes allocated
==20875==
==20875== LEAK SUMMARY:
==20875== definitely lost: 328 bytes in 2 blocks
==20875== indirectly lost: 0 bytes in 0 blocks
==20875== possibly lost: 1,600 bytes in 5 blocks
==20875== still reachable: 1,057,270 bytes in 2,040 blocks
==20875== suppressed: 0 bytes in 0 blocks
==20875== Rerun with --leak-check=full to see details of leaked memory
==20875==
==20875== For counts of detected and suppressed errors, rerun with: -v
==20875== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
If you look at the error summary, One output says there are errors and other says there are no errors (eventually both have leaks)
My question is - - what is counted as error in valgrind? (valgrind manual didn't helped me)
Memory leaks are not considered errors, they don't effect the logic of the program.
Errors are things such as invalid reads and writes.
Updated as result of comment: The invalid reads and writes in test 1 are for 5 different areas of memory which are accessed 98307 times.
Looking at the leaks, the large indirect losses in test 1 may indicated a linked data structure where only the root has been deleted.
The leaks in test 2 aren't too bad. As valgrind suggests rerun with --leak-check=full which should indicate which bit of code is causing the problem.
A full explanation of the errors can be found here