提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
例如:c++中是实例化一个对象分为二步,第一步开辟内存、第二步 调用构造函数init对象的属性信息.那么java new一个对象 都做哪些操作.这篇文档从字节码层面和jvm层面这二个角度来分析。
一、java的new一个对象的字节码
public class Demo{
public static void main(String[] args) {
Demo demo = new Demo();
}
}
Constant pool: //常量池
//类信息
#2 = Class #14 // Demo
#3 = Methodref #2.#13 // Demo."<init>":()V
#14 = Utf8 Demo
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
// 字节码是new,执行常量#2
0: new #2 // class Demo
//复制栈顶引用
3: dup
//消耗一个引用,触发调用构造方法 <init>
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: return
}
二、使jvm层面如何处理字节码new
1. jvm字节码解释器bytecodeInterpreter.cpp
代码如下(示例):
CASE(_new): {
u2 index = Bytes::get_Java_u2(pc+1);
//获取ConstantPool
ConstantPool* constants = istate->method()->constants();
if (!constants->tag_at(index).is_unresolved_klass()) {
//根据编译的常量池index查找
Klass* entry = constants->slot_at(index).get_klass();
...
InstanceKlass* ik = (InstanceKlass*) k_entry;
...
//此处tlab(Slow case allocation有tlab介绍)及检测省略
//设置对象头,偏向锁和synchronized原理相关此次不做解释
if (UseBiasedLocking) {
result->set_mark(ik->prototype_header());
} else {
result->set_mark(markOopDesc::prototype());
}
// Slow case allocation
CALL_VM(InterpreterRuntime::_new(THREAD, METHOD->constants(), index),
...
}
2. 进入运行解释器new创建对象InterpreterRuntime.cpp
// Allocation 和c++类似 分配内存和设置初内存中初始化的值
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index))
Klass* k_oop = pool->klass_at(index, CHECK);
instanceKlassHandle klass (THREAD, k_oop);
klass->check_valid_for_instantiation(true, CHECK);
// 加载 链接 解析 初始化 使用 销毁 确保初始化
klass->initialize(CHECK);
//分配内存
oop obj = klass->allocate_instance(CHECK);
thread->set_vm_result(obj);
IRT_END
3. 根据jvm手册初始化类的静态信息(调用static和static常量即jvm中clint)instaceKlass.cpp
void InstanceKlass::initialize(TRAPS) {
if (this->should_be_initialized()) {
HandleMark hm(THREAD);
instanceKlassHandle this_oop(THREAD, this);
//类是对象模板,初始化类信息
initialize_impl(this_oop, CHECK);
}
}
void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) {
// Make sure klass is linked (verified) before initializationn.
//链接阶段 -此处不详细解析(链接方法都在此处)
this_oop->link_class(CHECK);
// refer to the JVM book page 47 for description of steps
// Step 1 大多数为校验、验证之类 故省略
...
// Step 8
//调用clint(也即static块)------------
this_oop->call_class_initializer(THREAD);
}
4. 根据java类(jvm Klass)分配对象内存,类对象分配的模板.
instanceOop InstanceKlass::allocate_instance(TRAPS) {
int size = size_helper(); // 计算需要的内存
//调用CollectedHeap分配内存
instanceOop i = (instanceOop)CollectedHeap::obj_allocate(h_k, size, CHECK_NULL);
return i;
}
oop CollectedHeap::obj_allocate(KlassHandle klass, int size, TRAPS) {
//分配内存都要在走一步,命名可以看出来
HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
//分配后置处理,
post_allocation_setup_obj(klass, obj);
return (oop)obj;
}
5. 分配对象慢路径,如下根据jvm设置的垃圾回收新生代和老年代的配置相关
HeapWord* CollectedHeap::common_mem_allocate_noinit(KlassHandle klass, size_t size, TRAPS) {
//首先从从tlab分配,edns区线程独占无须上锁
HeapWord* result = NULL;
if (UseTLAB) {
result = allocate_from_tlab(klass, THREAD, size);
}
//从堆中分配空间,ci
result = Universe::heap()->mem_allocate(size,
}
6. 并行分配器ParallelScavengeHeap.cpp
HeapWord* ParallelScavengeHeap::mem_allocate(
size_t size,
bool* gc_overhead_limit_was_exceeded) {
HeapWord* result = young_gen()->allocate(size);
uint loop_count = 0;
uint gc_count = 0;
int gclocker_stalled_count = 0;
while (result == NULL) {
{
MutexLocker ml(Heap_lock);
gc_count = Universe::heap()->total_collections();
//年轻代分配
result = young_gen()->allocate(size);
if (result != NULL) {
return result;
}
// If certain conditions hold, try allocating from the old gen. 尝试老年代分配
result = mem_allocate_old_gen(size);
if (result != NULL) {
return result;
}
if (gclocker_stalled_count > GCLockerRetryAllocationCount) {
return NULL;
}
//触发gc,将执行gc请求放入gc管理线程中队列中
if (result == NULL) {
// Generate a VM operation
VM_ParallelGCFailedAllocation op(size, gc_count);
VMThread::execute(&op);
...
}
7. 在年代带eden区分配
// Allocation
HeapWord* allocate(size_t word_size) {
HeapWord* result = eden_space()->cas_allocate(word_size);
return result;
}
HeapWord* MutableSpace::cas_allocate(size_t size) {
do {
HeapWord* obj = top();
if (pointer_delta(end(), obj) >= size) {
//推动分配指针即可
HeapWord* new_top = obj + size;
HeapWord* result = (HeapWord*)Atomic::cmpxchg_ptr(new_top, top_addr(), obj);
...
} while (true);
}
8. 在old区分配(详细在gc的文章详细讲,此时在堆区分配到了内存)
HeapWord* ParallelScavengeHeap::mem_allocate_old_gen(size_t size) {
if (!should_alloc_in_eden(size) || GC_locker::is_active_and_needs_gc()) {
// Size is too big for eden, or gc is locked out.
return old_gen()->allocate(size);
}
return NULL;
}
HeapWord* PSOldGen::allocate(size_t word_size) {
assert_locked_or_safepoint(Heap_lock);
HeapWord* res = allocate_noexpand(word_size);
if (res == NULL) {
res = expand_and_allocate(word_size);
}
// Allocations in the old generation need to be reported
if (res != NULL) {
ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
//根据设置分配策略,在元数据区tenured分配
heap->size_policy()->tenured_allocation(word_size);
}