malloc etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
malloc etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

13 Kasım 2021 Cumartesi

libc malloc alternatifleri

Giriş
libc dışında alternatif olarak kullanılabilecek başka heap kütüphaneleri de mevcut.

gnu malloc Problemi Nedir?
Açıklaması şöyle
Under the hood, GNU malloc uses various data structures to make memory allocations more efficient. One such data structure is a collection of heaps. When the application calls malloc, one of these heaps is searched for a contiguous free chunk of memory big enough to fit the request. When the application calls free, a chunk of the heap frees up, which can be reused by a future malloc call. An important detail is that only the topmost chunk of each heap is available for returning to the OS. All empty chunks in the middle of heaps will technically be unused, but still count towards the memory the application is using. This is a very simplified view of what’s happening; check the glibc wiki for a complete overview of GNU malloc internals.
Şeklen şöyle

Bu da aslında bazen fazla bellek kullanılmasına sebep oluyor. Açıklaması şöyle. Kullanılan bellek oranı mallinfo() çağrısı ile görülebilir. gdb ile bağlanıp mallinfo() yapmak gerekiyor.
As you might imagine, there could be many chunks in the malloc heaps just sitting around empty, depending on the pattern of malloc and free calls an application executes. At this point we were wondering if this kind of memory fragmentation could explain the Fulfillment Service’s ever growing memory usage. While investigating possible ways to confirm this, we happened upon an excellent article from the LinkedIn engineering team, describing a problem extremely similar to ours. However, instead of using the gdb-heap tool the LinkedIn team used, we decided to confirm our hypothesis in a slightly more direct way.

It turns out that GNU malloc already exposes some statistics which are suitable for roughly quantifying memory fragmentation. The statistics we are interested in are: the total size of memory in use by the application, the total size of memory in malloc’s heaps but not in use by the application and the part of that memory allowed to be returned to the OS. GNU malloc’s mallinfo function returns these as the uordblks, fordblks and keepcost fields respectively. So, calculating 1 — uordblks / (fordblks — keepcost) gives the ratio of unused memory that cannot be returned to the OS, a measure of memory fragmentation.
Bu çağrının döndürdüğü şey şeklen şöyle

gdb ve java birlikte kullanımı için açıklama şöyle
Having learned this, we created a local testing setup of the Fulfillment Service. This setup consisted of a Docker container with gdb (the GNU debugger for debugging native code), OpenJDK with debug symbols and glibc with debug symbols. This setup allowed us to attach gdb to the JVM and call the mallinfo function from the gdb prompt.

1. jemalloc
github sayfası burada. Açıklaması şöyle. Eğer MALLOC_CONF değişkeni atanırsa, uygulama .heap uzantılı dosyalar üretir, bu dosyalar jeprof komutu ile okunaklı hale getirilebilir.
These two functions, malloc and free, are implemented in their own library. The default on most flavors of Linux is GNU malloc, but it can be swapped out for other implementations. One such implementation is jemalloc, which conveniently also allows tracking where malloc is being called from. This gives us the opportunity to see whether there are any native functions allocating increasing amounts of memory.

On Linux, jemalloc can be enabled by bundling its shared library with an application and setting the LD_PRELOAD environment variable to the location of libjemalloc.so before running Java. Memory profiling can be enabled through the MALLOC_CONF environment variable. The jemalloc wiki contains some useful examples. You can check the jemalloc man page for a full reference of these options. Now our application writes .heap files after a set volume of allocations. These files can be parsed using the jeprof command into something human-readable.
jemalloc ile ilgili bir başka açıklama şöyle. jemalloc tüm malloc() çağrılarını takip etmiyor ancak örnekleme yapıyor.
Jemalloc only samples memory allocations instead of measuring every single malloc call to prevent excessive resource consumption. Therefore, the output of jeprof cannot be directly interpreted as the number of bytes currently in use. However, it does allow us to spot any suspicious functions allocating native memory. Additionally, we could also spot functions that are holding on to significantly more memory relative to others (potentially indicating a memory leak).
Örnek
Şöyle yaparız
export LD_PRELOAD=/usr/local/lib/libjemalloc.so

# tell jemalloc to write a profile to the disk every few 1Gb allocations 
# and record a stack trace (referenced from the blog):
export MALLOC_CONF=prof:true,lg_prof_interval:30,lg_prof_sample:17

# jeprof*.heap. isimli dosyalar oluşur
# dosyalardan rapor oluştur
jeprof --show_bytes --gif /path/to/jvm/bin/java jeprof*.heap > /tmp/app-profiling.gif

