什么是Heap(堆)
在Dalvik虚拟机中,堆实际上就是一块匿名共享内存. 一个mspace(libc中的概念),可以通过libc提供的函数create_mspace_with_base创建一个mspace,然后再通过mspace_开头的函数管理该mspace。例如,我们可以通过mspace_malloc和mspace_bulk_free来在指定的mspace中分配和释放内存。
函数dvmAllocRegion所做的事情就是调用函数ashmem_create_region来创建一块匿名共享内存,一段连续的内存,大小是gDvm.heapMaximumSize.
libc中的内存管理函数
dlmalloc使用了两套API.一套对应默认的mspace,以dl前缀开头,如dlmalloc, dlrealloc等.如果创建了自定义的mspace,则使用mspace开头的API,如mspace_malloc, mspace_realloc
/* create_mspace_with_base uses the memory supplied as the initial base
of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this
space is used for bookkeeping, so the capacity must be at least this
large. (Otherwise 0 is returned.) When this initial space is
exhausted, additional memory will be obtained from the system.
Destroying this space will deallocate all additionally allocated
space (if possible) but not the initial base.*/
mspace create_mspace_with_base(void* base, size_t capacity, int locked);
/*
malloc_footprint_limit();
Returns the number of bytes that the heap is allowed to obtain from
the system, returning the last value returned by
malloc_set_footprint_limit, or the maximum size_t value if
never set. The returned value reflects a permission. There is no
guarantee that this number of bytes can actually be obtained from
the system. */
size_t mspace_footprint_limit(mspace msp);
size_t mspace_set_footprint_limit(mspace msp, size_t bytes);
void *dvmAllocRegion(size_t byteCount, int prot, const char *name) {
void *base;
int fd, ret;
byteCount = ALIGN_UP_TO_PAGE_SIZE(byteCount);
fd = ashmem_create_region(name, byteCount);
if (fd == -1) {
return NULL;
}
base = mmap(NULL, byteCount, prot, MAP_PRIVATE, fd, 0);
ret = close(fd);
if (base == MAP_FAILED) {
return NULL;
}
if (ret == -1) {
munmap(base, byteCount);
return NULL;
}
return base;
}
dvmHeapSourceStartup调用函数createMspace将前面得到的匿名共享内存块封装为一个mspace
mspace的起始大小为Java堆的起始大小。后面我们动态地调整这个mspace的大小,使得它可以使用更多的内存,但是不能超过Java堆的最大值。
static mspace createMspace(void* begin, size_t morecoreStart, size_t startingSize)
{
// Clear errno to allow strerror on error.
errno = 0;
// Allow access to inital pages that will hold mspace.
mprotect(begin, morecoreStart, PROT_READ | PROT_WRITE);
// Create mspace using our backing storage starting at begin and with a footprint of
// morecoreStart. Don't use an internal dlmalloc lock. When morecoreStart bytes of memory are
// exhausted morecore will be called.
mspace msp = create_mspace_with_base(begin, morecoreStart, false /*locked*/);
if (msp != NULL) {
// Do not allow morecore requests to succeed beyond the starting size of the heap.
mspace_set_footprint_limit(msp, startingSize);
} else {
ALOGE("create_mspace_with_base failed %s", strerror(errno));
}
return msp;
}
堆的作用
虚拟机要解决的问题之一就是帮助应用程序自动分配和释放内存。
虚拟机在启动的时候向操作系统申请一大块内存当作对象堆。之后当应用程序创建对象时,虚拟机就会在堆上分配合适的内存块。而当对象不再使用时,虚拟机就会将它占用的内存块归还给堆。
Active堆和一个Zygote堆
应用程序进程是由Zygote进程fork出来的。
应用程序进程和Zygote进程共享了同一个用来分配对象的堆。然而,当Zygote进程或者应用程序进程对该堆进行写操作时,内核就会执行真正的拷贝操作,使得Zygote进程和应用程序进程分别拥有自己的一份拷贝。
不利因素:
拷贝是一件费时费力的事情。因此,为了尽量地避免拷贝,Dalvik虚拟机将自己的堆划分为两部分。
Dalvik虚拟机的堆最初是只有一个的。也就是Zygote进程在启动过程中创建D