直面底层第一站 - Thread 上( Thread 大小)
- Thread 大小 (上)
- JNIEnv 什么作用,怎么来的 (中)
- 线程挂起实现,synchronized art原理 Monitor(art/runtime/monitor.cc) (下)
方法栈大小
在Thread 的构造函数中会调用init方法,其第四个long形参数就表示方法栈的大小,一般都是0(由系统指定默认值)
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
...
}
线程开启,调用start的时候由nativeCreate传向底层
public synchronized void start() {
...
nativeCreate(this, stackSize, daemon);
...
}
由JNI调度nativeCreate会调到thread.cc的CreateNativeThread
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
// 1.这里赋值完后 stack_size为1064960b(1M + 16kb)
stack_size = FixStackSize(stack_size);
pthread_attr_t attr;
// 调用到bionic/libc/bionic/pthread_attr.cpp的pthread_attr_setstacksize
// attr->stack_size = stack_size;
// 把stack_size设置到attr
CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size);
//2. 创建线程
pthread_create_result = pthread_create(&new_pthread,
&attr,
Thread::CreateCallback,
child_thread);
}
注释1. 方法栈栈大小设置
static size_t FixStackSize(size_t stack_size) {
if (stack_size == 0) {
// 这里通常情况下为0
stack_size = Runtime::Current()->GetDefaultStackSize();
}
// 1M
stack_size += 1 * MB;
...
// 所以stack_size最终大小为 1M + 16kb
stack_size = RoundUp(stack_size, kPageSize);
return stack_size;
}
gdb调试可以看的stack_size结果
注释2. 线程创建
bionic/libc/bionic/pthread_create.cpp
int pthread_create(pthread_t* thread_out, pthread_attr_t const* attr,
void* (*start_routine)(void*), void* arg) {
pthread_attr_t thread_attr;
thread_attr = *attr;
attr = NULL; // Prevent misuse below.
pthread_internal_t* thread = NULL;
void* child_stack = NULL;
//3. __allocate_thread 分配内存
int result = __allocate_thread(&thread_attr, &thread, &child_stack);
if (result != 0) {
return result;
}
}
注释3. 分配内存 TLS初始化
static int __allocate_thread(pthread_attr_t* attr, pthread_internal_t** threadp, void** child_stack) {
size_t mmap_size;
uint8_t* stack_top;
if (attr->stack_base == NULL) {
if (__builtin_add_overflow(attr->stack_size, attr->guard_size, &mmap_size)) return EAGAIN;
if (__builtin_add_overflow(mmap_size, sizeof(pthread_internal_t), &mmap_size)) return EAGAIN;
mmap_size = __BIONIC_ALIGN(mmap_size, PAGE_SIZE);
attr->guard_size = __BIONIC_ALIGN(attr->guard_size, PAGE_SIZE);
// 映射栈空间
attr->stack_base = __create_thread_mapped_space(mmap_size, attr->guard_size);
if (attr->stack_base == NULL) {
return EAGAIN;
}
stack_top = reinterpret_cast<uint8_t*>(attr->stack_base) + mmap_size;
}
...
// 为了安全访问pthread_internal_t和线程栈,调整栈顶,边界16字节对齐
stack_top = reinterpret_cast<uint8_t*>(
(reinterpret_cast<uintptr_t>(stack_top) - sizeof(pthread_internal_t)) & ~0xf);
pthread_internal_t* thread = reinterpret_cast<pthread_internal_t*>(stack_top);
if (mmap_size == 0) {
memset(thread, 0, sizeof(pthread_internal_t));
}
// 更新stack_zise
attr->stack_size = stack_top - reinterpret_cast<uint8_t*>(attr->stack_base);
thread->mmap_size = mmap_size;
thread->attr = *attr;
//4. 初始化线程局部存储TLS
if (!__init_tls(thread)) {
if (thread->mmap_size != 0) munmap(thread->attr.stack_base, thread->mmap_size);
return EAGAIN;
}
__init_thread_stack_guard(thread);
*threadp = thread;
*child_stack = stack_top;
return 0;
}
注释4. 本地临时存储TLS 初始化
bool __init_tls(pthread_internal_t* thread) {
thread->tls[TLS_SLOT_SELF] = thread->tls;
thread->tls[TLS_SLOT_THREAD_ID] = thread;
// 本地临时存储TLS大小20K(20480b)
size_t allocation_size = BIONIC_TLS_SIZE + (2 * PTHREAD_GUARD_SIZE);
void* allocation = mmap(nullptr, allocation_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (allocation == MAP_FAILED) {
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"pthread_create failed: couldn't allocate TLS: %s", strerror(errno));
return false;
}
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, allocation, allocation_size, "bionic TLS guard");
thread->bionic_tls = reinterpret_cast<bionic_tls*>(static_cast<char*>(allocation) +
PTHREAD_GUARD_SIZE);
if (mprotect(thread->bionic_tls, BIONIC_TLS_SIZE, PROT_READ | PROT_WRITE) != 0) {
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"pthread_create failed: couldn't mprotect TLS: %s", strerror(errno));
munmap(allocation, allocation_size);
return false;
}
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, thread->bionic_tls, BIONIC_TLS_SIZE, "bionic TLS");
return true;
}
Thread大小计算
首先看看一个Java Thread对象的大小,由Profile dump 内存,统计到一空的Thread大概132b,加上Runable等引用最少180b
Thread的大小 至少:
方法栈(1M+16K)+程局部存储TLS(20K)+Thread对象的大小(180B)
(ps:其实c++ thread的对象大小(包涵 monitor等各个成员)没有计算)
值得一提的是,mmap分配的都是虚拟内存,不会分配实际的物理内存,真正使用到的时候,linux由缺页中断,才会分配真正的物理内存。