Java虚拟机规范中定义的对象操作相关的字节码指令如下表所示。

0xb2 getstatic 获取指定类的静态域,并将其值压入栈顶
0xb3 putstatic 为指定的类的静态域赋值
0xb4 getfield 获取指定类的实例域,并将其值压入栈顶
0xb5 putfield 为指定的类的实例域赋值
0xbb new 创建一个对象,并将其引用值压入栈顶
0xbc newarray 创建一个指定原始类型(如int,、float,、char等)的数组,并将其引用值压入栈顶
0xbd anewarray 创建一个引用型(如类、接口或数组)的数组,并将其引用值压入栈顶
0xbe arraylength 获得数组的长度值并压入栈顶
0xc0 checkcast 检验类型转换,检验未通过将抛出ClassCastException
0xc1 instanceof 检验对象是否是指定的类的实例,如果是将1压入栈顶,否则将0压入栈顶
0xc5 multianewarray 创建指定类型和指定维度的多维数组(执行该指令时,操作栈中必须包含各维度的长度值),并将其引用值压入栈顶

字节码指令的模板定义如下:

def(Bytecodes::_getstatic           , ubcp|____|clvm|____, vtos, vtos, getstatic           , f1_byte      );
def(Bytecodes::_putstatic , ubcp|____|clvm|____, vtos, vtos, putstatic , f2_byte );
def(Bytecodes::_getfield , ubcp|____|clvm|____, vtos, vtos, getfield , f1_byte );
def(Bytecodes::_putfield , ubcp|____|clvm|____, vtos, vtos, putfield , f2_byte ); def(Bytecodes::_new , ubcp|____|clvm|____, vtos, atos, _new , _ );
def(Bytecodes::_newarray , ubcp|____|clvm|____, itos, atos, newarray , _ );
def(Bytecodes::_anewarray , ubcp|____|clvm|____, itos, atos, anewarray , _ );
def(Bytecodes::_multianewarray , ubcp|____|clvm|____, vtos, atos, multianewarray , _ ); def(Bytecodes::_arraylength , ____|____|____|____, atos, itos, arraylength , _ ); def(Bytecodes::_checkcast , ubcp|____|clvm|____, atos, atos, checkcast , _ ); def(Bytecodes::_instanceof , ubcp|____|clvm|____, atos, itos, instanceof , _ );

new字节码指令的生成函数为TemplateTable::_new(),这在《深入剖析Java虚拟机:源码剖析与实例详解(基础卷)》的第9章类对象创建时详细介绍过,这里不再介绍。

getstatic字节码指令获取指定类的静态域,并将其值压入栈顶。格式如下:

getstatic indexbyte1 indexbyte2

无符号数indexbyte1和indexbyte2构建为(indexbyte1<<8)|indexbyte2,这个值指明了一个当前类的运行时常量池索引值,指向的运行时常量池项为一个字段的符号引用。

getstatic字节码指令的生成函数为TemplateTable::getstatic(),还有个类似的getfield指令,这些生成函数如下:

void TemplateTable::getfield(int byte_no) {
getfield_or_static(byte_no, false); // getfield的byte_no值为1
} void TemplateTable::getstatic(int byte_no) {
getfield_or_static(byte_no, true); // getstatic的byte_no的值为1
}

最终都会调用getfield_or_static()函数生成机器指令片段。此函数生成的机器指令片段对应的汇编代码如下:

// 获取ConstantPoolCache中ConstantPoolCacheEntry的index
0x00007fffe101fd10: movzwl 0x1(%r13),%edx
// 从栈中获取ConstantPoolCache的首地址
0x00007fffe101fd15: mov -0x28(%rbp),%rcx
// 左移2位,因为%edx中存储的是ConstantPoolCacheEntry index,
// 左移2位是因为ConstantPoolCacheEntry的内存占用是4个字
0x00007fffe101fd19: shl $0x2,%edx
// 计算%rcx+%rdx*8+0x10,获取ConstantPoolCacheEntry[_indices,_f1,_f2,_flags]中的_indices
// 因为ConstantPoolCache的大小为0x16字节,%rcx+0x10定位到第一个ConstantPoolCacheEntry的开始位置
// %rdx*8算出来的是相对于第一个ConstantPoolCacheEntry的字节偏移
0x00007fffe101fd1c: mov 0x10(%rcx,%rdx,8),%ebx
// _indices向右移动16位后获取[get bytecode,set bytecode,original constant pool index]中的get bytecode与set bytecode
0x00007fffe101fd20: shr $0x10,%ebx
// 获取set bytecode字段的值
0x00007fffe101fd23: and $0xff,%ebx
// 0xb2是getstatic指令的Opcode,比较值,如果相等就说明已经连接,跳转到resolved
0x00007fffe101fd29: cmp $0xb2,%ebx
0x00007fffe101fd2f: je 0x00007fffe101fdce // 将getstatic字节码的Opcode存储到%ebx中
0x00007fffe101fd35: mov $0xb2,%ebx // 省略通过调用MacroAssembler::call_VM()函数来执行InterpreterRuntime::resolve_get_put()函数的汇编代码
// ...

调用MacroAssembler::call_VM()函数生成如下代码,通过这些代码来执行InterpreterRuntime::resolve_get_put()函数。MacroAssembler::call_VM()函数的汇编在之前已经详细介绍过,这里不再介绍,直接给出汇编代码,如下:

0x00007fffe101fd3a: callq  0x00007fffe101fd44
0x00007fffe101fd3f: jmpq 0x00007fffe101fdc2 0x00007fffe101fd44: mov %rbx,%rsi
0x00007fffe101fd47: lea 0x8(%rsp),%rax
0x00007fffe101fd4c: mov %r13,-0x38(%rbp)
0x00007fffe101fd50: mov %r15,%rdi
0x00007fffe101fd53: mov %rbp,0x200(%r15)
0x00007fffe101fd5a: mov %rax,0x1f0(%r15)
0x00007fffe101fd61: test $0xf,%esp
0x00007fffe101fd67: je 0x00007fffe101fd7f
0x00007fffe101fd6d: sub $0x8,%rsp
0x00007fffe101fd71: callq 0x00007ffff66b567c
0x00007fffe101fd76: add $0x8,%rsp
0x00007fffe101fd7a: jmpq 0x00007fffe101fd84
0x00007fffe101fd7f: callq 0x00007ffff66b567c
0x00007fffe101fd84: movabs $0x0,%r10
0x00007fffe101fd8e: mov %r10,0x1f0(%r15)
0x00007fffe101fd95: movabs $0x0,%r10
0x00007fffe101fd9f: mov %r10,0x200(%r15)
0x00007fffe101fda6: cmpq $0x0,0x8(%r15)
0x00007fffe101fdae: je 0x00007fffe101fdb9
0x00007fffe101fdb4: jmpq 0x00007fffe1000420
0x00007fffe101fdb9: mov -0x38(%rbp),%r13
0x00007fffe101fdbd: mov -0x30(%rbp),%r14
0x00007fffe101fdc1: retq

如上代码完成的事情很简单,就是调用C++函数编写的InterpreterRuntime::resolve_get_put()函数,此函数会填充常量池缓存中ConstantPoolCacheEntry信息,关于ConstantPoolCache以及ConstantPoolCacheEntry,还有ConstantPoolCacheEntry中各个字段的含义在《深入剖析Java虚拟机:源码剖析与实例详解(基础卷)》中已经详细介绍过,这里不再介绍。

InterpreterRuntime::resolve_get_put()函数的实现比较多,我们首先看一部分实现,如下:

