类运行过程的内存分析

时间:2019-08-06
本文章向大家介绍类运行过程的内存分析,主要包括类运行过程的内存分析使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Java 虚拟机主要可分为stack、heap、method area三个区域,其中栈主要用来存放基本类型变量,对象引用;堆主要存放类实例;方法区存放静态变量、静态方法、常量池。

1 栈的特点

(1) 栈用来描述方法执行的内存模型。每个方法被调用都会创建一个栈帧(存局部变量、操作数、方法出口);

(2) JVM 为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等);

(3) 栈属于线程私有,不能共享;

(4) 栈的存储特性,FIFO;

(5) 栈是由系统自动分配,速度快;栈是一个连续内存空间;

2 堆的特点

(1) 堆用于存储创建好的对象和数组;

(2) JVM 只有一个堆,被所有线程共享;

(3) 堆不是一个连续的内存空间,分配灵活,速度慢;

3 方法区

(1) JVM 只有一个方法区,被所有线程共享;

(2) 方法区实际上也是堆,用于存储类、常量相关信息;

(3) 堆用来存放程序中永远不变的内容,如类信息、静态变量、静态方法、字符串常量;

4 类运行过程中内存分析

(1) 类实例

package cn.latiny.normal;

/**
 * @author Latiny
 * @version 1.0
 * @description: Human
 * @date 2019/8/2 11:25
 */
public class HumanDemo {

    public static void main(String[] args) throws Exception {
        Street street = new Street();
        street.setName("中华路");
        street.setNumber(10000);
        Location location = new Location("广东", "佛山", street);
        Human human = new Human();
        human.setName("Latiny");
        human.setAge(99);
        human.setLocation(location);
        System.out.println(human);
    }

}

class Humana {

    private String name;
    private int age;
    private Location location;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Location getLocation() {
        return location;
    }

    public void setLocation(Location location) {
        this.location = location;
    }

    @Override
    public String toString() {
        return "Human{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", location=" + location +
                '}';
    }
}

class Locationa {

    private String province;
    private String city;
    private Street street;

    public Locationa(String province, String city, Street street){
        this.province = province;
        this.city = city;
        this.street = street;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public Street getStreet() {
        return street;
    }

    public void setStreet(Street street) {
        this.street = street;
    }

    @Override
    public String toString() {
        return "Location{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                ", street=" + street +
                '}';
    }
}

class Streeta  {

    String name;
    int number;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "Street{" +
                "name='" + name + '\'' +
                ", number=" + number +
                '}';
    }
}
View Code

(2) 类运行过程分析

1)执行HumanDemo 类的main方法时,系统收到我们发出的指令,启动一个Java虚拟机进程;

2)进程首先从classpath 中找到HumanDemo.class文件(如果没有会进行编译后再去查找),之后读取这个文件中的二进制数据;把HumanDemo 类的类信息存放到运行时数据区的方法区中,同时把静态变量和静态方法、常量("中华路", "广东", "佛山","Latiny")加载到方法区,这一过程称为类的加载过程;

3)Java虚拟机定位到方法区中HumanDemo 类的Main()方法的字节码,开始执行它的指令;

4)在栈中开辟一个main() 方法栈帧,执行第一句代码:

Street street = new Street();

Java 虚拟机创建一个Street 实例,使引用变量street 引用这个实例,看似简单的一句代码,但是JVM底层却做了很多工作,让我们来跟踪一下Java虚拟机,看看它究竟是怎么来执行这个任务的:

① Java 虚拟机根据指令需要创建一个Street 实例,于是就直奔方法区而去,需要先找到Street 类的类型信息,结果一看这会儿的方法区里还没有Street类信息,怎么办?Java虚拟机先加载Street 类,把Street 类的类型信息存放在方法区里。

② Street类找到,下面就开始干活啦。Java虚拟机首先在堆区中创建一个新的Street 实例,这个Street 实例持有着指向方法区的Street 类的类型信息的引用。这里所说的引用,实际上指的是Street 类的类型信息在方法区中的内存地址,有点类似于C语言里的指针,而这个地址呢,就存放了在Street 实例的数据区里。

③ 在Java虚拟机进程中,每个线程都会拥有一个方法调用栈,用来跟踪线程运行中一系列的方法调用过程,栈中的每一个元素就被称为栈帧,每当线程调用一个方法的时候就会向方法栈压入一个新帧。这里的帧用来存储方法的参数、局部变量和运算过程中的临时数据。位于 "=" 前的street 是一个在main()方法中定义的变量,可见,它是一个局部变量,因此它被会添加到了执行main()方法的主线程的Java方法调用栈中。而 "=" 将把这个street变量指向堆区中的Street 实例,也就是说,它持有指向Street实例的引用。

 整个执行过程如下:

5) 这两句代码运行的过程如下

street.setName("中华路");
street.setNumber(10000);

name 指向方法区常量池的"中华路"

number赋值为10000

 6)这句代码JVM做了非常多的工作,具体如下:

Location location = new Location("广东", "佛山", street);

① 加载Location类到方法区;

② 在堆内存中为Location 创建一个新对象,这个对象有指向方法区Location类的引用;

③ main方法栈帧中添加location 局部变量,并指向步骤二创建的对象(保存堆中的实例地址);

④ 给location 对象成员变量赋值,province指向常量池的"广东",city 指向 "深圳",street 指向前面创建的Street对象;

 7)这段代码JVM执行过程与之前类似不再详细描述,具体如下图:

Human human = new Human();
human.setName("Latiny");
human.setAge(99);
human.setLocation(location);

原文地址:https://www.cnblogs.com/Latiny/p/11307550.html