JVMJVM · Java虚拟机原理 · JVM上语言·框架· 生态系统

JVM内存模型(2)、对象的创建以及访问定位

2017-09-06  本文已影响56人  编程界的小学生

一、对象的创建
1、草图

Paste_Image.png

2、草图的解释
(2.1)、当JVM进程启动的时候,遇到了new指令时,会首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过,若没有,则进行那几步骤。(更详细的我会在JVM类加载器篇幅进行详解)

(2.2)、在方法去找到后会去堆内存为对象分配空间,所需空间大小在类加载完后即可确定。

(2.3)、为new出实例对象的引用分配空间(在栈上的str),并且与堆内存建立实际的引用关系。

3、代码实战解释(很到位,很全面)

/**
 * 从JVM调用的角度分析Java程序对内存空间的使用
 *
 * 当JVM进程启动的时候,会从类加载路径中找到包含main方法的入口类HelloJVM
 * 找到HelloJVM后会直接读取该文件中的二进制数据并且把该类的信息放到方法区
 * 然后会定位到HelloJVM中的main方法的字节码并开始执行main方法中的指令
 *
 * Student student = new Student("java");
 * 此时会创建Student实例对象并且使用student来引用该对象(或者说对该对象命名),其内幕如下:
 * 第一步:JVM会直接到方法区中查找Student类的信息,此时发现没有Student类,就通过类加载器加载该Student类文件
 * 第二步:在JVM的方法区加载并找到了Student类之后会在Heap区中为Student实例对象分配内存
 *        并且在Student的实例对象中持有指向方法区中的Student类的引用(内存地址);
 * 第三步:JVM实例完后会在当前线程中为Stack中的reference建立实际的引用关系,此时会赋值给student
 *
 * 在JVM中方法的调用一定是属于线程的行为,也就是说方法调用本身会发生在调用线程的方法调用栈:
 *  线程的方法调用栈(Method Stack Frames),每一个方法的调用就是方法调用栈中的一个Frame,
 *  该Frame包含了方法的参数、局部变量、临时数据等。
 *              student.sayHello();
 *
 * @author TongWei.Chen 2017-09-01 16:51:41
 */
public class HelloJVM {

    /**
     * 在JVM运行的时候,会通过反射的方式到Method区域找到入口类的入口方法main
     * @param args
     */
    //main方法也是放在方法区域的
    public static void main(String[] args) {
        /**
         * student是存放在主线程的Stack中的;
         * Student对象实例是放在所有线程共享的Heap区域中的
         */
        Student student = new Student("java");

        /**
         * 指针和句柄的粗略区别:
         * 指针:直接指向对象
         * 句柄:先指向句柄,句柄在指向对象
         *
         * 首先会通过student指针(非句柄)找到Student对象,
         * 当找到该对象后会通过对象内部指向方法区中的指针来调用具体的方法去执行任务
         */
        student.sayHello();

    }
}

class Student {
    //name本身作为成员是放在Stack中的,但是name指向的String对象是在Heap中的
    private String name;

    public Student(String name) {
        this.name = name;
    }

    //sayHello这个方法是放在方法区域中的
    public void sayHello() {
        System.out.println("Hello, this is " + this.name);
    }
}

4、代码实战的图示

Paste_Image.png

二、对象的访问定位
什么意思?
就是说栈中的引用是怎么找到堆内存的,堆内存中的实例对象又是怎么找到方法区中的类型数据的。

1、句柄方式
Java堆内存会划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址。而句柄中包含了对象实例数据与类型数据各自的具体地址信息

Paste_Image.png

2、直接指针
如果使用直接指针访问,那么reference中直接存储的就是对象地址。

Paste_Image.png

不难发现,上面【4、代码实战的图示】采取的就是直接指针方式。

3、两种方式的对比
(1)句柄方式:
最大的好处就是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时,只会改变句柄中的实例数据指针,而reference本身不需修改。

(2)直接指针:
速度更快,节省了一次指针定位的时间开销,由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。

4、采取哪种方式呢?
具体采取哪种方式是由虚拟机决定的,虚拟机不同方式不同。但是目前主流的虚拟机就是这两种方式。目前主流的Sun HptSpot(目前的Java虚拟几乎都是这个)采取的是直接指针。

若有兴趣,欢迎来加入群,【Java初学者学习交流群】:458430385,此群有Java开发人员、UI设计人员和前端工程师。有问必答,共同探讨学习,一起进步!
欢迎关注我的微信公众号【Java码农社区】,会定时推送各种干货:


qrcode_for_gh_577b64e73701_258.jpg
上一篇 下一篇

猜你喜欢

热点阅读