IRT_ENTRY(void, InterpreterRuntime::resolve_get_put(JavaThread* thread, Bytecodes::Code bytecode))
// resolve field
fieldDescriptor info;
constantPoolHandle pool(thread, method(thread)->constants());
bool is_put = (bytecode == Bytecodes::_putfield || bytecode == Bytecodes::_putstatic);
bool is_static = (bytecode == Bytecodes::_getstatic || bytecode == Bytecodes::_putstatic); {
JvmtiHideSingleStepping jhss(thread);
int x = get_index_u2_cpcache(thread, bytecode); // 根据线程栈中的bcp来获取常量池缓存索引
LinkResolver::resolve_field_access(info, pool, x ,bytecode, CHECK); // 向info中收集信息
} // check if link resolution caused cpCache to be updated
if (already_resolved(thread)){
return;
} ...
}

调用get_index_u2_cpcache()函数从当前方法对应的栈帧中获取bcp,然后通过bcp来获取字节码指令的操作数,也就是常量池索引,得到常量池索引后调用LinkResolver::resolve_field_access()函数可能会连接类和字段,然后将查询到的字段相关信息存储到fieldDescriptor中。resolve_field_access()函数的实现如下:

void LinkResolver::resolve_field_access(
fieldDescriptor& result,
constantPoolHandle pool,
int index, // 常量池索引
Bytecodes::Code byte,
TRAPS
) {
Symbol* field = pool->name_ref_at(index);
Symbol* sig = pool->signature_ref_at(index); // resolve specified klass 连接特定的类
KlassHandle resolved_klass;
resolve_klass(resolved_klass, pool, index, CHECK); KlassHandle current_klass(THREAD, pool->pool_holder());
resolve_field(result, resolved_klass, field, sig, current_klass, byte, true, true, CHECK);
} 

从pool中查找到的index处的索引项为CONSTANT_NameAndType_info,格式如下:

CONSTANT_NameAndType_info {
u1 tag;
u2 name_index; // 占用16位
u2 descriptor_index; // 占用16位
}

常量池中的一个CONSTANT_NameAndType_info数据项, 可以看做CONSTANT_NameAndType类型的一个实例 。 从这个数据项的名称可以看出, 它描述了两种信息,第一种信息是名称(Name), 第二种信息是类型(Type) 。这里的名称是指方法的名称或者字段的名称, 而Type是广义上的类型,它其实描述的是字段的描述符或方法的描述符。 也就是说, 如果Name部分是一个字段名称,那么Type部分就是相应字段的描述符; 如果Name部分描述的是一个方法的名称,那么Type部分就是对应的方法的描述符。 也就是说,一个CONSTANT_NameAndType_info就表示了一个方法或一个字段。

调用resolve_klass()连接类,调用resolve_field()连接字段。在resolve_field()函数中有如下实现:

InstanceKlass* tmp = InstanceKlass::cast(resolved_klass());
KlassHandle sel_klass(THREAD, tmp->find_field(field, sig, &fd));

最重要的就是调用InstanceKlass的find_field()函数查找字段,将查找到的相关信息存储到fieldDescriptor类型的fd中。关于字段在InstanceKlass中的存储以及具体的布局在《深入剖析Java虚拟机:源码剖析与实例详解(基础卷)》中已经详细介绍过,这里不再介绍。

fieldDescriptor类及重要属性的定义如下:

class fieldDescriptor VALUE_OBJ_CLASS_SPEC {
private:
AccessFlags _access_flags;
int _index; // the field index
constantPoolHandle _cp;
...
}

其中的_access_flags可用来表示字段是否有volatile、final等关键字修饰,_index表示字段是存储在InstanceKlass中相应数组的第几个元组中。_cp表示定义当前字段的类的常量池。

通过调用resolve_klass()和resolve_field()函数后就可拿到这些信息,然后返回到InterpreterRuntime::resolve_get_put()函数继续查看实现逻辑:

  TosState state  = as_TosState(info.field_type());

  Bytecodes::Code put_code = (Bytecodes::Code)0;

  InstanceKlass* klass = InstanceKlass::cast(info.field_holder());
