Panda3D
Loading...
Searching...
No Matches
memoryHook.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file memoryHook.cxx
10 * @author drose
11 * @date 2007-06-28
12 */
13
14#include "memoryHook.h"
15#include "deletedBufferChain.h"
16#include <stdlib.h>
17#include "typeRegistry.h"
18
19#ifdef WIN32
20
21// Windows case.
22#ifndef WIN32_LEAN_AND_MEAN
23#define WIN32_LEAN_AND_MEAN 1
24#endif
25#include <windows.h>
26
27#else
28
29// Posix case.
30#include <unistd.h>
31#include <sys/types.h>
32#include <sys/mman.h>
33
34#ifndef MAP_ANON
35#define MAP_ANON 0x1000
36#endif
37
38#endif // WIN32
39
40using std::cerr;
41
42// Ensure we made the right decisions about the alignment size.
43static_assert(MEMORY_HOOK_ALIGNMENT >= sizeof(size_t),
44 "MEMORY_HOOK_ALIGNMENT should at least be sizeof(size_t)");
45static_assert(MEMORY_HOOK_ALIGNMENT >= sizeof(void *),
46 "MEMORY_HOOK_ALIGNMENT should at least be sizeof(void *)");
47static_assert(MEMORY_HOOK_ALIGNMENT * 8 >= NATIVE_WORDSIZE,
48 "MEMORY_HOOK_ALIGNMENT * 8 should at least be NATIVE_WORDSIZE");
49static_assert((MEMORY_HOOK_ALIGNMENT & (MEMORY_HOOK_ALIGNMENT - 1)) == 0,
50 "MEMORY_HOOK_ALIGNMENT should be a power of two");
51
52#if defined(CPPPARSER)
53
54#elif defined(USE_MEMORY_MIMALLOC)
55
56// mimalloc is a modern memory manager by Microsoft that is very fast as well
57// as thread-safe.
58
59#include "mimalloc.h"
60
61#define call_malloc mi_malloc
62#define call_realloc mi_realloc
63#define call_free mi_free
64#undef MEMORY_HOOK_MALLOC_LOCK
65
66#elif defined(USE_MEMORY_DLMALLOC)
67
68// Memory manager: DLMALLOC This is Doug Lea's memory manager. It is very
69// fast, but it is not thread-safe. However, we provide thread locking within
70// MemoryHook.
71
72#define DLMALLOC_EXPORT static
73#define USE_DL_PREFIX 1
74#define NO_MALLINFO 1
75#ifdef _DEBUG
76 #define DEBUG 1
77#endif
78// dlmalloc can do the alignment we ask for.
79#define MALLOC_ALIGNMENT MEMORY_HOOK_ALIGNMENT
80
81#include "dlmalloc_src.cxx"
82
83#define call_malloc dlmalloc
84#define call_realloc dlrealloc
85#define call_free dlfree
86#define MEMORY_HOOK_MALLOC_LOCK 1
87
88#elif defined(USE_MEMORY_PTMALLOC2)
89// This doesn't appear to work in Linux; perhaps it is clashing with the
90// system library. It also doesn't appear to be thread-safe on OSX.
91
92/*
93 * Memory manager: PTMALLOC2 Ptmalloc2 is a derivative of Doug Lea's memory
94 * manager that was made thread-safe by Wolfram Gloger, then was ported to
95 * windows by Niall Douglas. It is not quite as fast as dlmalloc (because the
96 * thread-safety constructs take a certain amount of CPU time), but it's still
97 * much faster than the windows allocator.
98 */
99
100#define USE_DL_PREFIX 1
101#define NO_MALLINFO 1
102#ifdef _DEBUG
103 #define MALLOC_DEBUG 2
104#endif
105#include "ptmalloc2_smp_src.cxx"
106
107#define call_malloc dlmalloc
108#define call_realloc dlrealloc
109#define call_free dlfree
110#undef MEMORY_HOOK_MALLOC_LOCK
111
112#else
113
114// Memory manager: MALLOC This option uses the built-in system allocator.
115// This is a good choice on linux, but it's a terrible choice on windows.
116
117#define call_malloc malloc
118#define call_realloc realloc
119#define call_free free
120#undef MEMORY_HOOK_MALLOC_LOCK
121
122#endif // USE_MEMORY_*
123
124/**
125 * Increments the amount of requested size as necessary to accommodate the
126 * extra data we might piggyback on each allocated block.
127 */
128INLINE static size_t
129inflate_size(size_t size) {
130#if defined(MEMORY_HOOK_DO_ALIGN)
131 // If we're aligning, we need to request the header size, plus extra bytes
132 // to give us wiggle room to adjust the pointer.
133 return size + sizeof(uintptr_t) * 2 + MEMORY_HOOK_ALIGNMENT - 1;
134#elif defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
135 // If we are can access the allocator's bookkeeping to figure out how many
136 // bytes were allocated, we don't need to add our own information.
137 return size;
138#elif defined(DO_MEMORY_USAGE)
139 // If we're not aligning, but we're tracking memory allocations, we just
140 // need the header size extra (this gives us a place to store the size of
141 // the allocated block). However, we do need to make sure that any
142 // alignment guarantee is kept.
143 return size + MEMORY_HOOK_ALIGNMENT;
144#else
145 // If we're not doing any of that, we can just allocate the precise
146 // requested amount.
147 return size;
148#endif // DO_MEMORY_USAGE
149}
150
151/**
152 * Converts an allocated pointer to a pointer returnable to the application.
153 * Stuffs size in the first n bytes of the allocated space.
154 */
155INLINE static void *
156alloc_to_ptr(void *alloc, size_t size) {
157#if defined(MEMORY_HOOK_DO_ALIGN)
158 // Add room for two uintptr_t values.
159 uintptr_t *root = (uintptr_t *)((char *)alloc + sizeof(uintptr_t) * 2);
160 // Align this to the requested boundary.
161 root = (uintptr_t *)(((uintptr_t)root + MEMORY_HOOK_ALIGNMENT - 1) & ~(MEMORY_HOOK_ALIGNMENT - 1));
162 root[-2] = size;
163 root[-1] = (uintptr_t)alloc; // Save the pointer we originally allocated.
164 return (void *)root;
165#elif defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
166 return alloc;
167#elif defined(DO_MEMORY_USAGE)
168 size_t *root = (size_t *)alloc;
169 root[0] = size;
170 return (void *)((char *)root + MEMORY_HOOK_ALIGNMENT);
171#else
172 return alloc;
173#endif // DO_MEMORY_USAGE
174}
175
176/**
177 * Converts an application pointer back to the original allocated pointer.
178 * Extracts size from the first n bytes of the allocated space, but only if
179 * DO_MEMORY_USAGE is defined.
180 */
181INLINE static void *
182ptr_to_alloc(void *ptr, size_t &size) {
183#if defined(MEMORY_HOOK_DO_ALIGN)
184 uintptr_t *root = (uintptr_t *)ptr;
185 size = root[-2];
186 return (void *)root[-1]; // Get the pointer we originally allocated.
187#elif defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
188#ifdef DO_MEMORY_USAGE
189 size = MemoryHook::get_ptr_size(ptr);
190#endif
191 return ptr;
192#elif defined(DO_MEMORY_USAGE)
193 size_t *root = (size_t *)((char *)ptr - MEMORY_HOOK_ALIGNMENT);
194 size = root[0];
195 return (void *)root;
196#else
197 return ptr;
198#endif // DO_MEMORY_USAGE
199}
200
201/**
202 *
203 */
204MemoryHook::
205MemoryHook() {
206#ifdef WIN32
207
208 // Windows case.
209 SYSTEM_INFO sysinfo;
210 GetSystemInfo(&sysinfo);
211
212 _page_size = (size_t)sysinfo.dwPageSize;
213
214#else
215
216 // Posix case.
217 _page_size = sysconf(_SC_PAGESIZE);
218
219#endif // WIN32
220
221 _total_heap_single_size = 0;
222 _total_heap_array_size = 0;
223 _requested_heap_size = 0;
224 _total_mmap_size = 0;
225 _max_heap_size = ~(size_t)0;
226}
227
228/**
229 *
230 */
231MemoryHook::
232MemoryHook(const MemoryHook &copy) :
233 _total_heap_single_size(copy._total_heap_single_size),
234 _total_heap_array_size(copy._total_heap_array_size),
235 _requested_heap_size(copy._requested_heap_size),
236 _total_mmap_size(copy._total_mmap_size),
237 _max_heap_size(copy._max_heap_size),
238 _page_size(copy._page_size) {
239
240 copy._lock.lock();
241 _deleted_chains = copy._deleted_chains;
242 copy._lock.unlock();
243}
244
245/**
246 *
247 */
248MemoryHook::
249~MemoryHook() {
250 // Really, we only have this destructor to shut up gcc about the virtual
251 // functions warning.
252}
253
254/**
255 * Allocates a block of memory from the heap, similar to malloc(). This will
256 * never return NULL; it will abort instead if memory is not available.
257 *
258 * This particular function should be used to allocate memory for a single
259 * object, as opposed to an array. The only difference is in the bookkeeping.
260 */
262heap_alloc_single(size_t size) {
263 size_t inflated_size = inflate_size(size);
264
265#ifdef MEMORY_HOOK_MALLOC_LOCK
266 _lock.lock();
267 void *alloc = call_malloc(inflated_size);
268 _lock.unlock();
269#else
270 void *alloc = call_malloc(inflated_size);
271#endif
272
273 while (alloc == nullptr) {
274 alloc_fail(inflated_size);
275#ifdef MEMORY_HOOK_MALLOC_LOCK
276 _lock.lock();
277 alloc = call_malloc(inflated_size);
278 _lock.unlock();
279#else
280 alloc = call_malloc(inflated_size);
281#endif
282 }
283
284#ifdef DO_MEMORY_USAGE
285 // In the DO_MEMORY_USAGE case, we want to track the total size of allocated
286 // bytes on the heap.
287#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
288 // dlmalloc may slightly overallocate, however.
289 size = get_ptr_size(alloc);
290 inflated_size = size;
291#endif
292 AtomicAdjust::add(_total_heap_single_size, (AtomicAdjust::Integer)size);
293 if ((size_t)AtomicAdjust::get(_total_heap_single_size) +
294 (size_t)AtomicAdjust::get(_total_heap_array_size) >
295 _max_heap_size) {
296 overflow_heap_size();
297 }
298#endif // DO_MEMORY_USAGE
299
300 void *ptr = alloc_to_ptr(alloc, size);
301#ifdef _DEBUG
302 assert(((uintptr_t)ptr % MEMORY_HOOK_ALIGNMENT) == 0);
303 assert(ptr >= alloc && (char *)ptr + size <= (char *)alloc + inflated_size);
304#endif
305 return ptr;
306}
307
308/**
309 * Releases a block of memory previously allocated via heap_alloc_single.
310 */
312heap_free_single(void *ptr) {
313 size_t size;
314 void *alloc = ptr_to_alloc(ptr, size);
315
316#ifdef DO_MEMORY_USAGE
317 assert((int)size <= _total_heap_single_size);
318 AtomicAdjust::add(_total_heap_single_size, -(AtomicAdjust::Integer)size);
319#endif // DO_MEMORY_USAGE
320
321#ifdef MEMORY_HOOK_MALLOC_LOCK
322 _lock.lock();
323 call_free(alloc);
324 _lock.unlock();
325#else
326 call_free(alloc);
327#endif
328}
329
330/**
331 * Allocates a block of memory from the heap, similar to malloc(). This will
332 * never return NULL; it will abort instead if memory is not available.
333 *
334 * This particular function should be used to allocate memory for an array of
335 * objects, as opposed to a single object. The only difference is in the
336 * bookkeeping.
337 */
339heap_alloc_array(size_t size) {
340 size_t inflated_size = inflate_size(size);
341
342#ifdef MEMORY_HOOK_MALLOC_LOCK
343 _lock.lock();
344 void *alloc = call_malloc(inflated_size);
345 _lock.unlock();
346#else
347 void *alloc = call_malloc(inflated_size);
348#endif
349
350 while (alloc == nullptr) {
351 alloc_fail(inflated_size);
352#ifdef MEMORY_HOOK_MALLOC_LOCK
353 _lock.lock();
354 alloc = call_malloc(inflated_size);
355 _lock.unlock();
356#else
357 alloc = call_malloc(inflated_size);
358#endif
359 }
360
361#ifdef DO_MEMORY_USAGE
362 // In the DO_MEMORY_USAGE case, we want to track the total size of allocated
363 // bytes on the heap.
364#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
365 // dlmalloc may slightly overallocate, however.
366 size = get_ptr_size(alloc);
367 inflated_size = size;
368#endif
369 AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size);
370 if ((size_t)AtomicAdjust::get(_total_heap_single_size) +
371 (size_t)AtomicAdjust::get(_total_heap_array_size) >
372 _max_heap_size) {
373 overflow_heap_size();
374 }
375#endif // DO_MEMORY_USAGE
376
377 void *ptr = alloc_to_ptr(alloc, size);
378#ifdef _DEBUG
379 assert(((uintptr_t)ptr % MEMORY_HOOK_ALIGNMENT) == 0);
380 assert(ptr >= alloc && (char *)ptr + size <= (char *)alloc + inflated_size);
381#endif
382 return ptr;
383}
384
385/**
386 * Resizes a block of memory previously returned from heap_alloc_array.
387 */
389heap_realloc_array(void *ptr, size_t size) {
390 size_t orig_size;
391 void *alloc = ptr_to_alloc(ptr, orig_size);
392
393 size_t inflated_size = inflate_size(size);
394
395 void *alloc1 = alloc;
396#ifdef MEMORY_HOOK_MALLOC_LOCK
397 _lock.lock();
398 alloc1 = call_realloc(alloc1, inflated_size);
399 _lock.unlock();
400#else
401 alloc1 = call_realloc(alloc1, inflated_size);
402#endif
403
404 while (alloc1 == nullptr) {
405 alloc_fail(inflated_size);
406
407 // Recover the original pointer.
408 alloc1 = alloc;
409
410#ifdef MEMORY_HOOK_MALLOC_LOCK
411 _lock.lock();
412 alloc1 = call_realloc(alloc1, inflated_size);
413 _lock.unlock();
414#else
415 alloc1 = call_realloc(alloc1, inflated_size);
416#endif
417 }
418
419#ifdef DO_MEMORY_USAGE
420#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
421 // dlmalloc may slightly overallocate, however.
422 size = get_ptr_size(alloc1);
423 inflated_size = size;
424#endif
425 assert((AtomicAdjust::Integer)orig_size <= _total_heap_array_size);
426 AtomicAdjust::add(_total_heap_array_size, (AtomicAdjust::Integer)size-(AtomicAdjust::Integer)orig_size);
427#endif // DO_MEMORY_USAGE
428
429 // Align this to the requested boundary.
430#ifdef MEMORY_HOOK_DO_ALIGN
431 // This copies the code from alloc_to_ptr, since we can't write the size and
432 // pointer until after we have done the memmove.
433 uintptr_t *root = (uintptr_t *)((char *)alloc1 + sizeof(uintptr_t) * 2);
434 root = (uintptr_t *)(((uintptr_t)root + MEMORY_HOOK_ALIGNMENT - 1) & ~(MEMORY_HOOK_ALIGNMENT - 1));
435 void *ptr1 = (void *)root;
436
437 size_t orig_delta = (char *)ptr - (char *)alloc;
438 size_t new_delta = (char *)ptr1 - (char *)alloc1;
439 if (orig_delta != new_delta) {
440 memmove((char *)alloc1 + new_delta, (char *)alloc1 + orig_delta, std::min(size, orig_size));
441 }
442
443 root[-2] = size;
444 root[-1] = (uintptr_t)alloc1; // Save the pointer we originally allocated.
445#else
446 void *ptr1 = alloc_to_ptr(alloc1, size);
447#endif
448
449#ifdef _DEBUG
450 assert(ptr1 >= alloc1 && (char *)ptr1 + size <= (char *)alloc1 + inflated_size);
451 assert(((uintptr_t)ptr1 % MEMORY_HOOK_ALIGNMENT) == 0);
452#endif
453 return ptr1;
454}
455
456/**
457 * Releases a block of memory previously allocated via heap_alloc_array.
458 */
460heap_free_array(void *ptr) {
461 size_t size;
462 void *alloc = ptr_to_alloc(ptr, size);
463
464#ifdef DO_MEMORY_USAGE
465 assert((int)size <= _total_heap_array_size);
466 AtomicAdjust::add(_total_heap_array_size, -(AtomicAdjust::Integer)size);
467#endif // DO_MEMORY_USAGE
468
469#ifdef MEMORY_HOOK_MALLOC_LOCK
470 _lock.lock();
471 call_free(alloc);
472 _lock.unlock();
473#else
474 call_free(alloc);
475#endif
476}
477
478/**
479 * Attempts to release memory back to the system, if possible. The pad
480 * argument is the minimum amount of unused memory to keep in the heap
481 * (against future allocations). Any memory above that may be released to the
482 * system, reducing the memory size of this process. There is no guarantee
483 * that any memory may be released.
484 *
485 * Returns true if any memory was actually released, false otherwise.
486 */
488heap_trim(size_t pad) {
489 bool trimmed = false;
490
491#if defined(USE_MEMORY_DLMALLOC) || defined(USE_MEMORY_PTMALLOC2)
492 // Since malloc_trim() isn't standard C, we can't be sure it exists on a
493 // given platform. But if we're using dlmalloc, we know we have
494 // dlmalloc_trim.
495 _lock.lock();
496 if (dlmalloc_trim(pad)) {
497 trimmed = true;
498 }
499 _lock.unlock();
500#endif
501
502#ifdef WIN32
503 // Also, on Windows we have _heapmin().
504 if (_heapmin() == 0) {
505 trimmed = true;
506 }
507#endif
508
509 return trimmed;
510}
511
512/**
513 * Allocates a raw page or pages of memory directly from the OS. This will be
514 * in a different address space from the memory allocated by heap_alloc(), and
515 * so it won't contribute to fragmentation of that memory.
516 *
517 * The allocation size must be an integer multiple of the page size. Use
518 * round_to_page_size() if there is any doubt.
519 *
520 * If allow_exec is true, the memory will be flagged so that it is legal to
521 * execute code that has been written to this memory.
522 */
524mmap_alloc(size_t size, bool allow_exec) {
525 assert((size % _page_size) == 0);
526
527#ifdef DO_MEMORY_USAGE
528 _total_mmap_size += size;
529#endif
530
531#ifdef WIN32
532
533 // Windows case.
534 void *ptr = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE,
535 allow_exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
536 if (ptr == nullptr) {
537 DWORD err = GetLastError();
538 cerr << "Couldn't allocate memory page of size " << size << ": ";
539
540 PVOID buffer;
541 DWORD length =
542 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
543 nullptr, err, 0, (LPTSTR)&buffer, 0, nullptr);
544 if (length != 0) {
545 cerr << (char *)buffer << "\n";
546 } else {
547 cerr << "Error code " << err << "\n";
548 }
549 LocalFree(buffer);
550 abort();
551 }
552
553 return ptr;
554
555#else
556
557 // Posix case.
558 int prot = PROT_READ | PROT_WRITE;
559 if (allow_exec) {
560 prot |= PROT_EXEC;
561 }
562 void *ptr = mmap(nullptr, size, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
563 if (ptr == (void *)-1) {
564 perror("mmap");
565 abort();
566 }
567
568 return ptr;
569
570#endif // WIN32
571}
572
573/**
574 * Frees a block of memory previously allocated via mmap_alloc(). You must
575 * know how large the block was.
576 */
578mmap_free(void *ptr, size_t size) {
579 assert((size % _page_size) == 0);
580
581#ifdef DO_MEMORY_USAGE
582 assert((int)size <= _total_mmap_size);
583 _total_mmap_size -= size;
584#endif
585
586#ifdef WIN32
587 VirtualFree(ptr, 0, MEM_RELEASE);
588#else
589 munmap(ptr, size);
590#endif
591}
592
593/**
594 * This special method exists only to provide a callback hook into
595 * MemoryUsage. It indicates that the indicated pointer, allocated from
596 * somewhere other than a call to heap_alloc(), now contains a pointer to the
597 * indicated ReferenceCount object. If orig_size is 0, it indicates that the
598 * ReferenceCount object has been destroyed.
599 */
601mark_pointer(void *, size_t, ReferenceCount *) {
602}
603
604/**
605 * Returns a pointer to a global DeletedBufferChain object suitable for
606 * allocating arrays of the indicated size. There is one unique
607 * DeletedBufferChain object for every different size.
608 */
610get_deleted_chain(size_t buffer_size) {
611 DeletedBufferChain *chain;
612
613 _lock.lock();
614 DeletedChains::iterator dci = _deleted_chains.find(buffer_size);
615 if (dci != _deleted_chains.end()) {
616 chain = (*dci).second;
617 } else {
618 // Once allocated, this DeletedBufferChain object is never deleted.
619 chain = new DeletedBufferChain(buffer_size);
620 _deleted_chains.insert(DeletedChains::value_type(buffer_size, chain));
621 }
622
623 _lock.unlock();
624 return chain;
625}
626
627/**
628 * This callback method is called whenever a low-level call to call_malloc()
629 * has returned NULL, indicating failure.
630 *
631 * Since this method is called very low-level, and may be in the middle of any
632 * number of critical sections, it will be difficult for this callback
633 * initiate any emergency high-level operation to make more memory available.
634 * However, this module is set up to assume that that's what this method does,
635 * and will make another alloc attempt after it returns. Probably the only
636 * sensible thing this method can do, however, is just to display a message
637 * and abort.
638 */
640alloc_fail(size_t attempted_size) {
641 cerr << "Out of memory allocating " << attempted_size << " bytes\n";
642 abort();
643}
644
645/**
646 * This callback method is called whenever the total allocated heap size
647 * exceeds _max_heap_size. It's mainly intended for reporting memory leaks,
648 * on the assumption that once we cross some specified threshold, we're just
649 * leaking memory.
650 *
651 * The implementation for this method is in MemoryUsage.
652 */
653void MemoryHook::
654overflow_heap_size() {
655#ifdef DO_MEMORY_USAGE
656 _max_heap_size = ~(size_t)0;
657#endif // DO_MEMORY_USAGE
658}
static Integer add(Integer &var, Integer delta)
Atomically computes var += delta.
static Integer get(const Integer &var)
Atomically retrieves the snapshot value of the indicated variable.
This template class can be used to provide faster allocation/deallocation for many Panda objects.
This class provides a wrapper around the various possible malloc schemes Panda might employ.
Definition memoryHook.h:37
virtual void * mmap_alloc(size_t size, bool allow_exec)
Allocates a raw page or pages of memory directly from the OS.
virtual void mmap_free(void *ptr, size_t size)
Frees a block of memory previously allocated via mmap_alloc().
virtual void alloc_fail(size_t attempted_size)
This callback method is called whenever a low-level call to call_malloc() has returned NULL,...
virtual void * heap_alloc_array(size_t size)
Allocates a block of memory from the heap, similar to malloc().
DeletedBufferChain * get_deleted_chain(size_t buffer_size)
Returns a pointer to a global DeletedBufferChain object suitable for allocating arrays of the indicat...
virtual void heap_free_array(void *ptr)
Releases a block of memory previously allocated via heap_alloc_array.
virtual void * heap_alloc_single(size_t size)
Allocates a block of memory from the heap, similar to malloc().
virtual void mark_pointer(void *ptr, size_t orig_size, ReferenceCount *ref_ptr)
This special method exists only to provide a callback hook into MemoryUsage.
virtual void heap_free_single(void *ptr)
Releases a block of memory previously allocated via heap_alloc_single.
static size_t get_ptr_size(void *ptr)
Given a pointer that was returned by a MemoryHook allocation, returns the number of bytes that were a...
Definition memoryHook.I:67
bool heap_trim(size_t pad)
Attempts to release memory back to the system, if possible.
virtual void * heap_realloc_array(void *ptr, size_t size)
Resizes a block of memory previously returned from heap_alloc_array.
A base class for all things that want to be reference-counted.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.