2. tcmalloc
Örneğin TCMalloc google tarafından kullanılıyor.

$ LD_PRELOAD="/usr/lib/libtcmalloc.so"yaparak yeniden derlemeye ihtiyaç duymadan kullanılabilir. Açıklaması şöyle.
TCMalloc assigns each thread a thread-local cache. Small allocations are satisfied from the thread-local cache. Objects are moved from central data structures into a thread-local cache as needed, and periodic garbage collections are used to migrate memory back from a thread-local cache into the central data structures.

24 Mayıs 2019 Cuma

malloc metodu ve türevleri

Giriş
C++ dilinde programlarken malloc yerine genellikle new() kullanılır. Ancak yine de C kütüphanesinde bulunan malloc() ve türevlerinin nasıl çalıştığını bilmekte fayda var.

kalloc - Linux'ta bulunur
Linux kernel'in kodlarında bellek ayırmak için kullanılır.

alloca - standart C metodu değildir
alloca metodu yazısına taşıdım

malloc - Standart C Metodu
Metodun imzası şöyle.
void * malloc (size_t size);
size_t unsigned bir tiptir. size_t yerine int kullanırsak yanlış sonuçlar elde edebiliriz. Şöyle bir kod olsun. 32 bit makinelerde çıktı olarak "malloc failed" alırız.
float *ls;
int num = 56120;
ls = (float *)malloc((num * num)*sizeof(float));
if(ls == NULL){
  cout << "malloc failed !!!" << endl;
}
cout << "malloc succeeded ~~~" << endl;
Sebebi ise num * num işleminin eksi bir sayıya dönüşmesi.
-4582051584
Bunu da size_t'ye çevirince çok büyük bir sayı ortaya çıkıyor. num size_t olarak tanımlansa daha iyi.
18446744069127500032
libc malloc alternatifleri
libc malloc alternatifleri yazısına taşıdım

malloc ve sistem çağrısı
Yazının bu kısmı aslında tamamen malloc gerçekleştirimine bağlı. gcc gerçekleştiriminde mmap çağrısı brk() ve mmap() kullanır. Açıklaması şöyle
I traced Linux system calls and found that if I use malloc to request a small amount of heap memory, then malloc calls brk internally.

But if I use malloc to request a very large amount of heap memory, then malloc calls mmap internally.
Tabi bu gerçekleştirimi değiştirmesi zor. Açıklaması şöyle
The malloc implementation I suspect you are looking at (the one in the GNU C Library, based on your tags) is very old and mainly continues to be used because nobody is brave enough to take the risk of swapping it out for something newer that will probably but not certainly be better.
1. brk() ve sbrk()
Unix ve türevlerinde brk() yanında sbrk() sistem çağrısı da vardır. brk() ile alınan bellek genelde geri verilemiyor. Açıklaması şöyle
brk changes the ending address of a single, contiguous "arena" of virtual address space: if this address is increased it allocates more memory to the arena, and if it is decreased, it deallocates the memory at the end of the arena. Therefore, memory allocated with brk can only be released back to the operating system when a continuous range of addresses at the end of the arena is no longer needed by the process.
2. mmap()
M_MMAP_THRESHOLD değerinden büyük malloc istekleri mmap ile karşılanır. Açıklaması şöyle.
For allocations greater than or equal to the limit specified (in bytes) by M_MMAP_THRESHOLD that can't be satisfied from the free list, the memory-allocation functions employ mmap(2) instead of increasing the program break using sbrk(2).
Allocating memory using mmap(2) has the significant advantage that the allocated memory blocks can always be independently released back to the system. (By contrast, the heap can be trimmed only if memory is freed at the top end.) On the other hand, there are some disadvantages to the use of mmap(2): deallocated space is not placed on the free list for reuse by later allocations;
Linux ve Overcommit
Linux ve Overcommit yazısına taşıdım