bool uninitialized_static = ( (bytecode == Bytecodes::_getstatic || bytecode == Bytecodes::_putstatic) &&
!klass->is_initialized() );
Bytecodes::Code get_code = (Bytecodes::Code)0; if (!uninitialized_static) {
get_code = ((is_static) ? Bytecodes::_getstatic : Bytecodes::_getfield);
// 1、是putfield或putstatic指令
// 2、是getstatic或getfield指令并且不是获取final变量的值
if (is_put || !info.access_flags().is_final()) {
put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield);
}
} ConstantPoolCacheEntry* cpce = cache_entry(thread);
cpce->set_field(
get_code, // 设置的是_indices中的b1,当为getstatic或getfield时,则其中存储的是Opcode
put_code, // 设置的是_indices中的b2,当为setstatic或setfield时,则其中存储的是Opcode,所以get_code与put_code如果要连接了,其值不为0
info.field_holder(), // 设置的是_f1字段,表示字段的拥有者
info.index(), // field_index,设置的是flags
info.offset(), // field_offset,设置的是_f2字段,Offset (in words) of field from start of instanceOop / Klass*
state, // field_type,设置的是flags
info.access_flags().is_final(), // 设置的是flags
info.access_flags().is_volatile(), // 设置的是flags
pool->pool_holder()
);

通过info中的信息就可以得到字段的各种信息,然后填充ConstantPoolEntry信息,这样下次就不用对字段进行连接了,或者说不用从InstanceKlass中查找字段信息了,可直接从ConstantPoolCacheEntry中找到所有想得到的信息。 

上图在《深入剖析Java虚拟机:源码剖析与实例详解(基础卷)》一书中详细介绍过,通过我们解读getstatic字节码的解释执行过程,可以清楚的知道常量池缓存项的作用。对于getstatic来说,开始就会判断_indices中的高8位存储的是否为getstatic的操作码,如果不是,则表示没有连接,所以要调用InterpreterRuntime::resolve_get_put()函数进行连接操作。

在连接完成或已经连接完成时会继续执行如下汇编代码:

