java代码
编写如下代码,new一个对象
public class test {
public static void main(String[] args) {
Person p = new Person("liming");
System.out.println(p);
}
}
class Person {
String name;
public Person(String name) {
super();
this.name = name;
}
}
查看classfile
通过命令查看classfile
javap -verbose test.class
如下:
Classfile /D:/test.class
Last modified 2018-12-7; size 467 bytes
MD5 checksum 84f0a803effe2eab9e624b6d817ce1ce
Compiled from "test.java"
public class test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #8.#17 // java/lang/Object."<init>":()V
#2 = Class #18 // Person
#3 = String #19 // liming
#4 = Methodref #2.#20 // Person."<init>":(Ljava/lang/String;)V
#5 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#6 = Methodref #23.#24 // java/io/PrintStream.println:(Ljava/lang/Object;)V
#7 = Class #25 // test
#8 = Class #26 // java/lang/Object
#9 = Utf8 <init>
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 SourceFile
#16 = Utf8 test.java
#17 = NameAndType #9:#10 // "<init>":()V
#18 = Utf8 Person
#19 = Utf8 liming
#20 = NameAndType #9:#27 // "<init>":(Ljava/lang/String;)V
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#23 = Class #31 // java/io/PrintStream
#24 = NameAndType #32:#33 // println:(Ljava/lang/Object;)V
#25 = Utf8 test
#26 = Utf8 java/lang/Object
#27 = Utf8 (Ljava/lang/String;)V
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
#31 = Utf8 java/io/PrintStream
#32 = Utf8 println
#33 = Utf8 (Ljava/lang/Object;)V
{
public test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: new #2 // class Person
3: dup
4: ldc #3 // String liming
6: invokespecial #4 // Method Person."<init>":(Ljava/lang/String;)V
9: astore_1
10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_1
14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
17: return
LineNumberTable:
line 3: 0
line 4: 10
line 5: 17
}
SourceFile: "test.java"
字节码分析
Constant pool中包含java类中调用的所有方法,包括构造,main,和println方法
main的方法栈中包含调用顺序
LineNumberTable标记栈中每个命令在java文件中的行数
- 0: new #2 // class Person
代表new一个Person对象 - 3: dup
代表复制栈顶的内容再压入栈,也就是把栈顶的内容做个备份 - 4: ldc #3 // String liming
把String类型的"liming"入栈 - 6: invokespecial #4 // Method Person."":(Ljava/lang/String;)V
调用Person类的构造方法 - 9: astore_1
构造方法内的赋值,将当前栈顶引用类型的值"liming"赋值给slot1处的局部变量p的name,然后出栈 - 10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
加载public final static PrintStream out = null; - 13: aload_1
将位置为1的对象引用局部变量压入栈 - 14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
调用println方法 - 17: return
结束方法
new的分析
在如下网页查看源码里的解释器bytecodeInterpreter.cpp
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/interpreter/bytecodeInterpreter.cpp#l1816
bytecodeInterpreter代码分析
CASE(_new): {
//获取字节流中的索引值
u2 index = Bytes::get_Java_u2(pc+1);
//获取常量池
ConstantPool* constants = istate->method()->constants();
//如果该类型已经被初始化了
if (!constants->tag_at(index).is_unresolved_klass()) {
// Make sure klass is initialized and doesn't have a finalizer
Klass* entry = constants->slot_at(index).get_klass();
//断言entry是一个类
assert(entry->is_klass(), "Should be resolved klass");
Klass* k_entry = (Klass*) entry;
//断言k_entry是该类型的一个实例
assert(k_entry->oop_is_instance(), "Should be InstanceKlass");
InstanceKlass* ik = (InstanceKlass*) k_entry;
//如果该实例所属类型已经被初始化并且能够快速分配内存
if ( ik->is_initialized() && ik->can_be_fastpath_allocated() ) {
//取对象所需内存尺寸
size_t obj_size = ik->size_helper();
oop result = NULL;
// If the TLAB isn't pre-zeroed then we'll have to do it
//是否需要置零是tlab的否,若tlab已经置零了则不需要再置零,反之则需要
bool need_zero = !ZeroTLAB;
//如果使用TLAB分配内存
if (UseTLAB) {
//试图从线程的tlab区分配内存区域
result = (oop) THREAD->tlab().allocate(obj_size);
}
// Disable non-TLAB-based fast-path, because profiling requires that all
// allocations go through InterpreterRuntime::_new() if THREAD->tlab().allocate
// returns NULL.
#ifndef CC_INTERP_PROFILE
//如果result仍为null,即尚未分配内存区域
if (result == NULL) {
//需要置零
need_zero = true;
// Try allocate in shared eden
//直接在堆内分配内存
retry:
//获取堆顶的位置
HeapWord* compare_to = *Universe::heap()->top_addr();
//获取堆分配对象所需内存区域后堆顶的位置
HeapWord* new_top = compare_to + obj_size;
//如果分配后的堆顶位置小于堆的最大内存地址(即剩余内存位置足够)
if (new_top <= *Universe::heap()->end_addr()) {
//采用原子操作试图分配内存,失败则重试,直到成功为止(使用CAS方式实现原子操作)
if (Atomic::cmpxchg_ptr(new_top, Universe::heap()->top_addr(), compare_to) != compare_to) {
goto retry;
}
//成功后把原堆顶位置赋值给对象的引用
result = (oop) compare_to;
}
}
#endif
if (result != NULL) {
// Initialize object (if nonzero size and need) and then the header
//如果分配成功且需要置零则置零处理
if (need_zero ) {
//跳过object head部分的内存,对象头不置零
HeapWord* to_zero = (HeapWord*) result + sizeof(oopDesc) / oopSize;
obj_size -= sizeof(oopDesc) / oopSize;
if (obj_size > 0 ) {
//内存置零
memset(to_zero, 0, obj_size * HeapWordSize);
}
}
//如果对象使用了偏向锁则在对象头部中写入偏向锁状态为1
if (UseBiasedLocking) {
result->set_mark(ik->prototype_header());
} else {
result->set_mark(markOopDesc::prototype());
}
//进行内存对齐
result->set_klass_gap(0);
//在对象头部中写入类型的引用
result->set_klass(k_entry);
// Must prevent reordering of stores for object initialization
// with stores that publish the new object.
//预防用于对象初始化的帧栈内存区域被重排序,先暴露该线程对内存区域的改动
OrderAccess::storestore();
//对象入栈
SET_STACK_OBJECT(result, 0);
UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
}
}
}
延伸
多线程时有时会发生内存访问重排序,为了产生错误,需要通过storestore()方法构造StoreStore内存屏障。
对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它线程可见。
也可以通过对可能被改变的变量添加volatile关键字解决该问题,volatile实质上相当于:
JMM基于保守策略的JMM内存屏障插入策略:
1.在每个volatile写操作的前面插入一个StoreStore屏障
2.在每个volatile写操作的后面插入一个SotreLoad屏障
3.在每个volatile读操作的后面插入一个LoadLoad屏障
4.在每个volatile读操作的后面插入一个LoadStore屏障