malloc'a verilen parametre
İstenilen byte sayısı 0 ise sonuç malloc gerçekleştirimine bağlı (implementation dependent).
If the space cannot be allocated, a null pointer is returned. If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
Yani null veya bir bellek alanı dönebilir.
If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
Fakat isterer null dönsün ister bellek alanı dönsün şu kod parçasının sonucu rahatlıkla free metoduna verilebilir. Açıklaması şöyle.
The free() function shall cause the space pointed to by ptr to be deallocated; that is, made available for further allocation. If ptr is a null pointer, no action shall occur. Otherwise, if the argument does not match a pointer earlier returned by a function in POSIX.1-2008 that allocates memory as if by malloc(), or if the space has been deallocated by a call to free() or realloc(), the behavior is undefined.
Şöyle yaparız.
int* x = (int*) malloc(0);
...
free(x);
FreeBSD bir bellek alanı dönüyor. Kodu şöyle
void *
je_malloc(size_t size)
{
  void *ret;
  size_t usize JEMALLOC_CC_SILENCE_INIT(0);

  if (size == 0)
    size = 1;
  [...]
Apple'ın libmalloc bir byte büyüklüğünde bellek alanı dönüyor. Kodu şöyle
void *
szone_memalign(szone_t *szone, size_t alignment, size_t size)
{
  if (size == 0) {
    size = 1; // Ensures we'll return an aligned free()-able pointer
  [...]
GLIBC de bellek alanı dönüyor. Kodu şöyle.
#define request2size(req)                                       \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)  ?         \
    MINSIZE :                                                   \
    ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
bellek alanının içeriği
Bellek ilklendirilmemiştir.
.... The memory is not initialized. ...
Bu alanı okumaya çalışmak tanımsızdır.
...whose value is indeterminate. ...
Tam açıklama şöyle
The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
İşletim sistemi sayfa sayfa çalıştığı için malloc() ile istenen alanın biraz dışına taşarak yazmak uygulamanın çökmesine sebep olmayabilir. Zaten bu C++'taki klasik bellek hatalarını ta kendisi.
int* p = malloc(sizeof(int));

p[1000] = 12;
malloc'un döndürebileceği en büyük alan
Büyüklük tanımlı değil.
malloc ile alınan alanı geçmek
Pointer arithmetic ile malloc ile alınan alanı +1 olarak geçmek dereference yapılmadığı müddetçe sorun yaratmaz. Açıklaması şöyle.
It is well defined if p is pointing to one past the allocated memory and it is not
dereferenced.
Şöyle yaparız.
int a[5];
ptrdiff_t diff = &a[5] - &a[0]; // Well-defined

int *d = malloc(5 * sizeof(*d));
assert(d != NULL, "Memory allocation failed");
diff = &d[5] - &d[0];        // Well-defined
mallac ve return result
malloc null dönebilir. Sonucu kontrol etmek  gerekir.

Örnek
Şöyle yaparız.
char *block=malloc(10000);
if (block==NULL){
  printf("Can't allocate memory\n");
  return -1;
}
Örnek
Şöyle yaparız.
void *safe_malloc(size_t size)
{
  void *ptr = malloc(size);

  if (!ptr && (size > 0)) {
    perror("malloc failed!");
    exit(EXIT_FAILURE);
  }

  return ptr;
}
malloc ve return type
malloc() ile mmap() birbirlerine çok benziyorlar. Her ikisi de sanal bir adres döndürüyor.
malloc void* döner, C dilinde bu belleği kendi değişkenimize atarken, cast yapılmaması gerekir. Yani aşağıdaki kullanım doğru değil!

int *ptr = (int *)malloc(10 * sizeof (*ptr));
malloc ve free arasındaki ilişki
malloc döndürdüğü bellek alanının önündeki byte'lara bellek yönetimiyle ilgili bazı bilgileri ekler. Böylece free() metoduna kaç byte silmesi gerektiğini söylemek zorunda kalmayız. Aşağıda küçük bir örnek var.
void *my_alloc(size_t size) {
  void *block = malloc(sizeof(size) + size);
  *(size_t *)block = size;
  return (void *) ((size_t *)block + 1);
}
void my_free(void *block) {
  block = (size_t *)block - 1;
  mfree(block, *(size_t *)block);
}
calloc - Standart C Metodu
calloc metodu yazısına taşıdım.

realloc - Standart C Metodu
realloc metodu yazısına taşıdım.

mtrace
malloc ile ayrılan bellek alanlarını takip etmek için kullanılır. Açıklaması şöyle
The mtrace() function installs hook functions for the memory-allocation functions (malloc(3), realloc(3) memalign(3), free(3)). These hook functions record tracing information about memory allocation and deallocation. The tracing information can be used to discover memory leaks and attempts to free nonallocated memory in a program.
When mtrace() is called, it checks the value of the environment variable MALLOC_TRACE, which should contain the pathname of a file in which the tracing information is to be recorded. If the pathname is successfully opened, it is truncated to zero length.