// 将ConstantPoolCacheEntry的索引存储么%edx
0x00007fffe101fdc2: movzwl 0x1(%r13),%edx
// 将ConstantPoolCache的首地址存储到%rcx
0x00007fffe101fdc7: mov -0x28(%rbp),%rcx
// 获取对应的ConstantPoolCacheEntry对应的索引
0x00007fffe101fdcb: shl $0x2,%edx // --resolved -- // 获取[_indices,_f1,_f2,_flags]中的_f2,由于ConstantPoolCache占用16字节,而_indices
// 和_f2各占用8字节,所以_f2的偏移为32字节,也就是0x32
// _f2中保存的是字段在java.lang.Class实例中的字节偏移,通过此偏移就可获取此字段存储在
// java.lang.Class实例的值
0x00007fffe101fdce: mov 0x20(%rcx,%rdx,8),%rbx
// 获取[_indices,_f1,_f2,_flags]中的_flags
0x00007fffe101fdd3: mov 0x28(%rcx,%rdx,8),%eax
// 获取[_indices,_f1,_f2,_flags]中的_f1,_f1保存了字段拥有者,
// 也就是java.lang.Class对象
0x00007fffe101fdd7: mov 0x18(%rcx,%rdx,8),%rcx // 从_f1中获取_java_mirror属性的值
0x00007fffe101fddc: mov 0x70(%rcx),%rcx
// 将_flags向右移动28位,剩下TosState
0x00007fffe101fde0: shr $0x1c,%eax
0x00007fffe101fde3: and $0xf,%eax
// 如果不相等,说明TosState的值不为0,则跳转到notByte
0x00007fffe101fde6: jne 0x00007fffe101fdf6 // btos
// btos的编号为0,代码执行到这里时,可能栈顶缓存要求是btos
// %rcx中存储的是_java_mirror,%rbx中存储的是_f2,由于静态变量存储在_java_mirror中,所以要获取
// 对应的首地址并压入栈中
0x00007fffe101fdec: movsbl (%rcx,%rbx,1),%eax
0x00007fffe101fdf0: push %rax
// 跳转到Done
0x00007fffe101fdf1: jmpq 0x00007fffe101ff0c
// -- notByte --
// %eax中存储的是TosState,如果不为atos,则跳转到notObj
0x00007fffe101fdf6: cmp $0x7,%eax
0x00007fffe101fdf9: jne 0x00007fffe101fe90 // atos
// %rcx中存储的是_java_mirror,%rbx中存储的是_f2,
// 所以要获取静态变量的首地址并压入栈内
0x00007fffe101fdff: mov (%rcx,%rbx,1),%eax
0x00007fffe101fe02: push %r10
0x00007fffe101fe04: cmp 0x163a8d45(%rip),%r12 # 0x00007ffff73c8b50
0x00007fffe101fe0b: je 0x00007fffe101fe88
0x00007fffe101fe11: mov %rsp,-0x28(%rsp)
0x00007fffe101fe16: sub $0x80,%rsp
0x00007fffe101fe1d: mov %rax,0x78(%rsp)
0x00007fffe101fe22: mov %rcx,0x70(%rsp)
0x00007fffe101fe27: mov %rdx,0x68(%rsp)
0x00007fffe101fe2c: mov %rbx,0x60(%rsp)
0x00007fffe101fe31: mov %rbp,0x50(%rsp)
0x00007fffe101fe36: mov %rsi,0x48(%rsp)
0x00007fffe101fe3b: mov %rdi,0x40(%rsp)
0x00007fffe101fe40: mov %r8,0x38(%rsp)
0x00007fffe101fe45: mov %r9,0x30(%rsp)
0x00007fffe101fe4a: mov %r10,0x28(%rsp)
0x00007fffe101fe4f: mov %r11,0x20(%rsp)
0x00007fffe101fe54: mov %r12,0x18(%rsp)
0x00007fffe101fe59: mov %r13,0x10(%rsp)
0x00007fffe101fe5e: mov %r14,0x8(%rsp)
0x00007fffe101fe63: mov %r15,(%rsp)
0x00007fffe101fe67: movabs $0x7ffff6d4d828,%rdi
0x00007fffe101fe71: movabs $0x7fffe101fe11,%rsi
0x00007fffe101fe7b: mov %rsp,%rdx
0x00007fffe101fe7e: and $0xfffffffffffffff0,%rsp
0x00007fffe101fe82: callq 0x00007ffff6872e3a
0x00007fffe101fe87: hlt
0x00007fffe101fe88: pop %r10
0x00007fffe101fe8a: push %rax
0x00007fffe101fe8b: jmpq 0x00007fffe101ff0c // -- notObj --
0x00007fffe101fe90: cmp $0x3,%eax
// 如果不为itos,则跳转到notInt
0x00007fffe101fe93: jne 0x00007fffe101fea2 // itos
0x00007fffe101fe99: mov (%rcx,%rbx,1),%eax
0x00007fffe101fe9c: push %rax
// 跳转到Done
0x00007fffe101fe9d: jmpq 0x00007fffe101ff0c
// -- notInt --
// 如果不为ctos,则跳转到notChar
0x00007fffe101fea2: cmp $0x1,%eax
0x00007fffe101fea5: jne 0x00007fffe101feb5 // ctos
0x00007fffe101feab: movzwl (%rcx,%rbx,1),%eax
0x00007fffe101feaf: push %rax
// 跳转到Done
0x00007fffe101feb0: jmpq 0x00007fffe101ff0c
// -- notChar --
// 如果不为stos,则跳转到notShort
0x00007fffe101feb5: cmp $0x2,%eax
0x00007fffe101feb8: jne 0x00007fffe101fec8 // stos
0x00007fffe101febe: movswl (%rcx,%rbx,1),%eax
0x00007fffe101fec2: push %rax
// 跳转到done
0x00007fffe101fec3: jmpq 0x00007fffe101ff0c
// -- notShort --
// 如果不为ltos,则跳转到notLong
0x00007fffe101fec8: cmp $0x4,%eax
0x00007fffe101fecb: jne 0x00007fffe101fee2 // ltos
0x00007fffe101fed1: mov (%rcx,%rbx,1),%rax
0x00007fffe101fed5: sub $0x10,%rsp
0x00007fffe101fed9: mov %rax,(%rsp)
// 跳转到Done
0x00007fffe101fedd: jmpq 0x00007fffe101ff0c
// -- notLong --
// 如果不为ftos,则跳转到notFloat
0x00007fffe101fee2: cmp $0x5,%eax
0x00007fffe101fee5: jne 0x00007fffe101fefe // ftos
0x00007fffe101feeb: vmovss (%rcx,%rbx,1),%xmm0
0x00007fffe101fef0: sub $0x8,%rsp
0x00007fffe101fef4: vmovss %xmm0,(%rsp)
// 跳转到Done
0x00007fffe101fef9: jmpq 0x00007fffe101ff0c
// -- notFloat --
0x00007fffe101fefe: vmovsd (%rcx,%rbx,1),%xmm0
0x00007fffe101ff03: sub $0x10,%rsp
0x00007fffe101ff07: vmovsd %xmm0,(%rsp)  
  
// -- Done --
  

如上汇编代码虽然多,但是完成的逻辑却非常简单,就是通过ConstantPoolCacheEntry中存储的信息(所谓的字节码连接完成指的就是对应的常量池缓存项的信息已经完善)完成压栈的逻辑。由于静态字段的值存储在java.lang.Class实例中,所以需要获取到对应的值,然后根据栈顶缓存要求的状态将值压入表达式栈即可。

推荐阅读:

第1篇-关于JVM运行时,开篇说的简单些

第2篇-JVM虚拟机这样来调用Java主类的main()方法

第3篇-CallStub新栈帧的创建

第4篇-JVM终于开始调用Java主类的main()方法啦

第5篇-调用Java方法后弹出栈帧及处理返回结果

第6篇-Java方法新栈帧的创建

第7篇-为Java方法创建栈帧

第8篇-dispatch_next()函数分派字节码

第9篇-字节码指令的定义

第10篇-初始化模板表

第11篇-认识Stub与StubQueue

第12篇-认识CodeletMark

第13篇-通过InterpreterCodelet存储机器指令片段

第14篇-生成重要的例程

第15章-解释器及解释器生成器

第16章-虚拟机中的汇编器

第17章-x86-64寄存器

第18章-x86指令集之常用指令

第19篇-加载与存储指令(1)

第20篇-加载与存储指令之ldc与_fast_aldc指令(2)

第21篇-虚拟机字节码之运算指令

第22篇-虚拟机字节码指令之类型转换

如果有问题可直接评论留言或加作者微信mazhimazh

关注公众号,有HotSpot VM源码剖析系列文章!

  

  

  

第23篇-虚拟机对象操作指令之getstatic的更多相关文章

  1. Linux连接虚拟机及操作指令

    Linux的安装(虚拟机环境)与基础配置   一.背景 本文介绍如何安装虚拟机VMware以及如果在虚拟机上安装Linux系统以及Linux安装完毕之后的基础配置 需要准备的东西有VMware以及Li ...

  2. 深入理解JVM(1)——栈和局部变量操作指令

    将常量压入栈的指令 aconst_null 将null对象引用压入栈iconst_m1 将int类型常量-1压入栈iconst_0 将int类型常量0压入栈iconst_1 将int类型常量1压入栈i ...

  3. 前端学PHP之面向对象系列第五篇——对象操作

    × 目录 [1]对象克隆 [2]对象比较[3]对象串行化[4]json 前面的话 本文主要介绍面向对象中的一些对象操作 对象克隆 对象复制,又叫对象克隆,可以通过 clone 关键字来完成 在多数情况 ...

  4. Javascript 笔记与总结(2-3)Javascript 运算符、控制结构与对象操作

    [连接运算符 + ] <script> console.log(1+2+'a'+3+4); </script> 输出: 3a34 [逻辑运算符]返回的是最早能判断表达式结果的那 ...

  5. JVM(四)-虚拟机对象

    概述: 上一篇文章,介绍了虚拟机类加载的过程,那么类加载好之后,虚拟机下一步该干什么呢.我们知道java是面向对象的编程语言,所以对象可以说是java'的灵魂,这篇文章我们就来介绍 虚拟机是如何创建对 ...

  6. HotSpot虚拟机对象相关内容

    一.对象的创建 1.类加载检查 普通对象的创建过程:虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载.解析和初始化 ...

  7. 深入理解Java虚拟机读书笔记1----Java内存区域与HotSpot虚拟机对象

    一 Java内存区域与HotSpot虚拟机对象 1 Java技术体系.JDK.JRE?     Java技术体系包括:         · Java程序设计语言:         · 各种硬件平台上的 ...

  8. jquery实现点击展开列表同时隐藏其他列表 js 对象操作 对象原型操作 把一个对象A赋值给另一个对象B 并且对象B 修改 不会影响 A对象

    这篇文章主要介绍了jquery实现点击展开列表同时隐藏其他列表的方法,涉及jquery鼠标事件及节点的遍历与属性操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下 本文实例讲述了jquery实现点击 ...

  9. 深入理解JVM:HotSpot虚拟机对象探秘

    对象的创建 java是一门面向对象的语言.在Java程序执行过程中无时无刻有Java对象被创建出来.在语言层面上,创建对象(克隆.反序列化)一般是一个newkeyword而已,而在虚拟机中,对象的创建 ...

  10. Java虚拟机系列(二)---HotSpot虚拟机对象

    都知道Java是一门面向对象的编程语言,在Java程序运行过程中,无时无刻不在创建对象,所以这节来总结一下HotSpot虚拟机中的Java对象. 一.Java虚拟机对象的创建过程. 在语义层面创建一个 ...

随机推荐

  1. RY哥查字典

    时间限制: 1 s 空间限制: 16000 KB 题目等级 : 钻石 Diamond 题目描述 Description RY哥最近新买了一本字典,他十分高兴,因为这上面的单词都十分的和谐,他天天查字典 ...

  2. 迅为顶级四核开发板 Exynos4412开发板,仅售560元

    业内公认最强四核 藐视一切挑战 ●四核CPU  ●四核GPU  ●双通道内存  ●HKMG技术  ●超级性能  ●至佳稳定  ●供货周期长 Exynos 4412处理器成功应用于三星GALAXY S3 ...

  3. linux下sublime配置c++11编译环境

    Tools->Build System->New Build System { "cmd": ["g++", "-std=c++11&qu ...

  4. file的name值

    在picturelibrary中取一张jpg文件, 其Name值为  "NoThumbnail.jpg",注意后面的.jpg             foreach (SPFile ...

  5. Android-采用Matrix对Bitmap加工

    1.Android正在使用Matrix放.旋转.平移.斜切等变换的. Matrix是一个3*3的矩阵,其值相应例如以下: 以下给出详细坐标相应变形的属性|scaleX, skewX, translat ...

  6. pull类型消息中间件-消息消费者(二)

    消费者的实例化 关于consumer的默认实现,metaq有两种: DefaultMQPullConsumer:由业务方主动拉取消息 DefaultMQPushConsumer:通过业务方注册回调方法 ...

  7. oc唯一标时一部设备

    ios7之前可以使用mac地址 ios7之后首先创建两个类 #import MyKeyChainManager.h @implementation MyKeyChainManager : NSObje ...

  8. MariaDB与MySQL在一台服务器同时运行

    [root@HE3 ~]#groupadd mariadb -g 513 [root@HE3 ~]#useradd-u 513 -gmariadb -s /sbin/nologin -d /home/ ...

  9. JS 中 原生方法 (二) --- 数组 (修---添加ES6新增)

    const arr = [1, 2, 3, 5, 'a', 'b'] /** * * length * 这个只能被 称之为 数组的原生属性, 返回 一个 number * arr.length */ ...

  10. day 06

    深浅拷贝 # 值拷贝:应用场景最多ls = [1, 'abc', [10]]​ls1 = ls  # ls1直接将ls中存放的地址拿过来# ls内部的值发生任何变化,ls1都会随之变化​ls2 = l ...